diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/blockmodel/VertexPartition.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/blockmodel/VertexPartition.java index eba1efed..7428b254 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/blockmodel/VertexPartition.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/blockmodel/VertexPartition.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, The JUNG Authors + * Copyright (c) 2003, The JUNG Authors * * All rights reserved. * @@ -12,6 +12,7 @@ */ package edu.uci.ics.jung.algorithms.blockmodel; +import com.google.common.graph.Graph; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -19,117 +20,102 @@ import java.util.Map; import java.util.Set; -import com.google.common.graph.Graph; - /** - * Maintains information about a vertex partition of a graph. - * This can be built from a map from vertices to vertex sets - * or from a collection of (disjoint) vertex sets, - * such as those created by various clustering methods. + * Maintains information about a vertex partition of a graph. This can be built from a map from + * vertices to vertex sets or from a collection of (disjoint) vertex sets, such as those created by + * various clustering methods. */ -public class VertexPartition -{ - private Map> vertex_partition_map; - private Collection> vertex_sets; - private Graph graph; - - /** - * Creates an instance based on the specified graph and mapping from vertices - * to vertex sets, and generates a set of partitions based on this mapping. - * @param g the graph over which the vertex partition is defined - * @param partition_map the mapping from vertices to vertex sets (partitions) - */ - public VertexPartition(Graph g, Map> partition_map) - { - this.vertex_partition_map = Collections.unmodifiableMap(partition_map); - this.graph = g; - } +public class VertexPartition { + private Map> vertex_partition_map; + private Collection> vertex_sets; + private Graph graph; + + /** + * Creates an instance based on the specified graph and mapping from vertices to vertex sets, and + * generates a set of partitions based on this mapping. + * + * @param g the graph over which the vertex partition is defined + * @param partition_map the mapping from vertices to vertex sets (partitions) + */ + public VertexPartition(Graph g, Map> partition_map) { + this.vertex_partition_map = Collections.unmodifiableMap(partition_map); + this.graph = g; + } + + /** + * Creates an instance based on the specified graph, vertex-set mapping, and set of disjoint + * vertex sets. The vertex-set mapping and vertex partitions must be consistent; that is, the + * mapping must reflect the division of vertices into partitions, and each vertex must appear in + * exactly one partition. + * + * @param g the graph over which the vertex partition is defined + * @param partition_map the mapping from vertices to vertex sets (partitions) + * @param vertex_sets the set of disjoint vertex sets + */ + public VertexPartition(Graph g, Map> partition_map, Collection> vertex_sets) { + this.vertex_partition_map = Collections.unmodifiableMap(partition_map); + this.vertex_sets = vertex_sets; + this.graph = g; + } + + /** + * Creates an instance based on the specified graph and set of disjoint vertex sets, and generates + * a vertex-to-partition map based on these sets. + * + * @param g the graph over which the vertex partition is defined + * @param vertex_sets the set of disjoint vertex sets + */ + public VertexPartition(Graph g, Collection> vertex_sets) { + this.vertex_sets = vertex_sets; + this.graph = g; + } + + /** + * Returns the graph on which the partition is defined. + * + * @return the graph on which the partition is defined + */ + public Graph getGraph() { + return graph; + } - /** - * Creates an instance based on the specified graph, vertex-set mapping, - * and set of disjoint vertex sets. The vertex-set mapping and vertex - * partitions must be consistent; that is, the mapping must reflect the - * division of vertices into partitions, and each vertex must appear in - * exactly one partition. - * @param g the graph over which the vertex partition is defined - * @param partition_map the mapping from vertices to vertex sets (partitions) - * @param vertex_sets the set of disjoint vertex sets - */ - public VertexPartition(Graph g, Map> partition_map, - Collection> vertex_sets) - { - this.vertex_partition_map = Collections.unmodifiableMap(partition_map); - this.vertex_sets = vertex_sets; - this.graph = g; + /** + * Returns a map from each vertex in the input graph to its partition. This map is generated if it + * does not already exist. + * + * @return a map from each vertex in the input graph to a vertex set + */ + public Map> getVertexToPartitionMap() { + if (vertex_partition_map == null) { + this.vertex_partition_map = new HashMap>(); + for (Set set : this.vertex_sets) for (V v : set) this.vertex_partition_map.put(v, set); } + return vertex_partition_map; + } - /** - * Creates an instance based on the specified graph and set of disjoint vertex sets, - * and generates a vertex-to-partition map based on these sets. - * @param g the graph over which the vertex partition is defined - * @param vertex_sets the set of disjoint vertex sets - */ - public VertexPartition(Graph g, Collection> vertex_sets) - { - this.vertex_sets = vertex_sets; - this.graph = g; + /** + * Returns a collection of vertex sets, where each vertex in the input graph is in exactly one + * set. This collection is generated based on the vertex-to-partition map if it does not already + * exist. + * + * @return a collection of vertex sets such that each vertex in the instance's graph is in exactly + * one set + */ + public Collection> getVertexPartitions() { + if (vertex_sets == null) { + this.vertex_sets = new HashSet>(); + this.vertex_sets.addAll(vertex_partition_map.values()); } - - /** - * Returns the graph on which the partition is defined. - * @return the graph on which the partition is defined - */ - public Graph getGraph() - { - return graph; - } + return vertex_sets; + } - /** - * Returns a map from each vertex in the input graph to its partition. - * This map is generated if it does not already exist. - * @return a map from each vertex in the input graph to a vertex set - */ - public Map> getVertexToPartitionMap() - { - if (vertex_partition_map == null) - { - this.vertex_partition_map = new HashMap>(); - for (Set set : this.vertex_sets) - for (V v : set) - this.vertex_partition_map.put(v, set); - } - return vertex_partition_map; - } - - /** - * Returns a collection of vertex sets, where each vertex in the - * input graph is in exactly one set. - * This collection is generated based on the vertex-to-partition map - * if it does not already exist. - * @return a collection of vertex sets such that each vertex in the - * instance's graph is in exactly one set - */ - public Collection> getVertexPartitions() - { - if (vertex_sets == null) - { - this.vertex_sets = new HashSet>(); - this.vertex_sets.addAll(vertex_partition_map.values()); - } - return vertex_sets; - } + /** @return the number of partitions. */ + public int numPartitions() { + return vertex_sets.size(); + } - /** - * @return the number of partitions. - */ - public int numPartitions() - { - return vertex_sets.size(); - } - - @Override - public String toString() - { - return "Partitions: " + vertex_partition_map; - } + @Override + public String toString() { + return "Partitions: " + vertex_partition_map; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/BicomponentClusterer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/BicomponentClusterer.java index 221c13be..419e8b88 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/BicomponentClusterer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/BicomponentClusterer.java @@ -1,14 +1,17 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.cluster; +import com.google.common.base.Function; +import com.google.common.graph.EndpointPair; +import com.google.common.graph.Graph; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; @@ -16,152 +19,126 @@ import java.util.Set; import java.util.Stack; -import com.google.common.base.Function; -import com.google.common.graph.EndpointPair; -import com.google.common.graph.Graph; - - /** - * Finds all biconnected components (bicomponents) of an graph, ignoring edge direction. - * A graph is a biconnected component if - * at least 2 vertices must be removed in order to disconnect the graph. (Graphs - * consisting of one vertex, or of two connected vertices, are also biconnected.) Biconnected - * components of three or more vertices have the property that every pair of vertices in the component - * are connected by two or more vertex-disjoint paths. - *

- * Running time: O(|V| + |E|) where |V| is the number of vertices and |E| is the number of edges + * Finds all biconnected components (bicomponents) of an graph, ignoring edge direction. A + * graph is a biconnected component if at least 2 vertices must be removed in order to disconnect + * the graph. (Graphs consisting of one vertex, or of two connected vertices, are also biconnected.) + * Biconnected components of three or more vertices have the property that every pair of vertices in + * the component are connected by two or more vertex-disjoint paths. + * + *

Running time: O(|V| + |E|) where |V| is the number of vertices and |E| is the number of edges + * * @see "Depth first search and linear graph algorithms by R. E. Tarjan (1972), SIAM J. Comp." - * * @author Joshua O'Madadhain */ -public class BicomponentClusterer implements Function, Set>> -{ - protected Map dfs_num; - protected Map high; - protected Map parents; - protected Stack> stack; - protected int converse_depth; +public class BicomponentClusterer implements Function, Set>> { + protected Map dfs_num; + protected Map high; + protected Map parents; + protected Stack> stack; + protected int converse_depth; - /** - * Constructs a new bicomponent finder - */ - public BicomponentClusterer() { - } + /** Constructs a new bicomponent finder */ + public BicomponentClusterer() {} - /** - * Extracts the bicomponents from the graph. - * @param graph the graph whose bicomponents are to be extracted - * @return the ClusterSet of bicomponents - */ - public Set> apply(Graph graph) - { - Set> bicomponents = new LinkedHashSet>(); + /** + * Extracts the bicomponents from the graph. + * + * @param graph the graph whose bicomponents are to be extracted + * @return the ClusterSet of bicomponents + */ + public Set> apply(Graph graph) { + Set> bicomponents = new LinkedHashSet>(); - if (graph.nodes().isEmpty()) - return bicomponents; + if (graph.nodes().isEmpty()) return bicomponents; - // initialize DFS number for each vertex to 0 - dfs_num = new HashMap(); - for (V v : graph.nodes()) - { - dfs_num.put(v, 0); - } + // initialize DFS number for each vertex to 0 + dfs_num = new HashMap(); + for (V v : graph.nodes()) { + dfs_num.put(v, 0); + } + + for (V v : graph.nodes()) { + if (dfs_num.get(v).intValue() == 0) // if we haven't hit this vertex yet... + { + high = new HashMap(); + stack = new Stack>(); + parents = new HashMap(); + converse_depth = graph.nodes().size(); + // find the biconnected components for this subgraph, starting from v + findBiconnectedComponents(graph, v, bicomponents); - for (V v : graph.nodes()) - { - if (dfs_num.get(v).intValue() == 0) // if we haven't hit this vertex yet... - { - high = new HashMap(); - stack = new Stack>(); - parents = new HashMap(); - converse_depth = graph.nodes().size(); - // find the biconnected components for this subgraph, starting from v - findBiconnectedComponents(graph, v, bicomponents); - - // if we only visited one vertex, this method won't have - // ID'd it as a biconnected component, so mark it as one - if (graph.nodes().size() - converse_depth == 1) - { - Set s = new HashSet(); - s.add(v); - bicomponents.add(s); - } - } + // if we only visited one vertex, this method won't have + // ID'd it as a biconnected component, so mark it as one + if (graph.nodes().size() - converse_depth == 1) { + Set s = new HashSet(); + s.add(v); + bicomponents.add(s); } - - return bicomponents; + } } - /** - *

Stores, in bicomponents, all the biconnected - * components that are reachable from v. - * - *

The algorithm basically proceeds as follows: do a depth-first - * traversal starting from v, marking each vertex with - * a value that indicates the order in which it was encountered (dfs_num), - * and with - * a value that indicates the highest point in the DFS tree that is known - * to be reachable from this vertex using non-DFS edges (high). (Since it - * is measured on non-DFS edges, "high" tells you how far back in the DFS - * tree you can reach by two distinct paths, hence biconnectivity.) - * Each time a new vertex w is encountered, push the edge just traversed - * on a stack, and call this method recursively. If w.high is no greater than - * v.dfs_num, then the contents of the stack down to (v,w) is a - * biconnected component (and v is an articulation point, that is, a - * component boundary). In either case, set v.high to max(v.high, w.high), - * and continue. If w has already been encountered but is - * not v's parent, set v.high max(v.high, w.dfs_num) and continue. - * - *

(In case anyone cares, the version of this algorithm on p. 224 of - * Udi Manber's "Introduction to Algorithms: A Creative Approach" seems to be - * wrong: the stack should be initialized outside this method, - * (v,w) should only be put on the stack if w hasn't been seen already, - * and there's no real benefit to putting v on the stack separately: just - * check for (v,w) on the stack rather than v. Had I known this, I could - * have saved myself a few days. JRTOM) - * - * @param g the graph to check for biconnected components - * @param v the starting place for searching for biconnected components - * @param bicomponents storage for the biconnected components found by this algorithm - */ - protected void findBiconnectedComponents(Graph g, V v, Set> bicomponents) - { - int v_dfs_num = converse_depth; - dfs_num.put(v, v_dfs_num); - converse_depth--; - high.put(v, v_dfs_num); + return bicomponents; + } + + /** + * Stores, in bicomponents, all the biconnected components that are reachable from + * v. + * + *

The algorithm basically proceeds as follows: do a depth-first traversal starting from + * v, marking each vertex with a value that indicates the order in which it was encountered + * (dfs_num), and with a value that indicates the highest point in the DFS tree that is known to + * be reachable from this vertex using non-DFS edges (high). (Since it is measured on non-DFS + * edges, "high" tells you how far back in the DFS tree you can reach by two distinct paths, hence + * biconnectivity.) Each time a new vertex w is encountered, push the edge just traversed on a + * stack, and call this method recursively. If w.high is no greater than v.dfs_num, then the + * contents of the stack down to (v,w) is a biconnected component (and v is an articulation point, + * that is, a component boundary). In either case, set v.high to max(v.high, w.high), and + * continue. If w has already been encountered but is not v's parent, set v.high max(v.high, + * w.dfs_num) and continue. + * + *

(In case anyone cares, the version of this algorithm on p. 224 of Udi Manber's "Introduction + * to Algorithms: A Creative Approach" seems to be wrong: the stack should be initialized outside + * this method, (v,w) should only be put on the stack if w hasn't been seen already, and there's + * no real benefit to putting v on the stack separately: just check for (v,w) on the stack rather + * than v. Had I known this, I could have saved myself a few days. JRTOM) + * + * @param g the graph to check for biconnected components + * @param v the starting place for searching for biconnected components + * @param bicomponents storage for the biconnected components found by this algorithm + */ + protected void findBiconnectedComponents(Graph g, V v, Set> bicomponents) { + int v_dfs_num = converse_depth; + dfs_num.put(v, v_dfs_num); + converse_depth--; + high.put(v, v_dfs_num); - for (V w : g.adjacentNodes(v)) - { - int w_dfs_num = dfs_num.get(w).intValue(); - EndpointPair vw = EndpointPair.unordered(v, w); - if (w_dfs_num == 0) // w hasn't yet been visited - { - parents.put(w, v); // v is w's parent in the DFS tree - stack.push(vw); - findBiconnectedComponents(g, w, bicomponents); - int w_high = high.get(w).intValue(); - if (w_high <= v_dfs_num) - { - // v disconnects w from the rest of the graph, - // i.e., v is an articulation point - // thus, everything between the top of the stack and - // v is part of a single biconnected component - Set bicomponent = new HashSet(); - EndpointPair endpoints; - do - { - endpoints = stack.pop(); - bicomponent.add(endpoints.nodeU()); - bicomponent.add(endpoints.nodeV()); - } - while (!endpoints.equals(vw)); - bicomponents.add(bicomponent); - } - high.put(v, Math.max(w_high, high.get(v).intValue())); - } - else if (w != parents.get(v)) // (v,w) is a back or a forward edge - high.put(v, Math.max(w_dfs_num, high.get(v).intValue())); + for (V w : g.adjacentNodes(v)) { + int w_dfs_num = dfs_num.get(w).intValue(); + EndpointPair vw = EndpointPair.unordered(v, w); + if (w_dfs_num == 0) // w hasn't yet been visited + { + parents.put(w, v); // v is w's parent in the DFS tree + stack.push(vw); + findBiconnectedComponents(g, w, bicomponents); + int w_high = high.get(w).intValue(); + if (w_high <= v_dfs_num) { + // v disconnects w from the rest of the graph, + // i.e., v is an articulation point + // thus, everything between the top of the stack and + // v is part of a single biconnected component + Set bicomponent = new HashSet(); + EndpointPair endpoints; + do { + endpoints = stack.pop(); + bicomponent.add(endpoints.nodeU()); + bicomponent.add(endpoints.nodeV()); + } while (!endpoints.equals(vw)); + bicomponents.add(bicomponent); } + high.put(v, Math.max(w_high, high.get(v).intValue())); + } else if (w != parents.get(v)) // (v,w) is a back or a forward edge + high.put(v, Math.max(w_dfs_num, high.get(v).intValue())); } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/EdgeBetweennessClusterer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/EdgeBetweennessClusterer.java index a2e3ff41..beac1708 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/EdgeBetweennessClusterer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/EdgeBetweennessClusterer.java @@ -1,105 +1,104 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.cluster; -import java.util.LinkedHashSet; -import java.util.Set; - import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.graph.Graphs; import com.google.common.graph.MutableNetwork; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.scoring.BetweennessCentrality; - +import java.util.LinkedHashSet; +import java.util.Set; /** * An algorithm for computing clusters (community structure) in graphs based on edge betweenness. - * The betweenness of an edge is defined as the extent to which that edge lies along - * shortest paths between all pairs of nodes. + * The betweenness of an edge is defined as the extent to which that edge lies along shortest paths + * between all pairs of nodes. + * + *

This algorithm works by iteratively following the 2 step process: * - * This algorithm works by iteratively following the 2 step process: *

    - *
  • Compute edge betweenness for all edges in current graph - *
  • Remove edge with highest betweenness + *
  • Compute edge betweenness for all edges in current graph + *
  • Remove edge with highest betweenness *
- *

- * Running time is: O(kmn) where k is the number of edges to remove, m is the total number of edges, and - * n is the total number of vertices. For very sparse graphs the running time is closer to O(kn^2) and for - * graphs with strong community structure, the complexity is even lower. - *

- * This algorithm is a slight modification of the algorithm discussed below in that the number of edges - * to be removed is parameterized. + * + *

Running time is: O(kmn) where k is the number of edges to remove, m is the total number of + * edges, and n is the total number of vertices. For very sparse graphs the running time is closer + * to O(kn^2) and for graphs with strong community structure, the complexity is even lower. + * + *

This algorithm is a slight modification of the algorithm discussed below in that the number of + * edges to be removed is parameterized. + * * @author Scott White * @author Tom Nelson (converted to jung2) * @author Joshua O'Madadhain (converted to common.graph) * @see "Community structure in social and biological networks by Michelle Girvan and Mark Newman" */ -public class EdgeBetweennessClusterer implements Function,Set>> { - private final int mNumEdgesToRemove; - private LinkedHashSet edgesRemoved; +public class EdgeBetweennessClusterer implements Function, Set>> { + private final int mNumEdgesToRemove; + private LinkedHashSet edgesRemoved; - /** - * Constructs a new clusterer for the specified graph. - * @param numEdgesToRemove the number of edges to be progressively removed from the graph - */ - public EdgeBetweennessClusterer(int numEdgesToRemove) { - Preconditions.checkArgument(numEdgesToRemove >= 0, - "Number of edges to remove must be positive"); - mNumEdgesToRemove = numEdgesToRemove; - edgesRemoved = new LinkedHashSet<>(mNumEdgesToRemove); - } + /** + * Constructs a new clusterer for the specified graph. + * + * @param numEdgesToRemove the number of edges to be progressively removed from the graph + */ + public EdgeBetweennessClusterer(int numEdgesToRemove) { + Preconditions.checkArgument( + numEdgesToRemove >= 0, "Number of edges to remove must be positive"); + mNumEdgesToRemove = numEdgesToRemove; + edgesRemoved = new LinkedHashSet<>(mNumEdgesToRemove); + } - /** - * Finds the set of clusters which have the strongest "community structure". - * The more edges removed the smaller and more cohesive the clusters. - * - * @param graph the graph - */ - public Set> apply(Network graph) { - Preconditions.checkArgument(mNumEdgesToRemove <= graph.edges().size(), - "Number of edges to remove must be <= the number of edges in the graph"); - // TODO(jrtom): is there something smarter that we can do if we're removing - // (almost) all the edges in the graph? - MutableNetwork filtered = Graphs.copyOf(graph); - edgesRemoved.clear(); - - for (int k=0;k bc = new BetweennessCentrality(filtered); - E to_remove = null; - double score = 0; - for (E e : filtered.edges()) - if (bc.getEdgeScore(e) > score) - { - to_remove = e; - score = bc.getEdgeScore(e); - } - edgesRemoved.add(to_remove); - filtered.removeEdge(to_remove); + /** + * Finds the set of clusters which have the strongest "community structure". The more edges + * removed the smaller and more cohesive the clusters. + * + * @param graph the graph + */ + public Set> apply(Network graph) { + Preconditions.checkArgument( + mNumEdgesToRemove <= graph.edges().size(), + "Number of edges to remove must be <= the number of edges in the graph"); + // TODO(jrtom): is there something smarter that we can do if we're removing + // (almost) all the edges in the graph? + MutableNetwork filtered = Graphs.copyOf(graph); + edgesRemoved.clear(); + + for (int k = 0; k < mNumEdgesToRemove; k++) { + BetweennessCentrality bc = new BetweennessCentrality(filtered); + E to_remove = null; + double score = 0; + for (E e : filtered.edges()) + if (bc.getEdgeScore(e) > score) { + to_remove = e; + score = bc.getEdgeScore(e); } + edgesRemoved.add(to_remove); + filtered.removeEdge(to_remove); + } - WeakComponentClusterer wcSearch = new WeakComponentClusterer(); - Set> clusterSet = wcSearch.apply(filtered); + WeakComponentClusterer wcSearch = new WeakComponentClusterer(); + Set> clusterSet = wcSearch.apply(filtered); - return clusterSet; - } + return clusterSet; + } - /** - * Retrieves the set of all edges that were removed. The edges returned - * are stored in order in which they were removed. - * - * @return the edges removed from the original graph - */ - public Set getEdgesRemoved() - { - return edgesRemoved; - } + /** + * Retrieves the set of all edges that were removed. The edges returned are stored in order in + * which they were removed. + * + * @return the edges removed from the original graph + */ + public Set getEdgesRemoved() { + return edgesRemoved; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/VoltageClusterer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/VoltageClusterer.java index 37fde521..26d66d3a 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/VoltageClusterer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/VoltageClusterer.java @@ -11,6 +11,11 @@ */ package edu.uci.ics.jung.algorithms.cluster; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.scoring.VoltageScorer; +import edu.uci.ics.jung.algorithms.util.DiscreteDistribution; +import edu.uci.ics.jung.algorithms.util.KMeansClusterer; +import edu.uci.ics.jung.algorithms.util.KMeansClusterer.NotEnoughClustersException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -24,348 +29,286 @@ import java.util.Random; import java.util.Set; -import com.google.common.graph.Network; - -import edu.uci.ics.jung.algorithms.scoring.VoltageScorer; -import edu.uci.ics.jung.algorithms.util.DiscreteDistribution; -import edu.uci.ics.jung.algorithms.util.KMeansClusterer; -import edu.uci.ics.jung.algorithms.util.KMeansClusterer.NotEnoughClustersException; - /** - *

Clusters vertices of a Network based on their ranks as - * calculated by VoltageScorer. This algorithm is based on, - * but not identical with, the method described in the paper below. - * The primary difference is that Wu and Huberman assume a priori that the clusters - * are of approximately the same size, and therefore use a more complex - * method than k-means (which is used here) for determining cluster - * membership based on co-occurrence data. + * Clusters vertices of a Network based on their ranks as calculated by + * VoltageScorer. This algorithm is based on, but not identical with, the method described in + * the paper below. The primary difference is that Wu and Huberman assume a priori that the clusters + * are of approximately the same size, and therefore use a more complex method than k-means (which + * is used here) for determining cluster membership based on co-occurrence data. * *

The algorithm proceeds as follows: + * *

    - *
  • first, generate a set of candidate clusters as follows: - *
      - *
    • pick (widely separated) vertex pair, run VoltageScorer - *
    • group the vertices in two clusters according to their voltages - *
    • store resulting candidate clusters - *
    - *
  • second, generate k-1 clusters as follows: - *
      - *
    • pick a vertex v as a cluster 'seed' - *
      (Wu/Huberman: most frequent vertex in candidate clusters) - *
    • calculate co-occurrence over all candidate clusters of v with each other - * vertex - *
    • separate co-occurrence counts into high/low; - * high vertices constitute a cluster - *
    • remove v's vertices from candidate clusters; continue - *
    - *
  • finally, remaining unassigned vertices are assigned to the kth ("garbage") - * cluster. + *
  • first, generate a set of candidate clusters as follows: + *
      + *
    • pick (widely separated) vertex pair, run VoltageScorer + *
    • group the vertices in two clusters according to their voltages + *
    • store resulting candidate clusters + *
    + *
  • second, generate k-1 clusters as follows: + *
      + *
    • pick a vertex v as a cluster 'seed'
      + * (Wu/Huberman: most frequent vertex in candidate clusters) + *
    • calculate co-occurrence over all candidate clusters of v with each other vertex + *
    • separate co-occurrence counts into high/low; high vertices constitute a cluster + *
    • remove v's vertices from candidate clusters; continue + *
    + *
  • finally, remaining unassigned vertices are assigned to the kth ("garbage") cluster. *
* - *

NOTE: Depending on how the co-occurrence data splits the data into - * clusters, the number of clusters returned by this algorithm may be less than the - * number of clusters requested. The number of clusters will never be more than - * the number requested, however. + *

NOTE: Depending on how the co-occurrence data splits the data into clusters, the number + * of clusters returned by this algorithm may be less than the number of clusters requested. The + * number of clusters will never be more than the number requested, however. * * @author Joshua O'Madadhain - * @see "'Finding communities in linear time: a physics approach', Fang Wu and Bernardo Huberman, http://www.hpl.hp.com/research/idl/papers/linear/" + * @see "'Finding communities in linear time: a physics approach', Fang Wu and Bernardo Huberman, + * http://www.hpl.hp.com/research/idl/papers/linear/" * @see VoltageScorer * @see KMeansClusterer */ -public class VoltageClusterer -{ - protected int num_candidates; - protected KMeansClusterer kmc; - protected Random rand; - protected Network g; - - /** - * Creates an instance of a VoltageCluster with the specified parameters. - * These are mostly parameters that are passed directly to VoltageScorer - * and KMeansClusterer. - * - * @param g the graph whose vertices are to be clustered - * @param num_candidates the number of candidate clusters to create - */ - public VoltageClusterer(Network g, int num_candidates) - { - if (num_candidates < 1) - throw new IllegalArgumentException("must generate >=1 candidates"); - - this.num_candidates = num_candidates; - this.kmc = new KMeansClusterer(); - rand = new Random(); - this.g = g; +public class VoltageClusterer { + protected int num_candidates; + protected KMeansClusterer kmc; + protected Random rand; + protected Network g; + + /** + * Creates an instance of a VoltageCluster with the specified parameters. These are mostly + * parameters that are passed directly to VoltageScorer and KMeansClusterer. + * + * @param g the graph whose vertices are to be clustered + * @param num_candidates the number of candidate clusters to create + */ + public VoltageClusterer(Network g, int num_candidates) { + if (num_candidates < 1) throw new IllegalArgumentException("must generate >=1 candidates"); + + this.num_candidates = num_candidates; + this.kmc = new KMeansClusterer(); + rand = new Random(); + this.g = g; + } + + protected void setRandomSeed(int random_seed) { + rand = new Random(random_seed); + } + + /** + * @param v the vertex whose community we wish to discover + * @return a community (cluster) centered around v. + */ + public Collection> getCommunity(V v) { + return cluster_internal(v, 2); + } + + /** + * Clusters the vertices of g into num_clusters clusters, based on their + * connectivity. + * + * @param num_clusters the number of clusters to identify + * @return a collection of clusters (sets of vertices) + */ + public Collection> cluster(int num_clusters) { + return cluster_internal(null, num_clusters); + } + + /** + * Does the work of getCommunity and cluster. + * + * @param origin the vertex around which clustering is to be done + * @param num_clusters the (maximum) number of clusters to find + * @return a collection of clusters (sets of vertices) + */ + protected Collection> cluster_internal(V origin, int num_clusters) { + // generate candidate clusters + // repeat the following 'samples' times: + // * pick (widely separated) vertex pair, run VoltageScorer + // * use k-means to identify 2 communities in ranked graph + // * store resulting candidate communities + ArrayList v_array = new ArrayList(g.nodes()); + + LinkedList> candidates = new LinkedList>(); + + for (int j = 0; j < num_candidates; j++) { + V source; + if (origin == null) source = v_array.get((int) (rand.nextDouble() * v_array.size())); + else source = origin; + V target = null; + do { + target = v_array.get((int) (rand.nextDouble() * v_array.size())); + } while (source == target); + VoltageScorer vs = new VoltageScorer(g, source, target); + vs.evaluate(); + + Map voltage_ranks = new HashMap(); + for (V v : g.nodes()) voltage_ranks.put(v, new double[] {vs.getVertexScore(v)}); + + // addOneCandidateCluster(candidates, voltage_ranks); + addTwoCandidateClusters(candidates, voltage_ranks); } - protected void setRandomSeed(int random_seed) - { - rand = new Random(random_seed); + // repeat the following k-1 times: + // * pick a vertex v as a cluster seed + // (Wu/Huberman: most frequent vertex in candidates) + // * calculate co-occurrence (in candidate clusters) + // of this vertex with all others + // * use k-means to separate co-occurrence counts into high/low; + // high vertices are a cluster + // * remove v's vertices from candidate clusters + + Collection> clusters = new LinkedList>(); + Set remaining = new HashSet(g.nodes()); + + List seed_candidates = getSeedCandidates(candidates); + int seed_index = 0; + + for (int j = 0; j < (num_clusters - 1); j++) { + if (remaining.isEmpty()) break; + + V seed; + if (seed_index == 0 && origin != null) seed = origin; + else { + do { + seed = seed_candidates.get(seed_index++); + } while (!remaining.contains(seed)); + } + + Map occur_counts = getObjectCounts(candidates, seed); + if (occur_counts.size() < 2) break; + + // now that we have the counts, cluster them... + try { + Collection> high_low = kmc.cluster(occur_counts, 2); + // ...get the cluster with the highest-valued centroid... + Iterator> h_iter = high_low.iterator(); + Map cluster1 = h_iter.next(); + Map cluster2 = h_iter.next(); + double[] centroid1 = DiscreteDistribution.mean(cluster1.values()); + double[] centroid2 = DiscreteDistribution.mean(cluster2.values()); + Set new_cluster; + if (centroid1[0] >= centroid2[0]) new_cluster = cluster1.keySet(); + else new_cluster = cluster2.keySet(); + + // ...remove the elements of new_cluster from each candidate... + for (Set cluster : candidates) cluster.removeAll(new_cluster); + clusters.add(new_cluster); + remaining.removeAll(new_cluster); + } catch (NotEnoughClustersException nece) { + // all remaining vertices are in the same cluster + break; + } } - /** - * @param v the vertex whose community we wish to discover - * @return a community (cluster) centered around v. - */ - public Collection> getCommunity(V v) - { - return cluster_internal(v, 2); + // identify remaining vertices (if any) as a 'garbage' cluster + if (!remaining.isEmpty()) clusters.add(remaining); + + return clusters; + } + + /** + * Do k-means with three intervals and pick the smaller two clusters (presumed to be on the ends); + * this is closer to the Wu-Huberman method. + * + * @param candidates the list of clusters to populate + * @param voltage_ranks the voltage values for each vertex + */ + protected void addTwoCandidateClusters( + LinkedList> candidates, Map voltage_ranks) { + try { + List> clusters = + new ArrayList>(kmc.cluster(voltage_ranks, 3)); + boolean b01 = clusters.get(0).size() > clusters.get(1).size(); + boolean b02 = clusters.get(0).size() > clusters.get(2).size(); + boolean b12 = clusters.get(1).size() > clusters.get(2).size(); + if (b01 && b02) { + candidates.add(clusters.get(1).keySet()); + candidates.add(clusters.get(2).keySet()); + } else if (!b01 && b12) { + candidates.add(clusters.get(0).keySet()); + candidates.add(clusters.get(2).keySet()); + } else if (!b02 && !b12) { + candidates.add(clusters.get(0).keySet()); + candidates.add(clusters.get(1).keySet()); + } + } catch (NotEnoughClustersException e) { + // no valid candidates, continue } - - /** - * Clusters the vertices of g into - * num_clusters clusters, based on their connectivity. - * @param num_clusters the number of clusters to identify - * @return a collection of clusters (sets of vertices) - */ - public Collection> cluster(int num_clusters) - { - return cluster_internal(null, num_clusters); + } + + /** + * alternative to addTwoCandidateClusters(): cluster vertices by voltages into 2 clusters. We only + * consider the smaller of the two clusters returned by k-means to be a 'true' cluster candidate; + * the other is a garbage cluster. + * + * @param candidates the list of clusters to populate + * @param voltage_ranks the voltage values for each vertex + */ + protected void addOneCandidateCluster( + LinkedList> candidates, Map voltage_ranks) { + try { + List> clusters; + clusters = new ArrayList>(kmc.cluster(voltage_ranks, 2)); + if (clusters.get(0).size() < clusters.get(1).size()) candidates.add(clusters.get(0).keySet()); + else candidates.add(clusters.get(1).keySet()); + } catch (NotEnoughClustersException e) { + // no valid candidates, continue } - - /** - * Does the work of getCommunity and cluster. - * @param origin the vertex around which clustering is to be done - * @param num_clusters the (maximum) number of clusters to find - * @return a collection of clusters (sets of vertices) - */ - protected Collection> cluster_internal(V origin, int num_clusters) - { - // generate candidate clusters - // repeat the following 'samples' times: - // * pick (widely separated) vertex pair, run VoltageScorer - // * use k-means to identify 2 communities in ranked graph - // * store resulting candidate communities - ArrayList v_array = new ArrayList(g.nodes()); - - LinkedList> candidates = new LinkedList>(); - - for (int j = 0; j < num_candidates; j++) - { - V source; - if (origin == null) - source = v_array.get((int)(rand.nextDouble() * v_array.size())); - else - source = origin; - V target = null; - do - { - target = v_array.get((int)(rand.nextDouble() * v_array.size())); - } - while (source == target); - VoltageScorer vs = new VoltageScorer(g, source, target); - vs.evaluate(); - - Map voltage_ranks = new HashMap(); - for (V v : g.nodes()) - voltage_ranks.put(v, new double[] {vs.getVertexScore(v)}); - -// addOneCandidateCluster(candidates, voltage_ranks); - addTwoCandidateClusters(candidates, voltage_ranks); - } - - // repeat the following k-1 times: - // * pick a vertex v as a cluster seed - // (Wu/Huberman: most frequent vertex in candidates) - // * calculate co-occurrence (in candidate clusters) - // of this vertex with all others - // * use k-means to separate co-occurrence counts into high/low; - // high vertices are a cluster - // * remove v's vertices from candidate clusters - - Collection> clusters = new LinkedList>(); - Set remaining = new HashSet(g.nodes()); - - List seed_candidates = getSeedCandidates(candidates); - int seed_index = 0; - - for (int j = 0; j < (num_clusters - 1); j++) - { - if (remaining.isEmpty()) - break; - - V seed; - if (seed_index == 0 && origin != null) - seed = origin; - else - { - do { seed = seed_candidates.get(seed_index++); } - while (!remaining.contains(seed)); - } - - Map occur_counts = getObjectCounts(candidates, seed); - if (occur_counts.size() < 2) - break; - - // now that we have the counts, cluster them... - try - { - Collection> high_low = kmc.cluster(occur_counts, 2); - // ...get the cluster with the highest-valued centroid... - Iterator> h_iter = high_low.iterator(); - Map cluster1 = h_iter.next(); - Map cluster2 = h_iter.next(); - double[] centroid1 = DiscreteDistribution.mean(cluster1.values()); - double[] centroid2 = DiscreteDistribution.mean(cluster2.values()); - Set new_cluster; - if (centroid1[0] >= centroid2[0]) - new_cluster = cluster1.keySet(); - else - new_cluster = cluster2.keySet(); - - // ...remove the elements of new_cluster from each candidate... - for (Set cluster : candidates) - cluster.removeAll(new_cluster); - clusters.add(new_cluster); - remaining.removeAll(new_cluster); - } - catch (NotEnoughClustersException nece) - { - // all remaining vertices are in the same cluster - break; - } + } + + /** + * Returns a list of cluster seeds, ranked in decreasing order of number of appearances in the + * specified collection of candidate clusters. + * + * @param candidates the set of candidate clusters + * @return a set of cluster seeds + */ + protected List getSeedCandidates(Collection> candidates) { + final Map occur_counts = getObjectCounts(candidates, null); + + ArrayList occurrences = new ArrayList(occur_counts.keySet()); + Collections.sort(occurrences, new MapValueArrayComparator(occur_counts)); + + // System.out.println("occurrences: "); + for (int i = 0; i < occurrences.size(); i++) + System.out.println(occur_counts.get(occurrences.get(i))[0]); + + return occurrences; + } + + protected Map getObjectCounts(Collection> candidates, V seed) { + Map occur_counts = new HashMap(); + for (V v : g.nodes()) occur_counts.put(v, new double[] {0}); + + for (Set candidate : candidates) { + if (seed == null) System.out.println(candidate.size()); + if (seed == null || candidate.contains(seed)) { + for (V element : candidate) { + double[] count = occur_counts.get(element); + count[0]++; } - - // identify remaining vertices (if any) as a 'garbage' cluster - if (!remaining.isEmpty()) - clusters.add(remaining); - - return clusters; + } } - /** - * Do k-means with three intervals and pick the smaller two clusters - * (presumed to be on the ends); this is closer to the Wu-Huberman method. - * @param candidates the list of clusters to populate - * @param voltage_ranks the voltage values for each vertex - */ - protected void addTwoCandidateClusters(LinkedList> candidates, - Map voltage_ranks) - { - try - { - List> clusters = new ArrayList>(kmc.cluster(voltage_ranks, 3)); - boolean b01 = clusters.get(0).size() > clusters.get(1).size(); - boolean b02 = clusters.get(0).size() > clusters.get(2).size(); - boolean b12 = clusters.get(1).size() > clusters.get(2).size(); - if (b01 && b02) - { - candidates.add(clusters.get(1).keySet()); - candidates.add(clusters.get(2).keySet()); - } - else if (!b01 && b12) - { - candidates.add(clusters.get(0).keySet()); - candidates.add(clusters.get(2).keySet()); - } - else if (!b02 && !b12) - { - candidates.add(clusters.get(0).keySet()); - candidates.add(clusters.get(1).keySet()); - } - } - catch (NotEnoughClustersException e) - { - // no valid candidates, continue - } + if (seed == null) { + System.out.println("occur_counts size: " + occur_counts.size()); + for (V v : occur_counts.keySet()) System.out.println(occur_counts.get(v)[0]); } - /** - * alternative to addTwoCandidateClusters(): cluster vertices by voltages into 2 clusters. - * We only consider the smaller of the two clusters returned - * by k-means to be a 'true' cluster candidate; the other is a garbage cluster. - * @param candidates the list of clusters to populate - * @param voltage_ranks the voltage values for each vertex - */ - protected void addOneCandidateCluster(LinkedList> candidates, - Map voltage_ranks) - { - try - { - List> clusters; - clusters = new ArrayList>(kmc.cluster(voltage_ranks, 2)); - if (clusters.get(0).size() < clusters.get(1).size()) - candidates.add(clusters.get(0).keySet()); - else - candidates.add(clusters.get(1).keySet()); - } - catch (NotEnoughClustersException e) - { - // no valid candidates, continue - } - } - - /** - * Returns a list of cluster seeds, ranked in decreasing order - * of number of appearances in the specified collection of candidate - * clusters. - * @param candidates the set of candidate clusters - * @return a set of cluster seeds - */ - protected List getSeedCandidates(Collection> candidates) - { - final Map occur_counts = getObjectCounts(candidates, null); - - ArrayList occurrences = new ArrayList(occur_counts.keySet()); - Collections.sort(occurrences, new MapValueArrayComparator(occur_counts)); - -// System.out.println("occurrences: "); - for (int i = 0; i < occurrences.size(); i++) - System.out.println(occur_counts.get(occurrences.get(i))[0]); - - return occurrences; - } + return occur_counts; + } - protected Map getObjectCounts(Collection> candidates, V seed) - { - Map occur_counts = new HashMap(); - for (V v : g.nodes()) - occur_counts.put(v, new double[]{0}); - - for (Set candidate : candidates) - { - if (seed == null) - System.out.println(candidate.size()); - if (seed == null || candidate.contains(seed)) - { - for (V element : candidate) - { - double[] count = occur_counts.get(element); - count[0]++; - } - } - } + protected class MapValueArrayComparator implements Comparator { + private Map map; - if (seed == null) - { - System.out.println("occur_counts size: " + occur_counts.size()); - for (V v : occur_counts.keySet()) - System.out.println(occur_counts.get(v)[0]); - } - - return occur_counts; + protected MapValueArrayComparator(Map map) { + this.map = map; } - protected class MapValueArrayComparator implements Comparator - { - private Map map; - - protected MapValueArrayComparator(Map map) - { - this.map = map; - } - - public int compare(V o1, V o2) - { - double[] count0 = map.get(o1); - double[] count1 = map.get(o2); - if (count0[0] < count1[0]) - return 1; - else if (count0[0] > count1[0]) - return -1; - return 0; - } - + public int compare(V o1, V o2) { + double[] count0 = map.get(o1); + double[] count1 = map.get(o2); + if (count0[0] < count1[0]) return 1; + else if (count0[0] > count1[0]) return -1; + return 0; } - + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/WeakComponentClusterer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/WeakComponentClusterer.java index 44240fc5..7d48943a 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/WeakComponentClusterer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/cluster/WeakComponentClusterer.java @@ -1,73 +1,70 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.cluster; +import com.google.common.base.Function; +import com.google.common.graph.Network; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.Queue; import java.util.Set; -import com.google.common.base.Function; -import com.google.common.graph.Network; - - - - /** - * Finds all weak components in a graph as sets of vertex sets. A weak component is defined as - * a maximal subgraph in which all pairs of vertices in the subgraph are reachable from one - * another in the underlying undirected subgraph. - *

This implementation identifies components as sets of vertex sets. - * To create the induced graphs from any or all of these vertex sets, - * see algorithms.filters.FilterUtils. - *

- * Running time: O(|V| + |E|) where |V| is the number of vertices and |E| is the number of edges. + * Finds all weak components in a graph as sets of vertex sets. A weak component is defined as a + * maximal subgraph in which all pairs of vertices in the subgraph are reachable from one another in + * the underlying undirected subgraph. + * + *

This implementation identifies components as sets of vertex sets. To create the induced graphs + * from any or all of these vertex sets, see algorithms.filters.FilterUtils. + * + *

Running time: O(|V| + |E|) where |V| is the number of vertices and |E| is the number of edges. + * * @author Scott White */ -public class WeakComponentClusterer implements Function, Set>> -{ - /** - * Extracts the weak components from a graph. - * @param graph the graph whose weak components are to be extracted - * @return the list of weak components - */ - public Set> apply(Network graph) { +public class WeakComponentClusterer implements Function, Set>> { + /** + * Extracts the weak components from a graph. + * + * @param graph the graph whose weak components are to be extracted + * @return the list of weak components + */ + public Set> apply(Network graph) { - Set> clusterSet = new HashSet>(); + Set> clusterSet = new HashSet>(); - HashSet unvisitedVertices = new HashSet(graph.nodes()); + HashSet unvisitedVertices = new HashSet(graph.nodes()); - while (!unvisitedVertices.isEmpty()) { - Set cluster = new HashSet(); - V root = unvisitedVertices.iterator().next(); - unvisitedVertices.remove(root); - cluster.add(root); + while (!unvisitedVertices.isEmpty()) { + Set cluster = new HashSet(); + V root = unvisitedVertices.iterator().next(); + unvisitedVertices.remove(root); + cluster.add(root); - Queue queue = new LinkedList(); - queue.add(root); + Queue queue = new LinkedList(); + queue.add(root); - while (!queue.isEmpty()) { - V currentVertex = queue.remove(); - Collection neighbors = graph.adjacentNodes(currentVertex); + while (!queue.isEmpty()) { + V currentVertex = queue.remove(); + Collection neighbors = graph.adjacentNodes(currentVertex); - for(V neighbor : neighbors) { - if (unvisitedVertices.contains(neighbor)) { - queue.add(neighbor); - unvisitedVertices.remove(neighbor); - cluster.add(neighbor); - } - } - } - clusterSet.add(cluster); + for (V neighbor : neighbors) { + if (unvisitedVertices.contains(neighbor)) { + queue.add(neighbor); + unvisitedVertices.remove(neighbor); + cluster.add(neighbor); + } } - return clusterSet; + } + clusterSet.add(cluster); } + return clusterSet; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/filters/KNeighborhoodFilter.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/filters/KNeighborhoodFilter.java index 6c8809a5..9d5da977 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/filters/KNeighborhoodFilter.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/filters/KNeighborhoodFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, The JUNG Authors + * Copyright (c) 2003, The JUNG Authors * * All rights reserved. * @@ -16,66 +16,64 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import java.util.ArrayDeque; -import java.util.Queue; -import java.util.Set; - import com.google.common.graph.Graph; import com.google.common.graph.GraphBuilder; import com.google.common.graph.MutableGraph; - +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.Set; /** - * A filter used to extract the k-neighborhood around a set of root nodes. - * The k-neighborhood is defined as the subgraph induced by the set of - * nodes that are k or fewer hops away from the root node. - * + * A filter used to extract the k-neighborhood around a set of root nodes. The k-neighborhood is + * defined as the subgraph induced by the set of nodes that are k or fewer hops away from the root + * node. + * * @author Danyel Fisher * @author Joshua O'Madadhain */ public class KNeighborhoodFilter { - // TODO: create ValueGraph/Network versions - public static MutableGraph filterGraph(Graph graph, Set rootNodes, int radius) { - checkNotNull(graph); - checkNotNull(rootNodes); - checkArgument(graph.nodes().containsAll(rootNodes), "graph must contain all of rootNodes"); - checkArgument(radius > 0, "radius must be > 0"); + // TODO: create ValueGraph/Network versions + public static MutableGraph filterGraph(Graph graph, Set rootNodes, int radius) { + checkNotNull(graph); + checkNotNull(rootNodes); + checkArgument(graph.nodes().containsAll(rootNodes), "graph must contain all of rootNodes"); + checkArgument(radius > 0, "radius must be > 0"); + + MutableGraph filtered = GraphBuilder.from(graph).build(); + for (N root : rootNodes) { + filtered.addNode(root); + } + Queue currentNodes = new ArrayDeque<>(rootNodes); + Queue nextNodes = new ArrayDeque<>(); + + for (int depth = 1; depth <= radius && !currentNodes.isEmpty(); depth++) { + while (!currentNodes.isEmpty()) { + N currentNode = currentNodes.remove(); + for (N nextNode : graph.successors(currentNode)) { + // the addNode needs to happen before putEdge() because we need to know whether + // the node was present in the graph + // (and putEdge() will always add the node if not present) + if (filtered.addNode(nextNode)) { + nextNodes.add(nextNode); + } + filtered.putEdge(currentNode, nextNode); + } + } + Queue emptyQueue = currentNodes; + currentNodes = nextNodes; + nextNodes = emptyQueue; + } + + // put in in-edges from nodes in the filtered graph + for (N node : filtered.nodes()) { + for (N predecessor : graph.predecessors(node)) { + if (filtered.nodes().contains(predecessor)) { + filtered.putEdge(predecessor, node); + } + } + } - MutableGraph filtered = GraphBuilder.from(graph).build(); - for (N root : rootNodes) { - filtered.addNode(root); - } - Queue currentNodes = new ArrayDeque<>(rootNodes); - Queue nextNodes = new ArrayDeque<>(); - - for (int depth = 1; depth <= radius && !currentNodes.isEmpty(); depth++) { - while (!currentNodes.isEmpty()) { - N currentNode = currentNodes.remove(); - for (N nextNode : graph.successors(currentNode)) { - // the addNode needs to happen before putEdge() because we need to know whether - // the node was present in the graph - // (and putEdge() will always add the node if not present) - if (filtered.addNode(nextNode)) { - nextNodes.add(nextNode); - } - filtered.putEdge(currentNode, nextNode); - } - } - Queue emptyQueue = currentNodes; - currentNodes = nextNodes; - nextNodes = emptyQueue; - } - - // put in in-edges from nodes in the filtered graph - for (N node : filtered.nodes()) { - for (N predecessor : graph.predecessors(node)) { - if (filtered.nodes().contains(predecessor)) { - filtered.putEdge(predecessor, node); - } - } - } - - return filtered; - } + return filtered; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/flows/EdmondsKarpMaxFlow.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/flows/EdmondsKarpMaxFlow.java index 06b4f51c..799243bf 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/flows/EdmondsKarpMaxFlow.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/flows/EdmondsKarpMaxFlow.java @@ -1,21 +1,14 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.flows; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Map; -import java.util.Queue; -import java.util.Set; - import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.base.Supplier; @@ -23,26 +16,31 @@ import com.google.common.graph.Graphs; import com.google.common.graph.MutableNetwork; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.util.IterativeProcess; - +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.Set; /** - * Implements the Edmonds-Karp maximum flow algorithm for solving the maximum flow problem. - * After the algorithm is executed, - * the input {@code Map} is populated with a {@code Integer} for each edge that indicates - * the flow along that edge. - *

- * An example of using this algorithm is as follows: + * Implements the Edmonds-Karp maximum flow algorithm for solving the maximum flow problem. After + * the algorithm is executed, the input {@code Map} is populated with a {@code Integer} for each + * edge that indicates the flow along that edge. + * + *

An example of using this algorithm is as follows: + * *

- * EdmondsKarpMaxFlow ek = new EdmondsKarpMaxFlow(graph, source, sink, edge_capacities, edge_flows, 
+ * EdmondsKarpMaxFlow ek = new EdmondsKarpMaxFlow(graph, source, sink, edge_capacities, edge_flows,
  * edge_factory);
  * ek.evaluate(); // This instructs the class to compute the max flow
  * 
* * @see "Introduction to Algorithms by Cormen, Leiserson, Rivest, and Stein." * @see "Network Flows by Ahuja, Magnanti, and Orlin." - * @see "Theoretical improvements in algorithmic efficiency for network flow problems by Edmonds and Karp, 1972." + * @see "Theoretical improvements in algorithmic efficiency for network flow problems by Edmonds and + * Karp, 1972." * @author Scott White, adapted to jung2 by Tom Nelson */ // TODO: this should work for input ValueGraphs also @@ -50,249 +48,247 @@ // TODO: this currently works on Integers; can probably be generalized at least somewhat // TODO: does this algorithm in fact actually fail for undirected graphs? // TODO: no reason why the user should have to supply the edgeFlowMap -public class EdmondsKarpMaxFlow extends IterativeProcess { - - private MutableNetwork flowNetwork; - private Network network; - private N source; - private N target; - private int maxFlow; - private Set sourcePartitionNodes; - private Set sinkPartitionNodes; - private Set minCutEdges; - - private Map residualCapacityMap = new HashMap(); - private Map parentMap = new HashMap(); - private Map parentCapacityMap = new HashMap(); - private Function edgeCapacityTransformer; - private Map edgeFlowMap; - private Supplier edgeFactory; - - /** - * Constructs a new instance of the algorithm solver for a given graph, source, and sink. - * Source and sink vertices must be elements of the specified graph, and must be - * distinct. - * @param network the flow graph - * @param source the source vertex - * @param sink the sink vertex - * @param edgeCapacityTransformer the Function that gets the capacity for each edge. - * @param edgeFlowMap the map where the solver will place the value of the flow for each edge - * @param edgeFactory used to create new edge instances for backEdges - */ - public EdmondsKarpMaxFlow(Network network, N source, N sink, - Function edgeCapacityTransformer, Map edgeFlowMap, - Supplier edgeFactory) { - Preconditions.checkArgument(network.isDirected(), "input graph must be directed"); - Preconditions.checkArgument(network.nodes().contains(source), "input graph must contain source node"); - Preconditions.checkArgument(network.nodes().contains(sink), "input graph must contain sink node"); - Preconditions.checkArgument(!source.equals(sink), "source and sink nodes must be distinct"); - - this.network = network; - - this.source = source; - this.target = sink; - this.edgeFlowMap = edgeFlowMap; - this.edgeCapacityTransformer = edgeCapacityTransformer; - this.edgeFactory = edgeFactory; - this.flowNetwork = Graphs.copyOf(network); - maxFlow = 0; - sinkPartitionNodes = new HashSet(); - sourcePartitionNodes = new HashSet(); - minCutEdges = new HashSet(); - } - - private void clearParentValues() { - parentMap.clear(); - parentCapacityMap.clear(); - parentCapacityMap.put(source, Integer.MAX_VALUE); - parentMap.put(source, source); - } - - protected boolean hasAugmentingPath() { - sinkPartitionNodes.clear(); - sourcePartitionNodes.clear(); - sinkPartitionNodes.addAll(flowNetwork.nodes()); - - Set visitedEdgesMap = new HashSet(); - Queue queue = new LinkedList(); - queue.add(source); - - while (!queue.isEmpty()) { - N currentVertex = queue.remove(); - sinkPartitionNodes.remove(currentVertex); - sourcePartitionNodes.add(currentVertex); - Integer currentCapacity = parentCapacityMap.get(currentVertex); - - for (E neighboringEdge : flowNetwork.outEdges(currentVertex)) { - - N neighboringVertex = flowNetwork.incidentNodes(neighboringEdge).target(); - - Integer residualCapacity = residualCapacityMap.get(neighboringEdge); - if (residualCapacity <= 0 || visitedEdgesMap.contains(neighboringEdge)) - continue; - - N neighborsParent = parentMap.get(neighboringVertex); - Integer neighborCapacity = parentCapacityMap.get(neighboringVertex); - int newCapacity = Math.min(residualCapacity,currentCapacity); - - if ((neighborsParent == null) || newCapacity > neighborCapacity) { - parentMap.put(neighboringVertex, currentVertex); - parentCapacityMap.put(neighboringVertex, newCapacity); - visitedEdgesMap.add(neighboringEdge); - if (neighboringVertex != target) { - queue.add(neighboringVertex); - } - } - } - } - - boolean hasAugmentingPath = false; - Integer targetsParentCapacity = parentCapacityMap.get(target); - if (targetsParentCapacity != null && targetsParentCapacity > 0) { - updateResidualCapacities(); - hasAugmentingPath = true; +public class EdmondsKarpMaxFlow extends IterativeProcess { + + private MutableNetwork flowNetwork; + private Network network; + private N source; + private N target; + private int maxFlow; + private Set sourcePartitionNodes; + private Set sinkPartitionNodes; + private Set minCutEdges; + + private Map residualCapacityMap = new HashMap(); + private Map parentMap = new HashMap(); + private Map parentCapacityMap = new HashMap(); + private Function edgeCapacityTransformer; + private Map edgeFlowMap; + private Supplier edgeFactory; + + /** + * Constructs a new instance of the algorithm solver for a given graph, source, and sink. Source + * and sink vertices must be elements of the specified graph, and must be distinct. + * + * @param network the flow graph + * @param source the source vertex + * @param sink the sink vertex + * @param edgeCapacityTransformer the Function that gets the capacity for each edge. + * @param edgeFlowMap the map where the solver will place the value of the flow for each edge + * @param edgeFactory used to create new edge instances for backEdges + */ + public EdmondsKarpMaxFlow( + Network network, + N source, + N sink, + Function edgeCapacityTransformer, + Map edgeFlowMap, + Supplier edgeFactory) { + Preconditions.checkArgument(network.isDirected(), "input graph must be directed"); + Preconditions.checkArgument( + network.nodes().contains(source), "input graph must contain source node"); + Preconditions.checkArgument( + network.nodes().contains(sink), "input graph must contain sink node"); + Preconditions.checkArgument(!source.equals(sink), "source and sink nodes must be distinct"); + + this.network = network; + + this.source = source; + this.target = sink; + this.edgeFlowMap = edgeFlowMap; + this.edgeCapacityTransformer = edgeCapacityTransformer; + this.edgeFactory = edgeFactory; + this.flowNetwork = Graphs.copyOf(network); + maxFlow = 0; + sinkPartitionNodes = new HashSet(); + sourcePartitionNodes = new HashSet(); + minCutEdges = new HashSet(); + } + + private void clearParentValues() { + parentMap.clear(); + parentCapacityMap.clear(); + parentCapacityMap.put(source, Integer.MAX_VALUE); + parentMap.put(source, source); + } + + protected boolean hasAugmentingPath() { + sinkPartitionNodes.clear(); + sourcePartitionNodes.clear(); + sinkPartitionNodes.addAll(flowNetwork.nodes()); + + Set visitedEdgesMap = new HashSet(); + Queue queue = new LinkedList(); + queue.add(source); + + while (!queue.isEmpty()) { + N currentVertex = queue.remove(); + sinkPartitionNodes.remove(currentVertex); + sourcePartitionNodes.add(currentVertex); + Integer currentCapacity = parentCapacityMap.get(currentVertex); + + for (E neighboringEdge : flowNetwork.outEdges(currentVertex)) { + + N neighboringVertex = flowNetwork.incidentNodes(neighboringEdge).target(); + + Integer residualCapacity = residualCapacityMap.get(neighboringEdge); + if (residualCapacity <= 0 || visitedEdgesMap.contains(neighboringEdge)) continue; + + N neighborsParent = parentMap.get(neighboringVertex); + Integer neighborCapacity = parentCapacityMap.get(neighboringVertex); + int newCapacity = Math.min(residualCapacity, currentCapacity); + + if ((neighborsParent == null) || newCapacity > neighborCapacity) { + parentMap.put(neighboringVertex, currentVertex); + parentCapacityMap.put(neighboringVertex, newCapacity); + visitedEdgesMap.add(neighboringEdge); + if (neighboringVertex != target) { + queue.add(neighboringVertex); + } } - clearParentValues(); - return hasAugmentingPath; + } } - @Override - public void step() { - while (hasAugmentingPath()) { - } - computeMinCut(); + boolean hasAugmentingPath = false; + Integer targetsParentCapacity = parentCapacityMap.get(target); + if (targetsParentCapacity != null && targetsParentCapacity > 0) { + updateResidualCapacities(); + hasAugmentingPath = true; } - - private void computeMinCut() { - - for (E e : network.edges()) { - EndpointPair endpoints = network.incidentNodes(e); - N source = endpoints.source(); - N destination = endpoints.target(); - if (sinkPartitionNodes.contains(source) && sinkPartitionNodes.contains(destination)) { - continue; - } - if (sourcePartitionNodes.contains(source) && sourcePartitionNodes.contains(destination)) { - continue; - } - if (sinkPartitionNodes.contains(source) && sourcePartitionNodes.contains(destination)) { - continue; - } - minCutEdges.add(e); - } + clearParentValues(); + return hasAugmentingPath; + } + + @Override + public void step() { + while (hasAugmentingPath()) {} + computeMinCut(); + } + + private void computeMinCut() { + + for (E e : network.edges()) { + EndpointPair endpoints = network.incidentNodes(e); + N source = endpoints.source(); + N destination = endpoints.target(); + if (sinkPartitionNodes.contains(source) && sinkPartitionNodes.contains(destination)) { + continue; + } + if (sourcePartitionNodes.contains(source) && sourcePartitionNodes.contains(destination)) { + continue; + } + if (sinkPartitionNodes.contains(source) && sourcePartitionNodes.contains(destination)) { + continue; + } + minCutEdges.add(e); } - - /** - * @return the value of the maximum flow from the source to the sink. - */ - public int getMaxFlow() { - return maxFlow; + } + + /** @return the value of the maximum flow from the source to the sink. */ + public int getMaxFlow() { + return maxFlow; + } + + /** + * @return the nodes which share the same partition (as defined by the min-cut edges) as the sink + * node. + */ + public Set getNodesInSinkPartition() { + return sinkPartitionNodes; + } + + /** + * @return the nodes which share the same partition (as defined by the min-cut edges) as the + * source node. + */ + public Set getNodesInSourcePartition() { + return sourcePartitionNodes; + } + + /** @return the edges in the minimum cut. */ + public Set getMinCutEdges() { + return minCutEdges; + } + + /** @return the graph for which the maximum flow is calculated. */ + public Network getFlowGraph() { + return flowNetwork; + } + + @Override + protected void initializeIterations() { + parentCapacityMap.put(source, Integer.MAX_VALUE); + parentMap.put(source, source); + + Set> backEdges = new HashSet<>(); + for (E edge : flowNetwork.edges()) { + Integer capacity = edgeCapacityTransformer.apply(edge); + Preconditions.checkNotNull(capacity, "Edge capacities must exist for all edges"); + + residualCapacityMap.put(edge, capacity); + EndpointPair endpoints = flowNetwork.incidentNodes(edge); + N source = endpoints.source(); + N destination = endpoints.target(); + + if (!flowNetwork.successors(destination).contains(source)) { + backEdges.add(EndpointPair.ordered(destination, source)); + } } - /** - * @return the nodes which share the same partition (as defined by the min-cut edges) - * as the sink node. - */ - public Set getNodesInSinkPartition() { - return sinkPartitionNodes; + for (EndpointPair endpoints : backEdges) { + E backEdge = edgeFactory.get(); + flowNetwork.addEdge(endpoints.source(), endpoints.target(), backEdge); + residualCapacityMap.put(backEdge, 0); } + } - /** - * @return the nodes which share the same partition (as defined by the min-cut edges) - * as the source node. - */ - public Set getNodesInSourcePartition() { - return sourcePartitionNodes; - } + @Override + protected void finalizeIterations() { - /** - * @return the edges in the minimum cut. - */ - public Set getMinCutEdges() { - return minCutEdges; - } + for (E currentEdge : flowNetwork.edges()) { + Integer capacity = edgeCapacityTransformer.apply(currentEdge); - /** - * @return the graph for which the maximum flow is calculated. - */ - public Network getFlowGraph() { - return flowNetwork; + Integer residualCapacity = residualCapacityMap.get(currentEdge); + if (capacity != null) { + Integer flowValue = capacity - residualCapacity; + this.edgeFlowMap.put(currentEdge, flowValue); + } } - @Override - protected void initializeIterations() { - parentCapacityMap.put(source, Integer.MAX_VALUE); - parentMap.put(source, source); - - Set> backEdges = new HashSet<>(); - for (E edge : flowNetwork.edges()) { - Integer capacity = edgeCapacityTransformer.apply(edge); - Preconditions.checkNotNull(capacity, "Edge capacities must exist for all edges"); - - residualCapacityMap.put(edge, capacity); - EndpointPair endpoints = flowNetwork.incidentNodes(edge); - N source = endpoints.source(); - N destination = endpoints.target(); + Set backEdges = new HashSet(); + for (E currentEdge : flowNetwork.edges()) { - if (!flowNetwork.successors(destination).contains(source)) { - backEdges.add(EndpointPair.ordered(destination, source)); - } - } - - for (EndpointPair endpoints : backEdges) { - E backEdge = edgeFactory.get(); - flowNetwork.addEdge(endpoints.source(), endpoints.target(), backEdge); - residualCapacityMap.put(backEdge, 0); - } + if (edgeCapacityTransformer.apply(currentEdge) == null) { + backEdges.add(currentEdge); + } else { + residualCapacityMap.remove(currentEdge); + } } - - @Override - protected void finalizeIterations() { - - for (E currentEdge : flowNetwork.edges()) { - Integer capacity = edgeCapacityTransformer.apply(currentEdge); - - Integer residualCapacity = residualCapacityMap.get(currentEdge); - if (capacity != null) { - Integer flowValue = capacity - residualCapacity; - this.edgeFlowMap.put(currentEdge, flowValue); - } - } - - Set backEdges = new HashSet(); - for (E currentEdge: flowNetwork.edges()) { - - if (edgeCapacityTransformer.apply(currentEdge) == null) { - backEdges.add(currentEdge); - } else { - residualCapacityMap.remove(currentEdge); - } - } - for(E e : backEdges) { - flowNetwork.removeEdge(e); - } + for (E e : backEdges) { + flowNetwork.removeEdge(e); } + } - private void updateResidualCapacities() { + private void updateResidualCapacities() { - Integer augmentingPathCapacity = parentCapacityMap.get(target); - maxFlow += augmentingPathCapacity; - N currentVertex = target; - N parentVertex = null; - while ((parentVertex = parentMap.get(currentVertex)) != currentVertex) { - // TODO: change this to edgeConnecting() once we are using Guava 22.0+ - E currentEdge = flowNetwork.edgesConnecting(parentVertex, currentVertex).iterator().next(); + Integer augmentingPathCapacity = parentCapacityMap.get(target); + maxFlow += augmentingPathCapacity; + N currentVertex = target; + N parentVertex = null; + while ((parentVertex = parentMap.get(currentVertex)) != currentVertex) { + // TODO: change this to edgeConnecting() once we are using Guava 22.0+ + E currentEdge = flowNetwork.edgesConnecting(parentVertex, currentVertex).iterator().next(); - Integer residualCapacity = residualCapacityMap.get(currentEdge); + Integer residualCapacity = residualCapacityMap.get(currentEdge); - residualCapacity = residualCapacity - augmentingPathCapacity; - residualCapacityMap.put(currentEdge, residualCapacity); + residualCapacity = residualCapacity - augmentingPathCapacity; + residualCapacityMap.put(currentEdge, residualCapacity); - E backEdge = flowNetwork.edgesConnecting(currentVertex, parentVertex).iterator().next(); - residualCapacity = residualCapacityMap.get(backEdge); - residualCapacity = residualCapacity + augmentingPathCapacity; - residualCapacityMap.put(backEdge, residualCapacity); - currentVertex = parentVertex; - } + E backEdge = flowNetwork.edgesConnecting(currentVertex, parentVertex).iterator().next(); + residualCapacity = residualCapacityMap.get(backEdge); + residualCapacity = residualCapacity + augmentingPathCapacity; + residualCapacityMap.put(backEdge, residualCapacity); + currentVertex = parentVertex; } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/Lattice2DGenerator.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/Lattice2DGenerator.java index 03f46003..a76a876e 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/Lattice2DGenerator.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/Lattice2DGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, The JUNG Authors + * Copyright (c) 2009, The JUNG Authors * * All rights reserved. * @@ -10,13 +10,6 @@ package edu.uci.ics.jung.algorithms.generators; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.cache.CacheBuilder; @@ -26,190 +19,196 @@ import com.google.common.graph.Graph; import com.google.common.graph.MutableNetwork; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.shortestpath.Distance; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** - * Simple generator of graphs in the shape of an m x n lattice where each vertex - * is adjacent to each of its neighbors (to the left, right, up, and down). - * May be toroidal, in which case the vertices on the boundaries are connected to - * their counterparts on the opposite boundaries as well. - * + * Simple generator of graphs in the shape of an m x n lattice where each vertex is adjacent to each + * of its neighbors (to the left, right, up, and down). May be toroidal, in which case the vertices + * on the boundaries are connected to their counterparts on the opposite boundaries as well. + * * @author Joshua O'Madadhain */ -public class Lattice2DGenerator -{ - private final int rowCount; - private final int colCount; - private final boolean toroidal; - - // TODO: consider using a Builder here as well - - /** - * Creates an instance which generates graphs of the specified dimensions. - * If {@code toroidal} is true, the nodes on the column 0 are connected to - * the nodes on column n-1, and the nodes on row 0 are connected to the - * nodes on row n-1, forming a torus shape. - * - * @param rowCount - * @param colCount - * @param toroidal - */ - public Lattice2DGenerator(int rowCount, int colCount, boolean toroidal) { - // TODO: relax the row/col count restrictions to be >= 3 once we get the random selection mechanism - // in KleinbergSmallWorld to behave better - Preconditions.checkArgument(rowCount >= 4, "row count must be >= 4"); - Preconditions.checkArgument(colCount >= 4, "column count must be >= 4"); - this.rowCount = rowCount; - this.colCount = colCount; - this.toroidal = toroidal; - } - - /** - * Creates a lattice-shaped {@code Network} with the specified node and edge - * suppliers, and direction. - * - * @param directed - * @param nodeFactory - * @param edgeFactory - * @return - */ - public MutableNetwork generateNetwork(boolean directed, - Supplier nodeFactory, - Supplier edgeFactory) { - Preconditions.checkNotNull(nodeFactory); - Preconditions.checkNotNull(edgeFactory); - - int vertex_count = rowCount * colCount; - - int boundary_adjustment = (toroidal ? 0 : 1); - int edge_count = colCount * (rowCount - boundary_adjustment) + // vertical edges - rowCount * (colCount - boundary_adjustment); // horizontal edges - if (directed) { - edge_count *= 2; - } - - NetworkBuilder builder = - directed ? NetworkBuilder.directed() : NetworkBuilder.undirected(); - MutableNetwork graph = - builder.expectedNodeCount(vertex_count).expectedEdgeCount(edge_count).build(); - - for (int i = 0; i < vertex_count; i++) - { - N v = nodeFactory.get(); - graph.addNode(v); - } - List elements = new ArrayList(graph.nodes()); - - int end_row = toroidal ? rowCount : rowCount - 1; - int end_col = toroidal ? colCount : colCount - 1; - - // fill in edges - // down - for (int i = 0; i < end_row; i++) - for (int j = 0; j < colCount; j++) - graph.addEdge(elements.get(getIndex(i, j)), elements.get(getIndex(i + 1, j)), edgeFactory.get()); - // right - for (int i = 0; i < rowCount; i++) - for (int j = 0; j < end_col; j++) - graph.addEdge(elements.get(getIndex(i, j)), elements.get(getIndex(i, j + 1)), edgeFactory.get()); - - // if the graph is directed, fill in the edges going the other directions - if (graph.isDirected()) - { - Set> endpointPairs = new HashSet<>(); - for (E edge : graph.edges()) { - endpointPairs.add(graph.incidentNodes(edge)); - } - - for (EndpointPair endpoints : endpointPairs) { - graph.addEdge(endpoints.target(), endpoints.source(), edgeFactory.get()); - } - } - return graph; +public class Lattice2DGenerator { + private final int rowCount; + private final int colCount; + private final boolean toroidal; + + // TODO: consider using a Builder here as well + + /** + * Creates an instance which generates graphs of the specified dimensions. If {@code toroidal} is + * true, the nodes on the column 0 are connected to the nodes on column n-1, and the nodes on row + * 0 are connected to the nodes on row n-1, forming a torus shape. + * + * @param rowCount + * @param colCount + * @param toroidal + */ + public Lattice2DGenerator(int rowCount, int colCount, boolean toroidal) { + // TODO: relax the row/col count restrictions to be >= 3 once we get the random selection mechanism + // in KleinbergSmallWorld to behave better + Preconditions.checkArgument(rowCount >= 4, "row count must be >= 4"); + Preconditions.checkArgument(colCount >= 4, "column count must be >= 4"); + this.rowCount = rowCount; + this.colCount = colCount; + this.toroidal = toroidal; + } + + /** + * Creates a lattice-shaped {@code Network} with the specified node and edge suppliers, and + * direction. + * + * @param directed + * @param nodeFactory + * @param edgeFactory + * @return + */ + public MutableNetwork generateNetwork( + boolean directed, Supplier nodeFactory, Supplier edgeFactory) { + Preconditions.checkNotNull(nodeFactory); + Preconditions.checkNotNull(edgeFactory); + + int vertex_count = rowCount * colCount; + + int boundary_adjustment = (toroidal ? 0 : 1); + int edge_count = + colCount * (rowCount - boundary_adjustment) + + // vertical edges + rowCount * (colCount - boundary_adjustment); // horizontal edges + if (directed) { + edge_count *= 2; + } + + NetworkBuilder builder = + directed ? NetworkBuilder.directed() : NetworkBuilder.undirected(); + MutableNetwork graph = + builder.expectedNodeCount(vertex_count).expectedEdgeCount(edge_count).build(); + + for (int i = 0; i < vertex_count; i++) { + N v = nodeFactory.get(); + graph.addNode(v); + } + List elements = new ArrayList(graph.nodes()); + + int end_row = toroidal ? rowCount : rowCount - 1; + int end_col = toroidal ? colCount : colCount - 1; + + // fill in edges + // down + for (int i = 0; i < end_row; i++) + for (int j = 0; j < colCount; j++) + graph.addEdge( + elements.get(getIndex(i, j)), elements.get(getIndex(i + 1, j)), edgeFactory.get()); + // right + for (int i = 0; i < rowCount; i++) + for (int j = 0; j < end_col; j++) + graph.addEdge( + elements.get(getIndex(i, j)), elements.get(getIndex(i, j + 1)), edgeFactory.get()); + + // if the graph is directed, fill in the edges going the other directions + if (graph.isDirected()) { + Set> endpointPairs = new HashSet<>(); + for (E edge : graph.edges()) { + endpointPairs.add(graph.incidentNodes(edge)); + } + + for (EndpointPair endpoints : endpointPairs) { + graph.addEdge(endpoints.target(), endpoints.source(), edgeFactory.get()); + } + } + return graph; + } + + // TODO: this way of getting a Distance is kind of messed up: it shouldn't be possible to + // get a Distance for a graph other than the one provided, but it is because of how the API works. + // Fix this. + + /** + * Returns a {@code Distance} implementation that assumes that {@code graph} is lattice-shaped. + * + * @param graph + * @return + */ + public Distance distance(Graph graph) { + return new LatticeDistance(graph); + } + + private class LatticeDistance implements Distance { + private final Map nodeIndices = new HashMap<>(); + private final LoadingCache> distances = + CacheBuilder.newBuilder() + .build( + new CacheLoader>() { + public LoadingCache load(N source) { + return CacheBuilder.newBuilder() + .build( + new CacheLoader() { + public Number load(N target) { + return getDistance(source, target); + } + }); + } + }); + + private LatticeDistance(Graph graph) { + Preconditions.checkNotNull(graph); + int index = 0; + for (N node : graph.nodes()) { + nodeIndices.put(node, index++); + } } - - // TODO: this way of getting a Distance is kind of messed up: it shouldn't be possible to - // get a Distance for a graph other than the one provided, but it is because of how the API works. - // Fix this. - + + public Number getDistance(N source, N target) { + int sourceIndex = nodeIndices.get(source); + int targetIndex = nodeIndices.get(target); + int sourceRow = getRow(sourceIndex); + int sourceCol = getCol(sourceIndex); + int targetRow = getRow(targetIndex); + int targetCol = getCol(targetIndex); + + int v_dist = Math.abs(sourceRow - targetRow); + int h_dist = Math.abs(sourceCol - targetCol); + if (toroidal) { + v_dist = Math.min(v_dist, Math.abs(rowCount - v_dist) + 1); + h_dist = Math.min(h_dist, Math.abs(colCount - h_dist) + 1); + } + return v_dist + h_dist; + } + + @Override + public Map getDistanceMap(N source) { + return distances.getUnchecked(source).asMap(); + } + /** - * Returns a {@code Distance} implementation that assumes that {@code graph} is lattice-shaped. - * @param graph - * @return + * @param i index of the vertex whose row we want + * @return the row in which the vertex with index {@code i} is found */ - public Distance distance(Graph graph) { - return new LatticeDistance(graph); + private int getRow(int i) { + return i / colCount; } - - private class LatticeDistance implements Distance { - private final Map nodeIndices = new HashMap<>(); - private final LoadingCache> distances = - CacheBuilder.newBuilder().build(new CacheLoader>() { - public LoadingCache load(N source) { - return CacheBuilder.newBuilder().build(new CacheLoader() { - public Number load(N target) { - return getDistance(source, target); - } - }); - }}); - - private LatticeDistance(Graph graph) { - Preconditions.checkNotNull(graph); - int index = 0; - for (N node : graph.nodes()) { - nodeIndices.put(node, index++); - } - } - - public Number getDistance(N source, N target) { - int sourceIndex = nodeIndices.get(source); - int targetIndex = nodeIndices.get(target); - int sourceRow = getRow(sourceIndex); - int sourceCol = getCol(sourceIndex); - int targetRow = getRow(targetIndex); - int targetCol = getCol(targetIndex); - - int v_dist = Math.abs(sourceRow - targetRow); - int h_dist = Math.abs(sourceCol - targetCol); - if (toroidal) - { - v_dist = Math.min(v_dist, Math.abs(rowCount - v_dist) + 1); - h_dist = Math.min(h_dist, Math.abs(colCount - h_dist) + 1); - } - return v_dist + h_dist; - } - - @Override - public Map getDistanceMap(N source) { - return distances.getUnchecked(source).asMap(); - } - - /** - * @param i - * index of the vertex whose row we want - * @return the row in which the vertex with index {@code i} is found - */ - private int getRow(int i) { - return i / colCount; - } - - /** - * @param i - * index of the vertex whose column we want - * @return the column in which the vertex with index {@code i} is found - */ - private int getCol(int i) { - return i % colCount; - } + + /** + * @param i index of the vertex whose column we want + * @return the column in which the vertex with index {@code i} is found + */ + private int getCol(int i) { + return i % colCount; } - - int getIndex(int i, int j) { - return ((mod(i, rowCount)) * colCount) + (mod(j, colCount)); - } - - private int mod(int i, int modulus) { - int i_mod = i % modulus; - return i_mod >= 0 ? i_mod : i_mod + modulus; - } + } + + int getIndex(int i, int j) { + return ((mod(i, rowCount)) * colCount) + (mod(j, colCount)); + } + + private int mod(int i, int modulus) { + int i_mod = i % modulus; + return i_mod >= 0 ? i_mod : i_mod + modulus; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/BarabasiAlbertGenerator.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/BarabasiAlbertGenerator.java index 29b8dd71..214edd7b 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/BarabasiAlbertGenerator.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/BarabasiAlbertGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, The JUNG Authors + * Copyright (c) 2003, The JUNG Authors * * All rights reserved. * @@ -12,66 +12,54 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; - import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableSet; import com.google.common.graph.MutableNetwork; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.util.WeightedChoice; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; /** - *

- * Simple evolving scale-free random graph generator. At each time step, a new - * vertex is created and is connected to existing vertices according to the - * principle of "preferential attachment", whereby vertices with higher degree - * have a higher probability of being selected for attachment. - * - *

- * At a given timestep, the probability p of creating an edge - * between an existing vertex v and the newly added vertex is - * + * Simple evolving scale-free random graph generator. At each time step, a new vertex is created and + * is connected to existing vertices according to the principle of "preferential attachment", + * whereby vertices with higher degree have a higher probability of being selected for attachment. + * + *

At a given timestep, the probability p of creating an edge between an existing + * vertex v and the newly added vertex is + * *

  * p = (degree(v) + 1) / (|E| + |V|);
  * 
- * - *

- * where |E| and |V| are, respectively, the number of - * edges and vertices currently in the network (counting neither the new vertex - * nor the other edges that are being attached to it). - * - *

- * Note that the formula specified in the original paper (cited below) was - * + * + *

where |E| and |V| are, respectively, the number of edges and + * vertices currently in the network (counting neither the new vertex nor the other edges that are + * being attached to it). + * + *

Note that the formula specified in the original paper (cited below) was + * *

  * p = degree(v) / |E|
  * 
- * - * - *

- * However, this would have meant that the probability of attachment for any - * existing isolated vertex would be 0. This version uses Lagrangian smoothing - * to give each existing vertex a positive attachment probability. - * - *

- * The graph created may be either directed or undirected (controlled by a - * constructor parameter); the default is undirected. If the graph is specified - * to be directed, then the edges added will be directed from the newly added - * vertex u to the existing vertex v, with probability proportional to the - * indegree of v (number of edges directed towards v). If the graph is specified - * to be undirected, then the (undirected) edges added will connect u to v, with - * probability proportional to the degree of v. - * - *

- * The parallel constructor parameter specifies whether parallel - * edges may be created. - * + * + *

However, this would have meant that the probability of attachment for any existing isolated + * vertex would be 0. This version uses Lagrangian smoothing to give each existing vertex a positive + * attachment probability. + * + *

The graph created may be either directed or undirected (controlled by a constructor + * parameter); the default is undirected. If the graph is specified to be directed, then the edges + * added will be directed from the newly added vertex u to the existing vertex v, with probability + * proportional to the indegree of v (number of edges directed towards v). If the graph is specified + * to be undirected, then the (undirected) edges added will connect u to v, with probability + * proportional to the degree of v. + * + *

The parallel constructor parameter specifies whether parallel edges may be + * created. + * * @see "A.-L. Barabasi and R. Albert, Emergence of scaling in random networks, Science 286, 1999." * @author Scott White * @author Joshua O'Madadhain @@ -80,150 +68,157 @@ */ // TODO(jrtom): decide whether EvolvingGraphGenerator is actually necessary for this to extend public class BarabasiAlbertGenerator { - private int mNumEdgesToAttachPerStep; - private int mElapsedTimeSteps; - private Random mRandom; - protected Supplier vertexFactory; - protected Supplier edgeFactory; - protected ImmutableSet seedNodes; - private MutableNetwork graph; - - /** - * Constructs a new instance of the generator. - * - * @param graphBuilder builder for graph instances - * @param vertexFactory factory for vertices of the appropriate type - * @param edgeFactory factory for edges of the appropriate type - * @param init_vertices number of unconnected 'seed' vertices that the graph should start with - * @param numEdgesToAttach the number of edges that should be attached from the - * new vertex to pre-existing vertices at each time step - * @param seed random number seed - */ - // TODO(jrtom): consider using a Builder pattern here - public BarabasiAlbertGenerator(NetworkBuilder graphBuilder, - Supplier vertexFactory, - Supplier edgeFactory, - int init_vertices, int numEdgesToAttach, - int seed) - { - this.vertexFactory = checkNotNull(vertexFactory); - this.edgeFactory = checkNotNull(edgeFactory); - checkArgument(init_vertices > 0, - "Number of initial unconnected 'seed' vertices must be positive"); - checkArgument(numEdgesToAttach > 0, - "Number of edges to attach at each time step must be positive"); - checkArgument(numEdgesToAttach <= init_vertices, - "Number of edges to attach at each time step must be <= the number of initial vertices"); - this.graph = graphBuilder.build(); - - mNumEdgesToAttachPerStep = numEdgesToAttach; - mRandom = new Random(seed); - this.vertexFactory = vertexFactory; - initialize(init_vertices); - } + private int mNumEdgesToAttachPerStep; + private int mElapsedTimeSteps; + private Random mRandom; + protected Supplier vertexFactory; + protected Supplier edgeFactory; + protected ImmutableSet seedNodes; + private MutableNetwork graph; - /** - * Constructs a new instance of the generator, whose output will be an undirected graph, - * and which will use the current time as a seed for the random number generation. - * - * @param vertexFactory factory for vertices of the appropriate type - * @param edgeFactory factory for edges of the appropriate type - * @param init_vertices number of vertices that the graph should start with - * @param numEdgesToAttach the number of edges that should be attached from the - * new vertex to pre-existing vertices at each time step - */ - public BarabasiAlbertGenerator(NetworkBuilder graphBuilder, - Supplier vertexFactory, - Supplier edgeFactory, - int init_vertices, int numEdgesToAttach) { - this(graphBuilder, vertexFactory, edgeFactory, init_vertices, numEdgesToAttach, - (int) System.currentTimeMillis()); - } - - private void initialize(int init_vertices) { - ImmutableSet.Builder seedBuilder = ImmutableSet.builder(); - - for (int i = 0; i < init_vertices; i++) { - N v = vertexFactory.get(); - seedBuilder.add(v); - graph.addNode(v); - } - - seedNodes = seedBuilder.build(); - mElapsedTimeSteps = 0; - } + /** + * Constructs a new instance of the generator. + * + * @param graphBuilder builder for graph instances + * @param vertexFactory factory for vertices of the appropriate type + * @param edgeFactory factory for edges of the appropriate type + * @param init_vertices number of unconnected 'seed' vertices that the graph should start with + * @param numEdgesToAttach the number of edges that should be attached from the new vertex to + * pre-existing vertices at each time step + * @param seed random number seed + */ + // TODO(jrtom): consider using a Builder pattern here + public BarabasiAlbertGenerator( + NetworkBuilder graphBuilder, + Supplier vertexFactory, + Supplier edgeFactory, + int init_vertices, + int numEdgesToAttach, + int seed) { + this.vertexFactory = checkNotNull(vertexFactory); + this.edgeFactory = checkNotNull(edgeFactory); + checkArgument( + init_vertices > 0, "Number of initial unconnected 'seed' vertices must be positive"); + checkArgument( + numEdgesToAttach > 0, "Number of edges to attach at each time step must be positive"); + checkArgument( + numEdgesToAttach <= init_vertices, + "Number of edges to attach at each time step must be <= the number of initial vertices"); + this.graph = graphBuilder.build(); - private WeightedChoice buildNodeProbabilities() { - Map item_weights = new HashMap(); - for (N v : graph.nodes()) { - double degree; - double denominator; - - // Attachment probability is dependent on whether the graph is - // directed or undirected. - if (graph.isDirected()) { - degree = graph.inDegree(v); - denominator = graph.edges().size() + graph.nodes().size(); - } else { - degree = graph.degree(v); - denominator = (2 * graph.edges().size()) + graph.nodes().size(); - } - - double prob = (degree + 1) / denominator; - item_weights.put(v, prob); - } - WeightedChoice nodeProbabilities = new WeightedChoice(item_weights, mRandom); - - return nodeProbabilities; - } + mNumEdgesToAttachPerStep = numEdgesToAttach; + mRandom = new Random(seed); + this.vertexFactory = vertexFactory; + initialize(init_vertices); + } - - private List generateAdjacentNodes(int edgesToAdd) { - Preconditions.checkArgument(edgesToAdd >= 1); - WeightedChoice nodeChooser = buildNodeProbabilities(); - List adjacentNodes = new ArrayList(edgesToAdd); - while (adjacentNodes.size() < edgesToAdd) { - N attach_point = nodeChooser.nextItem(); - - // if parallel edges are not allowed, skip this node if already present - if (!graph.allowsParallelEdges() && adjacentNodes.contains(attach_point)) { - continue; - } - - adjacentNodes.add(attach_point); - } - return adjacentNodes; - } + /** + * Constructs a new instance of the generator, whose output will be an undirected graph, and which + * will use the current time as a seed for the random number generation. + * + * @param vertexFactory factory for vertices of the appropriate type + * @param edgeFactory factory for edges of the appropriate type + * @param init_vertices number of vertices that the graph should start with + * @param numEdgesToAttach the number of edges that should be attached from the new vertex to + * pre-existing vertices at each time step + */ + public BarabasiAlbertGenerator( + NetworkBuilder graphBuilder, + Supplier vertexFactory, + Supplier edgeFactory, + int init_vertices, + int numEdgesToAttach) { + this( + graphBuilder, + vertexFactory, + edgeFactory, + init_vertices, + numEdgesToAttach, + (int) System.currentTimeMillis()); + } + + private void initialize(int init_vertices) { + ImmutableSet.Builder seedBuilder = ImmutableSet.builder(); - public void evolveGraph(int numTimeSteps) { - for (int i = 0; i < numTimeSteps; i++) { - N newVertex = vertexFactory.get(); - - // determine the nodes to connect to newVertex before connecting anything, because - // we don't want to bias the degree calculations - // note: because we don't add newVertex to the graph until after identifying the - // adjacent nodes, we don't need to worry about creating a self-loop - List adjacentNodes = generateAdjacentNodes(mNumEdgesToAttachPerStep); - graph.addNode(newVertex); - - for (N node : adjacentNodes) - { - graph.addEdge(newVertex, node, edgeFactory.get()); - } - - mElapsedTimeSteps++; - } + for (int i = 0; i < init_vertices; i++) { + N v = vertexFactory.get(); + seedBuilder.add(v); + graph.addNode(v); } - public int numIterations() { - return mElapsedTimeSteps; + seedNodes = seedBuilder.build(); + mElapsedTimeSteps = 0; + } + + private WeightedChoice buildNodeProbabilities() { + Map item_weights = new HashMap(); + for (N v : graph.nodes()) { + double degree; + double denominator; + + // Attachment probability is dependent on whether the graph is + // directed or undirected. + if (graph.isDirected()) { + degree = graph.inDegree(v); + denominator = graph.edges().size() + graph.nodes().size(); + } else { + degree = graph.degree(v); + denominator = (2 * graph.edges().size()) + graph.nodes().size(); + } + + double prob = (degree + 1) / denominator; + item_weights.put(v, prob); } + WeightedChoice nodeProbabilities = new WeightedChoice(item_weights, mRandom); - public MutableNetwork get() { - return graph; + return nodeProbabilities; + } + + private List generateAdjacentNodes(int edgesToAdd) { + Preconditions.checkArgument(edgesToAdd >= 1); + WeightedChoice nodeChooser = buildNodeProbabilities(); + List adjacentNodes = new ArrayList(edgesToAdd); + while (adjacentNodes.size() < edgesToAdd) { + N attach_point = nodeChooser.nextItem(); + + // if parallel edges are not allowed, skip this node if already present + if (!graph.allowsParallelEdges() && adjacentNodes.contains(attach_point)) { + continue; + } + + adjacentNodes.add(attach_point); } - - public ImmutableSet seedNodes() { - return seedNodes; + return adjacentNodes; + } + + public void evolveGraph(int numTimeSteps) { + for (int i = 0; i < numTimeSteps; i++) { + N newVertex = vertexFactory.get(); + + // determine the nodes to connect to newVertex before connecting anything, because + // we don't want to bias the degree calculations + // note: because we don't add newVertex to the graph until after identifying the + // adjacent nodes, we don't need to worry about creating a self-loop + List adjacentNodes = generateAdjacentNodes(mNumEdgesToAttachPerStep); + graph.addNode(newVertex); + + for (N node : adjacentNodes) { + graph.addEdge(newVertex, node, edgeFactory.get()); + } + + mElapsedTimeSteps++; } + } + + public int numIterations() { + return mElapsedTimeSteps; + } + + public MutableNetwork get() { + return graph; + } + + public ImmutableSet seedNodes() { + return seedNodes; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/EppsteinPowerLawGenerator.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/EppsteinPowerLawGenerator.java index d700c837..13e54ba0 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/EppsteinPowerLawGenerator.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/EppsteinPowerLawGenerator.java @@ -1,131 +1,134 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.generators.random; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; -import java.util.Set; - import com.google.common.base.Supplier; import com.google.common.graph.Graph; import com.google.common.graph.GraphBuilder; import com.google.common.graph.MutableGraph; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.Set; /** * Graph generator that generates undirected graphs with power-law degree distributions. + * * @author Joshua O'Madadhain * @author Scott White * @see "A Steady State Model for Graph Power Law by David Eppstein and Joseph Wang" */ public class EppsteinPowerLawGenerator { - private int nodeCount; - private int edgeCount; - private int mNumIterations; - private double mMaxDegree; - private Random mRandom; - private Supplier vertexFactory; - private List nodes; + private int nodeCount; + private int edgeCount; + private int mNumIterations; + private double mMaxDegree; + private Random mRandom; + private Supplier vertexFactory; + private List nodes; - /** - * Creates an instance with the specified factories and specifications. - * @param vertexFactory the Supplier to use to create vertices - * @param numVertices the number of vertices for the generated graph - * @param numEdges the number of edges the generated graph will have, should be Theta(numVertices) - * @param r the number of iterations to use; the larger the value the better the graph's degree - * distribution will approximate a power-law - */ - public EppsteinPowerLawGenerator(Supplier vertexFactory, - int numVertices, int numEdges, int r) { - this.vertexFactory = vertexFactory; - nodeCount = numVertices; - edgeCount = numEdges; - mNumIterations = r; - mRandom = new Random(); - } + /** + * Creates an instance with the specified factories and specifications. + * + * @param vertexFactory the Supplier to use to create vertices + * @param numVertices the number of vertices for the generated graph + * @param numEdges the number of edges the generated graph will have, should be Theta(numVertices) + * @param r the number of iterations to use; the larger the value the better the graph's degree + * distribution will approximate a power-law + */ + public EppsteinPowerLawGenerator( + Supplier vertexFactory, int numVertices, int numEdges, int r) { + this.vertexFactory = vertexFactory; + nodeCount = numVertices; + edgeCount = numEdges; + mNumIterations = r; + mRandom = new Random(); + } - protected MutableGraph initializeGraph() { - MutableGraph graph = GraphBuilder.undirected().build(); - nodes = new ArrayList(nodeCount); - for(int i=0; i initializeGraph() { + MutableGraph graph = GraphBuilder.undirected().build(); + nodes = new ArrayList(nodeCount); + for (int i = 0; i < nodeCount; i++) { + V node = vertexFactory.get(); + graph.addNode(node); + nodes.add(node); + } + while (graph.edges().size() < edgeCount) { + V u = nodes.get((int) (mRandom.nextDouble() * nodeCount)); + V v = nodes.get((int) (mRandom.nextDouble() * nodeCount)); + if (!u.equals(v)) { // no self-loops + graph.putEdge(u, v); + } + } - return graph; + double maxDegree = 0; + for (V v : graph.nodes()) { + maxDegree = Math.max(graph.degree(v), maxDegree); } + mMaxDegree = maxDegree; - /** - * Generates a graph whose degree distribution approximates a power-law. - * @return the generated graph - */ - public Graph get() { - MutableGraph graph = initializeGraph(); + return graph; + } - for (int rIdx = 0; rIdx < mNumIterations; rIdx++) { + /** + * Generates a graph whose degree distribution approximates a power-law. + * + * @return the generated graph + */ + public Graph get() { + MutableGraph graph = initializeGraph(); - V v = null; - do { - v = nodes.get((int) (mRandom.nextDouble() * nodeCount)); - } while (graph.degree(v) == 0); + for (int rIdx = 0; rIdx < mNumIterations; rIdx++) { - Set neighbors = graph.adjacentNodes(v); - int neighborIndex = (int) (mRandom.nextDouble() * neighbors.size()); - int i = 0; - V w = null; - for (V neighbor : graph.adjacentNodes(v)) { - if (i++ == neighborIndex) { - w = neighbor; - break; - } - } + V v = null; + do { + v = nodes.get((int) (mRandom.nextDouble() * nodeCount)); + } while (graph.degree(v) == 0); - // FIXME: use WeightedChoice (see BarabasiAlbert) for a more efficient impl - // for finding an edge - - V x = nodes.get((int) (mRandom.nextDouble() * nodeCount)); - V y = null; - do { - y = nodes.get((int) (mRandom.nextDouble() * nodeCount)); - } while (mRandom.nextDouble() > ((graph.degree(y)+1)/mMaxDegree)); - - // TODO: figure out why we sometimes have insufficient edges in the graph - // if we make the two clauses below part of the while condition above - if (!x.equals(y) && !graph.successors(x).contains(y)) { - graph.removeEdge(v, w); - graph.putEdge(x, y); - } + Set neighbors = graph.adjacentNodes(v); + int neighborIndex = (int) (mRandom.nextDouble() * neighbors.size()); + int i = 0; + V w = null; + for (V neighbor : graph.adjacentNodes(v)) { + if (i++ == neighborIndex) { + w = neighbor; + break; } + } - return graph; - } + // FIXME: use WeightedChoice (see BarabasiAlbert) for a more efficient impl + // for finding an edge + + V x = nodes.get((int) (mRandom.nextDouble() * nodeCount)); + V y = null; + do { + y = nodes.get((int) (mRandom.nextDouble() * nodeCount)); + } while (mRandom.nextDouble() > ((graph.degree(y) + 1) / mMaxDegree)); - /** - * Sets the seed for the random number generator. - * @param seed input to the random number generator. - */ - public void setSeed(long seed) { - mRandom.setSeed(seed); + // TODO: figure out why we sometimes have insufficient edges in the graph + // if we make the two clauses below part of the while condition above + if (!x.equals(y) && !graph.successors(x).contains(y)) { + graph.removeEdge(v, w); + graph.putEdge(x, y); + } } + + return graph; + } + + /** + * Sets the seed for the random number generator. + * + * @param seed input to the random number generator. + */ + public void setSeed(long seed) { + mRandom.setSeed(seed); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/ErdosRenyiGenerator.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/ErdosRenyiGenerator.java index eb4e956a..c42a9165 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/ErdosRenyiGenerator.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/ErdosRenyiGenerator.java @@ -1,87 +1,81 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.generators.random; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - import com.google.common.base.Supplier; import com.google.common.graph.Graph; import com.google.common.graph.GraphBuilder; import com.google.common.graph.MutableGraph; - +import java.util.ArrayList; +import java.util.List; +import java.util.Random; /** - * Generates a random graph using the Erdos-Renyi binomial model - * (each pair of vertices is connected with probability p). - * - * @author William Giordano, Scott White, Joshua O'Madadhain + * Generates a random graph using the Erdos-Renyi binomial model (each pair of vertices is connected + * with probability p). + * + * @author William Giordano, Scott White, Joshua O'Madadhain */ public class ErdosRenyiGenerator { - private int nodeCount; - private double edgeConnectionProbability; - private Random random; - Supplier nodeSupplier; - - /** - * - * @param nodeSupplier factory for vertices of the appropriate type - * @param nodeCount number of vertices graph should have - * @param p Connection's probability between 2 vertices - */ - public ErdosRenyiGenerator(Supplier nodeSupplier, int nodeCount, double p) - { - this.nodeSupplier = checkNotNull(nodeSupplier); - checkArgument(nodeCount > 0, "Number of vertices must be positive"); - checkArgument(p >= 0 && p <= 1, "Probability of connection must be in [0, 1]"); - this.nodeCount = nodeCount; - edgeConnectionProbability = p; - random = new Random(); - } + private int nodeCount; + private double edgeConnectionProbability; + private Random random; + Supplier nodeSupplier; - /** - * Returns a graph in which each pair of vertices is connected by - * an undirected edge with the probability specified by the constructor. - */ - public Graph get() { - MutableGraph graph = GraphBuilder.undirected() - .expectedNodeCount(nodeCount) - .build(); - for(int i=0; i list = new ArrayList(graph.nodes()); + /** + * @param nodeSupplier factory for vertices of the appropriate type + * @param nodeCount number of vertices graph should have + * @param p Connection's probability between 2 vertices + */ + public ErdosRenyiGenerator(Supplier nodeSupplier, int nodeCount, double p) { + this.nodeSupplier = checkNotNull(nodeSupplier); + checkArgument(nodeCount > 0, "Number of vertices must be positive"); + checkArgument(p >= 0 && p <= 1, "Probability of connection must be in [0, 1]"); + this.nodeCount = nodeCount; + edgeConnectionProbability = p; + random = new Random(); + } - for (int i = 0; i < nodeCount-1; i++) { - N v_i = list.get(i); - for (int j = i+1; j < nodeCount; j++) { - N v_j = list.get(j); - if (random.nextDouble() < edgeConnectionProbability) { - graph.putEdge(v_i, v_j); - } - } - } - return graph; + /** + * Returns a graph in which each pair of vertices is connected by an undirected edge with the + * probability specified by the constructor. + */ + public Graph get() { + MutableGraph graph = GraphBuilder.undirected().expectedNodeCount(nodeCount).build(); + for (int i = 0; i < nodeCount; i++) { + graph.addNode(nodeSupplier.get()); } + List list = new ArrayList(graph.nodes()); - /** - * Sets the seed of the internal random number generator to {@code seed}. - * Enables consistent behavior. - * - * @param seed the seed to use for the internal random number generator - */ - public void setSeed(long seed) { - random.setSeed(seed); + for (int i = 0; i < nodeCount - 1; i++) { + N v_i = list.get(i); + for (int j = i + 1; j < nodeCount; j++) { + N v_j = list.get(j); + if (random.nextDouble() < edgeConnectionProbability) { + graph.putEdge(v_i, v_j); + } + } } + return graph; + } + + /** + * Sets the seed of the internal random number generator to {@code seed}. Enables consistent + * behavior. + * + * @param seed the seed to use for the internal random number generator + */ + public void setSeed(long seed) { + random.setSeed(seed); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/KleinbergSmallWorld.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/KleinbergSmallWorld.java index 71373c51..b9277d2e 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/KleinbergSmallWorld.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/generators/random/KleinbergSmallWorld.java @@ -2,165 +2,157 @@ package edu.uci.ics.jung.algorithms.generators.random; /* -* Copyright (c) 2009, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Optional; -import java.util.Random; -import java.util.Set; + * Copyright (c) 2009, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.graph.Graph; import com.google.common.graph.MutableNetwork; - import edu.uci.ics.jung.algorithms.shortestpath.Distance; import edu.uci.ics.jung.algorithms.util.WeightedChoice; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Random; +import java.util.Set; /** - * Graph generator that adds edges to an existing graph so as to give it small world properties. + * Graph generator that adds edges to an existing graph so as to give it small world properties. * To create a small-world graph based on a 2D lattice, use {@code Lattice2DGenerator}: - * - *

{@code 
+ *
+ * 
{@code
  *   Lattice2DGenerator latticeGenerator = new Lattice2DGenerator(rowCount, colCount, false);
  *   MutableNetwork network = latticeGenerator.generateNetwork(true, nodeSupplier, edgeSupplier);
  *   KleinbergSmallWorldGenerator generator =
  *       KleinbergSmallWorldGenerator.builder().connectionCount(3).build();
  *   generator.addSmallWorldConnections(network, latticeGenerator.distance(network.asGraph()), edgeSupplier);
  * 
- * - * The underlying model is an mxn (optionally toroidal) lattice. Each node u + * + * The underlying model is an mxn (optionally toroidal) lattice. Each node u * has four local connections, one to each of its neighbors, and * in addition 1+ long range connections to some node v where v is chosen randomly according to * probability proportional to d^-alpha where d is the lattice distance between u and v and alpha * is the clustering exponent. - * + * * @see "Navigation in a small world J. Kleinberg, Nature 406(2000), 845." * @author Joshua O'Madadhain */ -// TODO: +// TODO: // * can we make this cleaner/clearer? // * consider renaming this class; it's no longer a "generator" public class KleinbergSmallWorld { - private final double clusteringExponent; - private final Random random; - private final int connectionCount; - - private KleinbergSmallWorld(Builder builder) { - this.clusteringExponent = builder.exponent; - this.random = builder.random; - this.connectionCount = builder.numConnections; + private final double clusteringExponent; + private final Random random; + private final int connectionCount; + + private KleinbergSmallWorld(Builder builder) { + this.clusteringExponent = builder.exponent; + this.random = builder.random; + this.connectionCount = builder.numConnections; + } + + public static Builder builder() { + return new Builder<>(); + } + + public static class Builder { + private double exponent = 2.0; + private int numConnections = 1; + private Optional seed = Optional.empty(); + private Random random = new Random(0); + + private Builder() {} + + /** Specifies the clustering exponent. Defaults to 2. */ + public Builder clusteringExponent(double exponent) { + this.exponent = exponent; + return this; } - - public static Builder builder() { - return new Builder<>(); + + /** Specifies the random seed. No default value specified. */ + public Builder randomSeed(long seed) { + this.random.setSeed(seed); + return this; } - - public static class Builder { - private double exponent = 2.0; - private int numConnections = 1; - private Optional seed = Optional.empty(); - private Random random = new Random(0); - - private Builder() {} - - /** - * Specifies the clustering exponent. Defaults to 2. - */ - public Builder clusteringExponent(double exponent) { - this.exponent = exponent; - return this; - } - - /** - * Specifies the random seed. No default value specified. - */ - public Builder randomSeed(long seed) { - this.random.setSeed(seed); - return this; - } - - /** - * Specifies the random number generator to use. Defaults to {@code new Random()}. - */ - public Builder random(Random random) { - this.random = Preconditions.checkNotNull(random); - if (!seed.isPresent()) { - seed = Optional.of(0L); - } - this.random.setSeed(seed.get()); - return this; - } - - /** - * Specifies the number of connections to add to each node. Defaults to 1. - */ - public Builder connectionCount(int count) { - this.numConnections = count; - return this; - } - - public KleinbergSmallWorld build() { - return new KleinbergSmallWorld(this); - } + + /** Specifies the random number generator to use. Defaults to {@code new Random()}. */ + public Builder random(Random random) { + this.random = Preconditions.checkNotNull(random); + if (!seed.isPresent()) { + seed = Optional.of(0L); + } + this.random.setSeed(seed.get()); + return this; } - - public void addSmallWorldConnections(MutableNetwork graph, Distance distance, - Supplier edgeFactory) { - // verify that it's actually possible to give each node 'connectionCount' new incident edges - // without creating parallel edges or self-loops (both are disallowed) - Preconditions.checkArgument(graph.nodes().size() - 5 >= connectionCount); - - // TODO: For toroidal graphs, we can make this more clever by pre-creating the WeightedChoice object - // and using the output as an offset to the current vertex location. - - for (N node : graph.nodes()) { - // TODO: come up with a better random selection mechanism. - // in this case we want selection without replacement, which is not what WeightedChoice does; - // otherwise we can keep selecting the same target over and over again, which is inefficient. - WeightedChoice weightedChoice = getWeightedChoiceForDistance(node, graph.asGraph(), distance); - - Set targets = new HashSet<>(); - while (targets.size() < connectionCount) { - // the item returned is guaranteed by getWeightedChoiceForDistance() to not be equal to node - // or any of its successors; we may try to add the same node to targets more than once - // (see the note above re: selection w/o replacement) but the Set semantics disallows duplicates - targets.add(weightedChoice.nextItem()); - } - - for (N target : targets) { - graph.addEdge(node, target, edgeFactory.get()); - } - } + + /** Specifies the number of connections to add to each node. Defaults to 1. */ + public Builder connectionCount(int count) { + this.numConnections = count; + return this; } - - private WeightedChoice getWeightedChoiceForDistance( - N source, Graph graph, Distance distance) { - Map nodeWeights = new HashMap<>(); - Set successors = graph.successors(source); - - for (N node : graph.nodes()) { - // don't include the source or its successors - if (!node.equals(source) && !successors.contains(node)) { - nodeWeights.put(node, - Math.pow(distance.getDistance(source, node).doubleValue(), - -clusteringExponent)); - } - } - Preconditions.checkState(nodeWeights.size() >= connectionCount, - "number of possible targets (%s) must be greater than connection count (%s)", - nodeWeights.size(), connectionCount); - WeightedChoice weightedChoice = new WeightedChoice<>(nodeWeights, random); - - return weightedChoice; + + public KleinbergSmallWorld build() { + return new KleinbergSmallWorld(this); } + } + + public void addSmallWorldConnections( + MutableNetwork graph, Distance distance, Supplier edgeFactory) { + // verify that it's actually possible to give each node 'connectionCount' new incident edges + // without creating parallel edges or self-loops (both are disallowed) + Preconditions.checkArgument(graph.nodes().size() - 5 >= connectionCount); + + // TODO: For toroidal graphs, we can make this more clever by pre-creating the WeightedChoice object + // and using the output as an offset to the current vertex location. + + for (N node : graph.nodes()) { + // TODO: come up with a better random selection mechanism. + // in this case we want selection without replacement, which is not what WeightedChoice does; + // otherwise we can keep selecting the same target over and over again, which is inefficient. + WeightedChoice weightedChoice = + getWeightedChoiceForDistance(node, graph.asGraph(), distance); + + Set targets = new HashSet<>(); + while (targets.size() < connectionCount) { + // the item returned is guaranteed by getWeightedChoiceForDistance() to not be equal to node + // or any of its successors; we may try to add the same node to targets more than once + // (see the note above re: selection w/o replacement) but the Set semantics disallows duplicates + targets.add(weightedChoice.nextItem()); + } + + for (N target : targets) { + graph.addEdge(node, target, edgeFactory.get()); + } + } + } + + private WeightedChoice getWeightedChoiceForDistance( + N source, Graph graph, Distance distance) { + Map nodeWeights = new HashMap<>(); + Set successors = graph.successors(source); + + for (N node : graph.nodes()) { + // don't include the source or its successors + if (!node.equals(source) && !successors.contains(node)) { + nodeWeights.put( + node, Math.pow(distance.getDistance(source, node).doubleValue(), -clusteringExponent)); + } + } + Preconditions.checkState( + nodeWeights.size() >= connectionCount, + "number of possible targets (%s) must be greater than connection count (%s)", + nodeWeights.size(), + connectionCount); + WeightedChoice weightedChoice = new WeightedChoice<>(nodeWeights, random); + + return weightedChoice; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/AbstractLayout.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/AbstractLayout.java index 952673ef..7c054b82 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/AbstractLayout.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/AbstractLayout.java @@ -1,21 +1,15 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * * Created on Jul 7, 2003 - * + * */ package edu.uci.ics.jung.algorithms.layout; -import java.awt.Dimension; -import java.awt.geom.Point2D; -import java.util.ConcurrentModificationException; -import java.util.HashSet; -import java.util.Set; - import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Preconditions; @@ -23,246 +17,236 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.graph.Graph; +import java.awt.Dimension; +import java.awt.geom.Point2D; +import java.util.ConcurrentModificationException; +import java.util.HashSet; +import java.util.Set; /** - * Abstract class for implementations of {@code Layout}. It handles some of the - * basic functions: storing coordinates, maintaining the dimensions, initializing - * the locations, maintaining locked nodes. - * + * Abstract class for implementations of {@code Layout}. It handles some of the basic functions: + * storing coordinates, maintaining the dimensions, initializing the locations, maintaining locked + * nodes. + * * @author Danyel Fisher, Scott White * @author Tom Nelson - converted to jung2 * @param the node type * @param the edge type */ -abstract public class AbstractLayout implements Layout { +public abstract class AbstractLayout implements Layout { + + /** A set of nodes that are fixed in place and not affected by the layout algorithm */ + private Set dontmove = new HashSet(); + + protected Dimension size; + protected Set nodes; + protected boolean initialized; + + protected LoadingCache locations = + CacheBuilder.newBuilder() + .build( + new CacheLoader() { + public Point2D load(N node) { + return new Point2D.Double(); + } + }); - /** - * A set of nodes that are fixed in place and not affected by the layout algorithm - */ - private Set dontmove = new HashSet(); + /** + * Creates an instance for {@code graph} which does not initialize the node locations. + * + * @param graph the graph on which the layout algorithm is to operate + */ + protected AbstractLayout(Graph graph) { + Preconditions.checkNotNull(graph); + this.nodes = graph.nodes(); + } - protected Dimension size; - protected Set nodes; - protected boolean initialized; + /** + * Creates an instance for {@code graph} which initializes the node locations using {@code + * initializer}. + * + * @param graph the graph on which the layout algorithm is to operate + * @param initializer specifies the starting positions of the nodes + */ + protected AbstractLayout(Graph graph, Function initializer) { + this.nodes = graph.nodes(); + Function chain = + Functions.compose(p -> (Point2D) p.clone(), initializer); + this.locations = CacheBuilder.newBuilder().build(CacheLoader.from(chain)); + initialized = true; + } - protected LoadingCache locations = - CacheBuilder.newBuilder().build(new CacheLoader() { - public Point2D load(N node) { - return new Point2D.Double(); - } - }); + /** + * Creates an instance for {@code graph} which sets the size of the layout to {@code size}. + * + * @param graph the graph on which the layout algorithm is to operate + * @param size the dimensions of the region in which the layout algorithm will place nodes + */ + protected AbstractLayout(Graph graph, Dimension size) { + this.nodes = graph.nodes(); + this.size = size; + } - /** - * Creates an instance for {@code graph} which does not initialize the node locations. - * - * @param graph the graph on which the layout algorithm is to operate - */ - protected AbstractLayout(Graph graph) { - Preconditions.checkNotNull(graph); - this.nodes = graph.nodes(); - } - - /** - * Creates an instance for {@code graph} which initializes the node locations - * using {@code initializer}. - * - * @param graph the graph on which the layout algorithm is to operate - * @param initializer specifies the starting positions of the nodes - */ - protected AbstractLayout(Graph graph, Function initializer) { - this.nodes = graph.nodes(); - Function chain = - Functions.compose( - p -> (Point2D)p.clone(), - initializer); - this.locations = CacheBuilder.newBuilder().build(CacheLoader.from(chain)); - initialized = true; - } - - /** - * Creates an instance for {@code graph} which sets the size of the layout to {@code size}. - * - * @param graph the graph on which the layout algorithm is to operate - * @param size the dimensions of the region in which the layout algorithm will place nodes - */ - protected AbstractLayout(Graph graph, Dimension size) { - this.nodes = graph.nodes(); - this.size = size; - } - - /** - * Creates an instance for {@code graph} which initializes the node locations - * using {@code initializer} and sets the size of the layout to {@code size}. - * - * @param graph the graph on which the layout algorithm is to operate - * @param initializer specifies the starting positions of the nodes - * @param size the dimensions of the region in which the layout algorithm will place nodes - */ - protected AbstractLayout(Graph graph, Function initializer, Dimension size) { - this.nodes = graph.nodes(); - Function chain = - Functions.compose( - p -> (Point2D)p.clone(), - initializer); - this.locations = CacheBuilder.newBuilder().build(CacheLoader.from(chain)); - this.size = size; - } - - /** - * When a visualization is resized, it presumably wants to fix the - * locations of the nodes and possibly to reinitialize its data. The - * current method calls initializeLocations followed by initialize_local. - */ - public void setSize(Dimension size) { - Preconditions.checkNotNull(size); - Dimension oldSize = this.size; - this.size = size; - initialize(); - - if(oldSize != null) { - adjustLocations(oldSize, size); - } - } - - private void adjustLocations(Dimension oldSize, Dimension size) { + /** + * Creates an instance for {@code graph} which initializes the node locations using {@code + * initializer} and sets the size of the layout to {@code size}. + * + * @param graph the graph on which the layout algorithm is to operate + * @param initializer specifies the starting positions of the nodes + * @param size the dimensions of the region in which the layout algorithm will place nodes + */ + protected AbstractLayout(Graph graph, Function initializer, Dimension size) { + this.nodes = graph.nodes(); + Function chain = + Functions.compose(p -> (Point2D) p.clone(), initializer); + this.locations = CacheBuilder.newBuilder().build(CacheLoader.from(chain)); + this.size = size; + } - int xOffset = (size.width - oldSize.width) / 2; - int yOffset = (size.height - oldSize.height) / 2; + /** + * When a visualization is resized, it presumably wants to fix the locations of the nodes and + * possibly to reinitialize its data. The current method calls initializeLocations + * followed by initialize_local. + */ + public void setSize(Dimension size) { + Preconditions.checkNotNull(size); + Dimension oldSize = this.size; + this.size = size; + initialize(); - // now, move each node to be at the new screen center - while(true) { - try { - for(N node : nodes) { - offsetnode(node, xOffset, yOffset); - } - break; - } catch(ConcurrentModificationException cme) { - } - } - } - - public boolean isLocked(N node) { - return dontmove.contains(node); + if (oldSize != null) { + adjustLocations(oldSize, size); } - - public void setInitializer(Function initializer) { - if(this.equals(initializer)) { - throw new IllegalArgumentException("Layout cannot be initialized with itself"); - } - Function chain = - Functions.compose( - p -> (Point2D)p.clone(), - initializer); - this.locations = CacheBuilder.newBuilder().build(CacheLoader.from(chain)); - initialized = true; + } + + private void adjustLocations(Dimension oldSize, Dimension size) { + + int xOffset = (size.width - oldSize.width) / 2; + int yOffset = (size.height - oldSize.height) / 2; + + // now, move each node to be at the new screen center + while (true) { + try { + for (N node : nodes) { + offsetnode(node, xOffset, yOffset); + } + break; + } catch (ConcurrentModificationException cme) { + } } - - /** - * Returns the current size of the visualization space, accoring to the - * last call to resize(). - * - * @return the current size of the screen - */ - public Dimension getSize() { - return size; - } + } + + public boolean isLocked(N node) { + return dontmove.contains(node); + } + + public void setInitializer(Function initializer) { + if (this.equals(initializer)) { + throw new IllegalArgumentException("Layout cannot be initialized with itself"); + } + Function chain = + Functions.compose(p -> (Point2D) p.clone(), initializer); + this.locations = CacheBuilder.newBuilder().build(CacheLoader.from(chain)); + initialized = true; + } + + /** + * Returns the current size of the visualization space, accoring to the last call to resize(). + * + * @return the current size of the screen + */ + public Dimension getSize() { + return size; + } - /** - * Returns the Coordinates object that stores the node' x and y location. - * - * @param v - * A node that is a part of the Graph being visualized. - * @return A Coordinates object with x and y locations. - */ - private Point2D getCoordinates(N node) { - return locations.getUnchecked(node); - } - - public Point2D apply(N node) { - return getCoordinates(node); - } - - /** - * Returns the x coordinate of the node from the Coordinates object. - * in most cases you will be better off calling transform(node). - * - * @param v the node whose x coordinate is to be returned - * @return the x coordinate of {@code node} - */ - public double getX(N node) { - Preconditions.checkNotNull(getCoordinates(node), - "Cannot getX for an unmapped node %s", node); - return getCoordinates(node).getX(); - } + /** + * Returns the Coordinates object that stores the node' x and y location. + * + * @param v A node that is a part of the Graph being visualized. + * @return A Coordinates object with x and y locations. + */ + private Point2D getCoordinates(N node) { + return locations.getUnchecked(node); + } - /** - * Returns the y coordinate of the node from the Coordinates object. - * In most cases you will be better off calling transform(node). - * - * @param v the node whose y coordinate is to be returned - * @return the y coordinate of {@code node} - */ - public double getY(N node) { - Preconditions.checkNotNull(getCoordinates(node), - "Cannot getY for an unmapped node %s", node); - return getCoordinates(node).getY(); - } - - /** - * @param v the node whose coordinates are to be offset - * @param xOffset the change to apply to this node's x coordinate - * @param yOffset the change to apply to this node's y coordinate - */ - protected void offsetnode(N node, double xOffset, double yOffset) { - Point2D c = getCoordinates(node); - c.setLocation(c.getX()+xOffset, c.getY()+yOffset); - setLocation(node, c); - } + public Point2D apply(N node) { + return getCoordinates(node); + } - /** - * @return the graph that this layout operates on - */ - public Set nodes() { - return nodes; - } - - /** - * Forcibly moves a node to the (x,y) location by setting its x and y - * locations to the specified location. Does not add the node to the - * "dontmove" list, and (in the default implementation) does not make any - * adjustments to the rest of the graph. - * @param picked the node whose location is being set - * @param x the x coordinate of the location to set - * @param y the y coordinate of the location to set - */ - public void setLocation(N picked, double x, double y) { - Point2D coord = getCoordinates(picked); - coord.setLocation(x, y); - } + /** + * Returns the x coordinate of the node from the Coordinates object. in most cases you will be + * better off calling transform(node). + * + * @param v the node whose x coordinate is to be returned + * @return the x coordinate of {@code node} + */ + public double getX(N node) { + Preconditions.checkNotNull(getCoordinates(node), "Cannot getX for an unmapped node %s", node); + return getCoordinates(node).getX(); + } - public void setLocation(N picked, Point2D p) { - Point2D coord = getCoordinates(picked); - coord.setLocation(p); - } + /** + * Returns the y coordinate of the node from the Coordinates object. In most cases you will be + * better off calling transform(node). + * + * @param v the node whose y coordinate is to be returned + * @return the y coordinate of {@code node} + */ + public double getY(N node) { + Preconditions.checkNotNull(getCoordinates(node), "Cannot getY for an unmapped node %s", node); + return getCoordinates(node).getY(); + } - /** - * Locks {@code node} in place if {@code state} is {@code true}, otherwise unlocks it. - * @param v the node whose position is to be (un)locked - * @param state {@code true} if the node is to be locked, {@code false} if to be unlocked - */ - public void lock(N node, boolean state) { - if(state == true) - dontmove.add(node); - else - dontmove.remove(node); - } - - /** - * @param lock {@code true} to lock all nodes in place, {@code false} to unlock all nodes - */ - public void lock(boolean lock) { - for(N node : nodes) { - lock(node, lock); - } - } + /** + * @param v the node whose coordinates are to be offset + * @param xOffset the change to apply to this node's x coordinate + * @param yOffset the change to apply to this node's y coordinate + */ + protected void offsetnode(N node, double xOffset, double yOffset) { + Point2D c = getCoordinates(node); + c.setLocation(c.getX() + xOffset, c.getY() + yOffset); + setLocation(node, c); + } + + /** @return the graph that this layout operates on */ + public Set nodes() { + return nodes; + } + + /** + * Forcibly moves a node to the (x,y) location by setting its x and y locations to the specified + * location. Does not add the node to the "dontmove" list, and (in the default implementation) + * does not make any adjustments to the rest of the graph. + * + * @param picked the node whose location is being set + * @param x the x coordinate of the location to set + * @param y the y coordinate of the location to set + */ + public void setLocation(N picked, double x, double y) { + Point2D coord = getCoordinates(picked); + coord.setLocation(x, y); + } + + public void setLocation(N picked, Point2D p) { + Point2D coord = getCoordinates(picked); + coord.setLocation(p); + } + + /** + * Locks {@code node} in place if {@code state} is {@code true}, otherwise unlocks it. + * + * @param v the node whose position is to be (un)locked + * @param state {@code true} if the node is to be locked, {@code false} if to be unlocked + */ + public void lock(N node, boolean state) { + if (state == true) dontmove.add(node); + else dontmove.remove(node); + } + + /** @param lock {@code true} to lock all nodes in place, {@code false} to unlock all nodes */ + public void lock(boolean lock) { + for (N node : nodes) { + lock(node, lock); + } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/AggregateLayout.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/AggregateLayout.java index f7958955..35bc7d4e 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/AggregateLayout.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/AggregateLayout.java @@ -1,15 +1,17 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * - * - * + * + * + * */ package edu.uci.ics.jung.algorithms.layout; +import com.google.common.base.Function; +import edu.uci.ics.jung.algorithms.util.IterativeContext; import java.awt.Dimension; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; @@ -17,229 +19,213 @@ import java.util.Map; import java.util.Set; -import com.google.common.base.Function; - -import edu.uci.ics.jung.algorithms.util.IterativeContext; - /** - * A {@code Layout} implementation that combines - * multiple other layouts so that they may be manipulated - * as one layout. The relaxer thread will step each layout - * in sequence. - * - * @author Tom Nelson - tomnelson@dev.java.net + * A {@code Layout} implementation that combines multiple other layouts so that they may be + * manipulated as one layout. The relaxer thread will step each layout in sequence. * + * @author Tom Nelson - tomnelson@dev.java.net * @param the node type * @param the edge type */ public class AggregateLayout implements Layout, IterativeContext { - protected Layout delegate; - protected Map,Point2D> layouts = new HashMap,Point2D>(); - - /** - * Creates an instance backed by the specified {@code delegate}. - * @param delegate the layout to which this instance is delegating - */ - public AggregateLayout(Layout delegate) { - this.delegate = delegate; - } - - /** - * @return the delegate - */ - public Layout getDelegate() { - return delegate; - } - - /** - * @param delegate the delegate to set - */ - public void setDelegate(Layout delegate) { - this.delegate = delegate; - } - - /** - * Adds the passed layout as a sublayout, and specifies - * the center of where this sublayout should appear. - * @param layout the layout algorithm to use as a sublayout - * @param center the center of the coordinates for the sublayout - */ - public void put(Layout layout, Point2D center) { - layouts.put(layout,center); - } - - /** - * @param layout the layout whose center is to be returned - * @return the center of the passed layout - */ - public Point2D get(Layout layout) { - return layouts.get(layout); - } - - /** - * Removes {@code layout} from this instance. - * @param layout the layout to remove - */ - public void remove(Layout layout) { - layouts.remove(layout); - } - - /** - * Removes all layouts from this instance. - */ - public void removeAll() { - layouts.clear(); - } - - public Set nodes() { - return delegate.nodes(); - } - - public Dimension getSize() { - return delegate.getSize(); - } - - public void initialize() { - delegate.initialize(); - for(Layout layout : layouts.keySet()) { - layout.initialize(); - } - } - - /** - * @param v the node whose locked state is to be returned - * @return true if v is locked in any of the layouts, and false otherwise - */ - public boolean isLocked(N node) { - for(Layout layout : layouts.keySet()) { - if (layout.isLocked(node)) { - return true; - } - } - return delegate.isLocked(node); - } - - /** - * Locks this node in the main layout and in any sublayouts whose graph contains - * this node. - * @param v the node whose locked state is to be set - * @param state {@code true} if the node is to be locked, and {@code false} if unlocked - */ - public void lock(N node, boolean state) { - for(Layout layout : layouts.keySet()) { - if(layout.nodes().contains(node)) { - layout.lock(node, state); - } - } - delegate.lock(node, state); - } - - public void reset() { - for(Layout layout : layouts.keySet()) { - layout.reset(); - } - delegate.reset(); - } - - public void setInitializer(Function initializer) { - delegate.setInitializer(initializer); - } - - public void setLocation(N node, Point2D location) { - boolean wasInSublayout = false; - for(Layout layout : layouts.keySet()) { - if(layout.nodes().contains(node)) { - Point2D center = layouts.get(layout); - // transform by the layout itself, but offset to the - // center of the sublayout - Dimension d = layout.getSize(); - - AffineTransform at = - AffineTransform.getTranslateInstance(-center.getX()+d.width/2,-center.getY()+d.height/2); - Point2D localLocation = at.transform(location, null); - layout.setLocation(node, localLocation); - wasInSublayout = true; - } - } - if(wasInSublayout == false && nodes().contains(node)) { - delegate.setLocation(node, location); - } - } - - public void setSize(Dimension d) { - delegate.setSize(d); - } - - /** - * @return a map from each {@code Layout} instance to its center point. - */ - public Map,Point2D> getLayouts() { - return layouts; - } - - /** - * Returns the location of the node. The location is specified first - * by the sublayouts, and then by the base layout if no sublayouts operate - * on this node. - * @return the location of the node - */ - public Point2D apply(N node) { - boolean wasInSublayout = false; - for(Layout layout : layouts.keySet()) { - if(layout.nodes().contains(node)) { - wasInSublayout = true; - Point2D center = layouts.get(layout); - // transform by the layout itself, but offset to the - // center of the sublayout - Dimension d = layout.getSize(); - AffineTransform at = - AffineTransform.getTranslateInstance(center.getX()-d.width/2, - center.getY()-d.height/2); - return at.transform(layout.apply(node),null); - } - } - if(wasInSublayout == false) { - return delegate.apply(node); - } - return null; - - } - - /** - * @return {@code true} iff the delegate layout and all sublayouts are done - */ - public boolean done() { - for (Layout layout : layouts.keySet()) { - if (layout instanceof IterativeContext) { - if (! ((IterativeContext) layout).done() ) { - return false; - } - } - } - if(delegate instanceof IterativeContext) { - return ((IterativeContext)delegate).done(); - } - return true; - } - - /** - * Call step on any sublayout that is also an IterativeContext and is not done - */ - public void step() { - for(Layout layout : layouts.keySet()) { - if(layout instanceof IterativeContext) { - IterativeContext context = (IterativeContext)layout; - if(context.done() == false) { - context.step(); - } - } - } - if(delegate instanceof IterativeContext) { - IterativeContext context = (IterativeContext)delegate; - if(context.done() == false) { - context.step(); - } - } - } + protected Layout delegate; + protected Map, Point2D> layouts = new HashMap, Point2D>(); + + /** + * Creates an instance backed by the specified {@code delegate}. + * + * @param delegate the layout to which this instance is delegating + */ + public AggregateLayout(Layout delegate) { + this.delegate = delegate; + } + + /** @return the delegate */ + public Layout getDelegate() { + return delegate; + } + + /** @param delegate the delegate to set */ + public void setDelegate(Layout delegate) { + this.delegate = delegate; + } + + /** + * Adds the passed layout as a sublayout, and specifies the center of where this sublayout should + * appear. + * + * @param layout the layout algorithm to use as a sublayout + * @param center the center of the coordinates for the sublayout + */ + public void put(Layout layout, Point2D center) { + layouts.put(layout, center); + } + + /** + * @param layout the layout whose center is to be returned + * @return the center of the passed layout + */ + public Point2D get(Layout layout) { + return layouts.get(layout); + } + + /** + * Removes {@code layout} from this instance. + * + * @param layout the layout to remove + */ + public void remove(Layout layout) { + layouts.remove(layout); + } + + /** Removes all layouts from this instance. */ + public void removeAll() { + layouts.clear(); + } + + public Set nodes() { + return delegate.nodes(); + } + + public Dimension getSize() { + return delegate.getSize(); + } + + public void initialize() { + delegate.initialize(); + for (Layout layout : layouts.keySet()) { + layout.initialize(); + } + } + + /** + * @param v the node whose locked state is to be returned + * @return true if v is locked in any of the layouts, and false otherwise + */ + public boolean isLocked(N node) { + for (Layout layout : layouts.keySet()) { + if (layout.isLocked(node)) { + return true; + } + } + return delegate.isLocked(node); + } + + /** + * Locks this node in the main layout and in any sublayouts whose graph contains this node. + * + * @param v the node whose locked state is to be set + * @param state {@code true} if the node is to be locked, and {@code false} if unlocked + */ + public void lock(N node, boolean state) { + for (Layout layout : layouts.keySet()) { + if (layout.nodes().contains(node)) { + layout.lock(node, state); + } + } + delegate.lock(node, state); + } + + public void reset() { + for (Layout layout : layouts.keySet()) { + layout.reset(); + } + delegate.reset(); + } + + public void setInitializer(Function initializer) { + delegate.setInitializer(initializer); + } + + public void setLocation(N node, Point2D location) { + boolean wasInSublayout = false; + for (Layout layout : layouts.keySet()) { + if (layout.nodes().contains(node)) { + Point2D center = layouts.get(layout); + // transform by the layout itself, but offset to the + // center of the sublayout + Dimension d = layout.getSize(); + + AffineTransform at = + AffineTransform.getTranslateInstance( + -center.getX() + d.width / 2, -center.getY() + d.height / 2); + Point2D localLocation = at.transform(location, null); + layout.setLocation(node, localLocation); + wasInSublayout = true; + } + } + if (wasInSublayout == false && nodes().contains(node)) { + delegate.setLocation(node, location); + } + } + + public void setSize(Dimension d) { + delegate.setSize(d); + } + + /** @return a map from each {@code Layout} instance to its center point. */ + public Map, Point2D> getLayouts() { + return layouts; + } + + /** + * Returns the location of the node. The location is specified first by the sublayouts, and then + * by the base layout if no sublayouts operate on this node. + * + * @return the location of the node + */ + public Point2D apply(N node) { + boolean wasInSublayout = false; + for (Layout layout : layouts.keySet()) { + if (layout.nodes().contains(node)) { + wasInSublayout = true; + Point2D center = layouts.get(layout); + // transform by the layout itself, but offset to the + // center of the sublayout + Dimension d = layout.getSize(); + AffineTransform at = + AffineTransform.getTranslateInstance( + center.getX() - d.width / 2, center.getY() - d.height / 2); + return at.transform(layout.apply(node), null); + } + } + if (wasInSublayout == false) { + return delegate.apply(node); + } + return null; + } + + /** @return {@code true} iff the delegate layout and all sublayouts are done */ + public boolean done() { + for (Layout layout : layouts.keySet()) { + if (layout instanceof IterativeContext) { + if (!((IterativeContext) layout).done()) { + return false; + } + } + } + if (delegate instanceof IterativeContext) { + return ((IterativeContext) delegate).done(); + } + return true; + } + + /** Call step on any sublayout that is also an IterativeContext and is not done */ + public void step() { + for (Layout layout : layouts.keySet()) { + if (layout instanceof IterativeContext) { + IterativeContext context = (IterativeContext) layout; + if (context.done() == false) { + context.step(); + } + } + } + if (delegate instanceof IterativeContext) { + IterativeContext context = (IterativeContext) delegate; + if (context.done() == false) { + context.step(); + } + } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/BalloonLayout.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/BalloonLayout.java index 03082d1a..f1f8f917 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/BalloonLayout.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/BalloonLayout.java @@ -9,137 +9,133 @@ */ package edu.uci.ics.jung.algorithms.layout; -import java.awt.Dimension; -import java.awt.geom.Point2D; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Iterables; import com.google.common.graph.Graph; - import edu.uci.ics.jung.graph.util.TreeUtils; +import java.awt.Dimension; +import java.awt.geom.Point2D; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; /** - * A {@code Layout} implementation that assigns positions to {@code Tree} or - * {@code Network} nodes using associations with nested circles ("balloons"). - * A balloon is nested inside another balloon if the first balloon's subtree - * is a subtree of the second balloon's subtree. - * - * @author Tom Nelson - * + * A {@code Layout} implementation that assigns positions to {@code Tree} or {@code Network} nodes + * using associations with nested circles ("balloons"). A balloon is nested inside another balloon + * if the first balloon's subtree is a subtree of the second balloon's subtree. + * + * @author Tom Nelson */ public class BalloonLayout extends TreeLayout { - protected LoadingCache polarLocations - = CacheBuilder.newBuilder().build(new CacheLoader() { - public PolarPoint load(N node) { - return new PolarPoint(); - } - }); - - protected Map radii = new HashMap(); - - /** - * Creates an instance based on the input Network. - * @param g the Network on which this layout will operate - */ - public BalloonLayout(Graph g) - { - super(g); - } - - protected void setRootPolars() - { - Set roots = TreeUtils.roots(graph); - if(roots.size() == 1) { - // its a Tree - N root = Iterables.getOnlyElement(roots); - setRootPolar(root); - setPolars(graph.successors(root), getCenter(), getSize().width/2); - } else if (roots.size() > 1) { - // its a Network - setPolars(roots, getCenter(), getSize().width/2); - } - } - - protected void setRootPolar(N root) { - PolarPoint pp = new PolarPoint(0,0); - Point2D p = getCenter(); - polarLocations.put(root, pp); - locations.put(root, p); + protected LoadingCache polarLocations = + CacheBuilder.newBuilder() + .build( + new CacheLoader() { + public PolarPoint load(N node) { + return new PolarPoint(); + } + }); + + protected Map radii = new HashMap(); + + /** + * Creates an instance based on the input Network. + * + * @param g the Network on which this layout will operate + */ + public BalloonLayout(Graph g) { + super(g); + } + + protected void setRootPolars() { + Set roots = TreeUtils.roots(graph); + if (roots.size() == 1) { + // its a Tree + N root = Iterables.getOnlyElement(roots); + setRootPolar(root); + setPolars(graph.successors(root), getCenter(), getSize().width / 2); + } else if (roots.size() > 1) { + // its a Network + setPolars(roots, getCenter(), getSize().width / 2); } - - - protected void setPolars(Set kids, Point2D parentLocation, double parentRadius) { - - int childCount = kids.size(); - if(childCount == 0) return; - // handle the 1-child case with 0 limit on angle. - double angle = Math.max(0, Math.PI / 2 * (1 - 2.0/childCount)); - double childRadius = parentRadius*Math.cos(angle) / (1 + Math.cos(angle)); - double radius = parentRadius - childRadius; - - double rand = Math.random(); - - int i = 0; - for (N child : kids) { - double theta = i++ * 2 * Math.PI/childCount + rand; - radii.put(child, childRadius); - - PolarPoint pp = new PolarPoint(theta, radius); - polarLocations.put(child, pp); - - Point2D p = PolarPoint.polarToCartesian(pp); - p.setLocation(p.getX()+parentLocation.getX(), p.getY()+parentLocation.getY()); - locations.put(child, p); - - setPolars(graph.successors(child), p, childRadius); - } + } + + protected void setRootPolar(N root) { + PolarPoint pp = new PolarPoint(0, 0); + Point2D p = getCenter(); + polarLocations.put(root, pp); + locations.put(root, p); + } + + protected void setPolars(Set kids, Point2D parentLocation, double parentRadius) { + + int childCount = kids.size(); + if (childCount == 0) return; + // handle the 1-child case with 0 limit on angle. + double angle = Math.max(0, Math.PI / 2 * (1 - 2.0 / childCount)); + double childRadius = parentRadius * Math.cos(angle) / (1 + Math.cos(angle)); + double radius = parentRadius - childRadius; + + double rand = Math.random(); + + int i = 0; + for (N child : kids) { + double theta = i++ * 2 * Math.PI / childCount + rand; + radii.put(child, childRadius); + + PolarPoint pp = new PolarPoint(theta, radius); + polarLocations.put(child, pp); + + Point2D p = PolarPoint.polarToCartesian(pp); + p.setLocation(p.getX() + parentLocation.getX(), p.getY() + parentLocation.getY()); + locations.put(child, p); + + setPolars(graph.successors(child), p, childRadius); } + } + + @Override + public void setSize(Dimension size) { + this.size = size; + setRootPolars(); + } - @Override - public void setSize(Dimension size) { - this.size = size; - setRootPolars(); + /** + * @param v the node whose center is to be returned + * @return the coordinates of {@code node}'s parent, or the center of this layout's area if it's a + * root. + */ + public Point2D getCenter(N node) { + N parent = Iterables.getOnlyElement(graph.predecessors(node), null); + if (parent == null) { + return getCenter(); } + return locations.getUnchecked(parent); + } + + @Override + public void setLocation(N node, Point2D location) { + Point2D c = getCenter(node); + Point2D pv = new Point2D.Double(location.getX() - c.getX(), location.getY() - c.getY()); + PolarPoint newLocation = PolarPoint.cartesianToPolar(pv); + polarLocations.getUnchecked(node).setLocation(newLocation); + + Point2D center = getCenter(node); + pv.setLocation(pv.getX() + center.getX(), pv.getY() + center.getY()); + locations.put(node, pv); + } + + @Override + public Point2D apply(N node) { + return locations.getUnchecked(node); + } - /** - * @param v the node whose center is to be returned - * @return the coordinates of {@code node}'s parent, or the center of this layout's area if it's a root. - */ - public Point2D getCenter(N node) { - N parent = Iterables.getOnlyElement(graph.predecessors(node), null); - if (parent == null) { - return getCenter(); - } - return locations.getUnchecked(parent); - } - - @Override - public void setLocation(N node, Point2D location) { - Point2D c = getCenter(node); - Point2D pv = new Point2D.Double(location.getX()-c.getX(),location.getY()-c.getY()); - PolarPoint newLocation = PolarPoint.cartesianToPolar(pv); - polarLocations.getUnchecked(node).setLocation(newLocation); - - Point2D center = getCenter(node); - pv.setLocation(pv.getX()+center.getX(), pv.getY()+center.getY()); - locations.put(node, pv); - } - - @Override - public Point2D apply(N node) { - return locations.getUnchecked(node); - } - - /** - * @return the radii - */ - public Map getRadii() { - return radii; - } + /** @return the radii */ + public Map getRadii() { + return radii; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/CircleLayout.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/CircleLayout.java index c4b96ea6..5ce66380 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/CircleLayout.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/CircleLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, The JUNG Authors + * Copyright (c) 2003, The JUNG Authors * * All rights reserved. * @@ -12,6 +12,10 @@ */ package edu.uci.ics.jung.algorithms.layout; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.graph.Graph; import java.awt.Dimension; import java.awt.geom.Point2D; import java.util.ArrayList; @@ -19,13 +23,6 @@ import java.util.Comparator; import java.util.List; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.graph.Graph; - - - /** * A {@code Layout} implementation that positions nodes equally spaced on a regular circle. * @@ -33,115 +30,109 @@ */ public class CircleLayout extends AbstractLayout { - private double radius; - private List node_ordered_list; - - protected LoadingCache circleNodeDatas = - CacheBuilder.newBuilder().build(new CacheLoader() { - public CircleNodeData load(N node) { - return new CircleNodeData(); - } - }); - - public CircleLayout(Graph g) { - super(g); - } - - /** - * @return the radius of the circle. - */ - public double getRadius() { - return radius; - } - - /** - * Sets the radius of the circle. Must be called before {@code initialize()} is called. - * @param radius the radius of the circle - */ - public void setRadius(double radius) { - this.radius = radius; - } - - /** - * Sets the order of the nodes in the layout according to the ordering - * specified by {@code comparator}. - * @param comparator the comparator to use to order the nodes - */ - public void setNodeOrder(Comparator comparator) - { - if (node_ordered_list == null) - node_ordered_list = new ArrayList(nodes()); - Collections.sort(node_ordered_list, comparator); - } - - /** - * Sets the order of the nodes in the layout according to the ordering - * of {@code node_list}. - * @param node_list a list specifying the ordering of the nodes - */ - public void setNodeOrder(List node_list) - { - if (!node_list.containsAll(nodes())) - throw new IllegalArgumentException("Supplied list must include " + - "all nodes of the graph"); - this.node_ordered_list = node_list; - } - - public void reset() { - initialize(); - } - - public void initialize() - { - Dimension d = getSize(); - - if (d != null) - { - if (node_ordered_list == null) - setNodeOrder(new ArrayList(nodes())); - - double height = d.getHeight(); - double width = d.getWidth(); - - if (radius <= 0) { - radius = 0.45 * (height < width ? height : width); - } - - int i = 0; - for (N node : node_ordered_list) - { - Point2D coord = apply(node); - - double angle = (2 * Math.PI * i) / node_ordered_list.size(); - - coord.setLocation(Math.cos(angle) * radius + width / 2, - Math.sin(angle) * radius + height / 2); - - CircleNodeData data = getCircleData(node); - data.setAngle(angle); - i++; - } - } - } - - protected CircleNodeData getCircleData(N node) { - return circleNodeDatas.getUnchecked(node); - } - - protected static class CircleNodeData { - private double angle; - - protected double getAngle() { - return angle; - } - - protected void setAngle(double angle) { - this.angle = angle; - } - - @Override - public String toString() { - return "CircleNodeData: angle=" + angle; - } - } + private double radius; + private List node_ordered_list; + + protected LoadingCache circleNodeDatas = + CacheBuilder.newBuilder() + .build( + new CacheLoader() { + public CircleNodeData load(N node) { + return new CircleNodeData(); + } + }); + + public CircleLayout(Graph g) { + super(g); + } + + /** @return the radius of the circle. */ + public double getRadius() { + return radius; + } + + /** + * Sets the radius of the circle. Must be called before {@code initialize()} is called. + * + * @param radius the radius of the circle + */ + public void setRadius(double radius) { + this.radius = radius; + } + + /** + * Sets the order of the nodes in the layout according to the ordering specified by {@code + * comparator}. + * + * @param comparator the comparator to use to order the nodes + */ + public void setNodeOrder(Comparator comparator) { + if (node_ordered_list == null) node_ordered_list = new ArrayList(nodes()); + Collections.sort(node_ordered_list, comparator); + } + + /** + * Sets the order of the nodes in the layout according to the ordering of {@code node_list}. + * + * @param node_list a list specifying the ordering of the nodes + */ + public void setNodeOrder(List node_list) { + if (!node_list.containsAll(nodes())) + throw new IllegalArgumentException("Supplied list must include " + "all nodes of the graph"); + this.node_ordered_list = node_list; + } + + public void reset() { + initialize(); + } + + public void initialize() { + Dimension d = getSize(); + + if (d != null) { + if (node_ordered_list == null) setNodeOrder(new ArrayList(nodes())); + + double height = d.getHeight(); + double width = d.getWidth(); + + if (radius <= 0) { + radius = 0.45 * (height < width ? height : width); + } + + int i = 0; + for (N node : node_ordered_list) { + Point2D coord = apply(node); + + double angle = (2 * Math.PI * i) / node_ordered_list.size(); + + coord.setLocation( + Math.cos(angle) * radius + width / 2, Math.sin(angle) * radius + height / 2); + + CircleNodeData data = getCircleData(node); + data.setAngle(angle); + i++; + } + } + } + + protected CircleNodeData getCircleData(N node) { + return circleNodeDatas.getUnchecked(node); + } + + protected static class CircleNodeData { + private double angle; + + protected double getAngle() { + return angle; + } + + protected void setAngle(double angle) { + this.angle = angle; + } + + @Override + public String toString() { + return "CircleNodeData: angle=" + angle; + } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/DAGLayout.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/DAGLayout.java index 6d5fef4e..def999c9 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/DAGLayout.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/DAGLayout.java @@ -11,328 +11,294 @@ */ package edu.uci.ics.jung.algorithms.layout; +import com.google.common.graph.EndpointPair; +import com.google.common.graph.Graph; import java.awt.Dimension; import java.awt.geom.Point2D; import java.util.HashMap; import java.util.Map; -import com.google.common.graph.EndpointPair; -import com.google.common.graph.Graph; - /** - * An implementation of {@code Layout} suitable for tree-like directed - * acyclic graphs. Parts of it will probably not terminate if the graph is - * cyclic! The layout will result in directed edges pointing generally upwards. - * Any nodes with no successors are considered to be level 0, and tend - * towards the top of the layout. Any node has a level one greater than the - * maximum level of all its successors. - * + * An implementation of {@code Layout} suitable for tree-like directed acyclic graphs. Parts of it + * will probably not terminate if the graph is cyclic! The layout will result in directed edges + * pointing generally upwards. Any nodes with no successors are considered to be level 0, and tend + * towards the top of the layout. Any node has a level one greater than the maximum level of all its + * successors. * * @author John Yesberg */ public class DAGLayout extends SpringLayout { - /** - * Each node has a minimumLevel. Any node with no successors has - * minimumLevel of zero. The minimumLevel of any node must be strictly - * greater than the minimumLevel of its parents. (node A is a parent of - * node B iff there is an edge from B to A.) Typically, a node will - * have a minimumLevel which is one greater than the minimumLevel of its - * parent's. However, if the node has two parents, its minimumLevel will - * be one greater than the maximum of the parents'. We need to calculate - * the minimumLevel for each node. When we layout the graph, nodes - * cannot be drawn any higher than the minimumLevel. The graphHeight of a - * graph is the greatest minimumLevel that is used. We will modify the - * SpringLayout calculations so that nodes cannot move above their assigned - * minimumLevel. - */ - private Map minLevels = new HashMap(); - // Simpler than the "pair" technique. - static int graphHeight; - static int numRoots; - final double SPACEFACTOR = 1.3; - // How much space do we allow for additional floating at the bottom. - final double LEVELATTRACTIONRATE = 0.8; - - /** - * A bunch of parameters to help work out when to stop quivering. - * - * If the MeanSquareVel(ocity) ever gets below the MSV_THRESHOLD, then we - * will start a final cool-down phase of COOL_DOWN_INCREMENT increments. If - * the MeanSquareVel ever exceeds the threshold, we will exit the cool down - * phase, and continue looking for another opportunity. - */ - final double MSV_THRESHOLD = 10.0; - double meanSquareVel; - boolean stoppingIncrements = false; - int incrementsLeft; - final int COOL_DOWN_INCREMENTS = 200; - - public DAGLayout(Graph g) { - super(g); - } - - /** - * Calculates the level of each node in the graph. Level 0 is - * allocated to each node with no successors. Level n+1 is allocated to - * any node whose successors' maximum level is n. - */ - public void setRoot() { - numRoots = 0; - Graph g = graph; - for(N node : g.nodes()) { - if (g.successors(node).isEmpty()) { - setRoot(node); - numRoots++; - } - } - } - - /** - * Set node v to be level 0. - * @param v the node to set as root - */ - public void setRoot(N node) { - minLevels.put(node, new Integer(0)); - // set all the levels. - propagateMinimumLevel(node); - } - - /** - * A recursive method for allocating the level for each node. Ensures - * that all predecessors of v have a level which is at least one greater - * than the level of v. - * - * @param v the node whose minimum level is to be calculated - */ - public void propagateMinimumLevel(N node) { - int level = minLevels.get(node).intValue(); - for(N child : graph.predecessors(node)) { - int oldLevel, newLevel; - Number o = minLevels.get(child); - if (o != null) - oldLevel = o.intValue(); - else - oldLevel = 0; - newLevel = Math.max(oldLevel, level + 1); - minLevels.put(child, new Integer(newLevel)); - - if (newLevel > graphHeight) - graphHeight = newLevel; - propagateMinimumLevel(child); - } - } - - /** - * Sets a random location for a node within the dimensions of the space. - * - * @param v the node whose position is to be set - * @param coord the coordinates of the node once the position has been set - * @param d the dimensions of the space - */ - private void initializeLocation( - N node, - Point2D coord, - Dimension d) { - - int level = minLevels.get(node).intValue(); - int minY = (int) (level * d.getHeight() / (graphHeight * SPACEFACTOR)); - double x = Math.random() * d.getWidth(); - double y = Math.random() * (d.getHeight() - minY) + minY; - coord.setLocation(x,y); - } - - @Override - public void setSize(Dimension size) { - super.setSize(size); - for(N node : nodes()) { - initializeLocation(node,apply(node),getSize()); - } - } - - /** - * Had to override this one as well, to ensure that setRoot() is called. - */ - @Override - public void initialize() { - super.initialize(); - setRoot(); - } - - /** - * Override the moveNodes() method from SpringLayout. The only change we - * need to make is to make sure that nodes don't float higher than the minY - * coordinate, as calculated by their minimumLevel. - */ - @Override - protected void moveNodes() { - // Dimension d = currentSize; - double oldMSV = meanSquareVel; - meanSquareVel = 0; - - synchronized (getSize()) { - - for(N node : nodes()) { - if (isLocked(node)) - continue; - SpringLayout.SpringNodeData vd = springNodeData.getUnchecked(node); - Point2D xyd = apply(node); - - int width = getSize().width; - int height = getSize().height; - - // (JY addition: three lines are new) - int level = - minLevels.get(node).intValue(); - int minY = (int) (level * height / (graphHeight * SPACEFACTOR)); - int maxY = - level == 0 - ? (int) (height / (graphHeight * SPACEFACTOR * 2)) - : height; - - // JY added 2* - double the sideways repulsion. - vd.dx += 2 * vd.repulsiondx + vd.edgedx; - vd.dy += vd.repulsiondy + vd.edgedy; - - // JY Addition: Attract the node towards it's minimumLevel - // height. - double delta = xyd.getY() - minY; - vd.dy -= delta * LEVELATTRACTIONRATE; - if (level == 0) - vd.dy -= delta * LEVELATTRACTIONRATE; - // twice as much at the top. - - // JY addition: - meanSquareVel += (vd.dx * vd.dx + vd.dy * vd.dy); - - // keeps nodes from moving any faster than 5 per time unit - xyd.setLocation(xyd.getX()+Math.max(-5, Math.min(5, vd.dx)) , xyd.getY()+Math.max(-5, Math.min(5, vd.dy)) ); - - if (xyd.getX() < 0) { - xyd.setLocation(0, xyd.getY()); - } else if (xyd.getX() > width) { - xyd.setLocation(width, xyd.getY()); - } - - // (JY addition: These two lines replaced 0 with minY) - if (xyd.getY() < minY) { - xyd.setLocation(xyd.getX(), minY); - // (JY addition: replace height with maxY) - } else if (xyd.getY() > maxY) { - xyd.setLocation(xyd.getX(), maxY); - } - - // (JY addition: if there's only one root, anchor it in the - // middle-top of the screen) - if (numRoots == 1 && level == 0) { - xyd.setLocation(width/2, xyd.getY()); - } - } - } - //System.out.println("MeanSquareAccel="+meanSquareVel); - if (!stoppingIncrements - && Math.abs(meanSquareVel - oldMSV) < MSV_THRESHOLD) { - stoppingIncrements = true; - incrementsLeft = COOL_DOWN_INCREMENTS; - } else if ( - stoppingIncrements - && Math.abs(meanSquareVel - oldMSV) <= MSV_THRESHOLD) { - incrementsLeft--; - if (incrementsLeft <= 0) - incrementsLeft = 0; - } - } - - /** - * Override incrementsAreDone so that we can eventually stop. - */ - @Override - public boolean done() { - if (stoppingIncrements && incrementsLeft == 0) - return true; - else - return false; - } - - /** - * Override forceMove so that if someone moves a node, we can re-layout - * everything. - * @param picked the node whose location is to be set - * @param x the x coordinate of the location to set - * @param y the y coordinate of the location to set - */ - @Override - public void setLocation(N picked, double x, double y) { - Point2D coord = apply(picked); - coord.setLocation(x,y); - stoppingIncrements = false; - } - - /** - * Override forceMove so that if someone moves a node, we can re-layout - * everything. - * @param picked the node whose location is to be set - * @param p the location to set - */ - @Override - public void setLocation(N picked, Point2D p) { - setLocation(picked, p.getX(), p.getY()); - } - - /** - * Overridden relaxEdges. This one reduces the effect of edges between - * greatly different levels. - * - */ - @Override - protected void relaxEdges() { - for(EndpointPair endpoints : graph.edges()) { - N node1 = endpoints.nodeU(); - N node2 = endpoints.nodeV(); - - Point2D p1 = apply(node1); - Point2D p2 = apply(node2); - double vx = p1.getX() - p2.getX(); - double vy = p1.getY() - p2.getY(); - double len = Math.sqrt(vx * vx + vy * vy); - - // JY addition. - int level1 = - minLevels.get(node1).intValue(); - int level2 = - minLevels.get(node2).intValue(); - - double desiredLen = lengthFunction.apply(endpoints); - - // round from zero, if needed [zero would be Bad.]. - len = (len == 0) ? .0001 : len; - - // force factor: optimal length minus actual length, - // is made smaller as the current actual length gets larger. - // why? - - double f = force_multiplier * (desiredLen - len) / len; - - f = f * Math.pow(stretch / 100.0, - (graph.degree(node1) + graph.degree(node2) -2)); - - // JY addition. If this is an edge which stretches a long way, - // don't be so concerned about it. - if (level1 != level2) - f = f / Math.pow(Math.abs(level2 - level1), 1.5); - - // the actual movement distance 'dx' is the force multiplied by the - // distance to go. - double dx = f * vx; - double dy = f * vy; - SpringNodeData v1D, v2D; - v1D = springNodeData.getUnchecked(node1); - v2D = springNodeData.getUnchecked(node2); - - v1D.edgedx += dx; - v1D.edgedy += dy; - v2D.edgedx += -dx; - v2D.edgedy += -dy; - } - } + /** + * Each node has a minimumLevel. Any node with no successors has minimumLevel of zero. The + * minimumLevel of any node must be strictly greater than the minimumLevel of its parents. (node A + * is a parent of node B iff there is an edge from B to A.) Typically, a node will have a + * minimumLevel which is one greater than the minimumLevel of its parent's. However, if the node + * has two parents, its minimumLevel will be one greater than the maximum of the parents'. We need + * to calculate the minimumLevel for each node. When we layout the graph, nodes cannot be drawn + * any higher than the minimumLevel. The graphHeight of a graph is the greatest minimumLevel that + * is used. We will modify the SpringLayout calculations so that nodes cannot move above their + * assigned minimumLevel. + */ + private Map minLevels = new HashMap(); + // Simpler than the "pair" technique. + static int graphHeight; + static int numRoots; + final double SPACEFACTOR = 1.3; + // How much space do we allow for additional floating at the bottom. + final double LEVELATTRACTIONRATE = 0.8; + + /** + * A bunch of parameters to help work out when to stop quivering. + * + *

If the MeanSquareVel(ocity) ever gets below the MSV_THRESHOLD, then we will start a final + * cool-down phase of COOL_DOWN_INCREMENT increments. If the MeanSquareVel ever exceeds the + * threshold, we will exit the cool down phase, and continue looking for another opportunity. + */ + final double MSV_THRESHOLD = 10.0; + + double meanSquareVel; + boolean stoppingIncrements = false; + int incrementsLeft; + final int COOL_DOWN_INCREMENTS = 200; + + public DAGLayout(Graph g) { + super(g); + } + + /** + * Calculates the level of each node in the graph. Level 0 is allocated to each node with no + * successors. Level n+1 is allocated to any node whose successors' maximum level is n. + */ + public void setRoot() { + numRoots = 0; + Graph g = graph; + for (N node : g.nodes()) { + if (g.successors(node).isEmpty()) { + setRoot(node); + numRoots++; + } + } + } + + /** + * Set node v to be level 0. + * + * @param v the node to set as root + */ + public void setRoot(N node) { + minLevels.put(node, new Integer(0)); + // set all the levels. + propagateMinimumLevel(node); + } + + /** + * A recursive method for allocating the level for each node. Ensures that all predecessors of v + * have a level which is at least one greater than the level of v. + * + * @param v the node whose minimum level is to be calculated + */ + public void propagateMinimumLevel(N node) { + int level = minLevels.get(node).intValue(); + for (N child : graph.predecessors(node)) { + int oldLevel, newLevel; + Number o = minLevels.get(child); + if (o != null) oldLevel = o.intValue(); + else oldLevel = 0; + newLevel = Math.max(oldLevel, level + 1); + minLevels.put(child, new Integer(newLevel)); + + if (newLevel > graphHeight) graphHeight = newLevel; + propagateMinimumLevel(child); + } + } + + /** + * Sets a random location for a node within the dimensions of the space. + * + * @param v the node whose position is to be set + * @param coord the coordinates of the node once the position has been set + * @param d the dimensions of the space + */ + private void initializeLocation(N node, Point2D coord, Dimension d) { + + int level = minLevels.get(node).intValue(); + int minY = (int) (level * d.getHeight() / (graphHeight * SPACEFACTOR)); + double x = Math.random() * d.getWidth(); + double y = Math.random() * (d.getHeight() - minY) + minY; + coord.setLocation(x, y); + } + + @Override + public void setSize(Dimension size) { + super.setSize(size); + for (N node : nodes()) { + initializeLocation(node, apply(node), getSize()); + } + } + + /** Had to override this one as well, to ensure that setRoot() is called. */ + @Override + public void initialize() { + super.initialize(); + setRoot(); + } + + /** + * Override the moveNodes() method from SpringLayout. The only change we need to make is to make + * sure that nodes don't float higher than the minY coordinate, as calculated by their + * minimumLevel. + */ + @Override + protected void moveNodes() { + // Dimension d = currentSize; + double oldMSV = meanSquareVel; + meanSquareVel = 0; + + synchronized (getSize()) { + for (N node : nodes()) { + if (isLocked(node)) continue; + SpringLayout.SpringNodeData vd = springNodeData.getUnchecked(node); + Point2D xyd = apply(node); + + int width = getSize().width; + int height = getSize().height; + + // (JY addition: three lines are new) + int level = minLevels.get(node).intValue(); + int minY = (int) (level * height / (graphHeight * SPACEFACTOR)); + int maxY = level == 0 ? (int) (height / (graphHeight * SPACEFACTOR * 2)) : height; + + // JY added 2* - double the sideways repulsion. + vd.dx += 2 * vd.repulsiondx + vd.edgedx; + vd.dy += vd.repulsiondy + vd.edgedy; + + // JY Addition: Attract the node towards it's minimumLevel + // height. + double delta = xyd.getY() - minY; + vd.dy -= delta * LEVELATTRACTIONRATE; + if (level == 0) vd.dy -= delta * LEVELATTRACTIONRATE; + // twice as much at the top. + + // JY addition: + meanSquareVel += (vd.dx * vd.dx + vd.dy * vd.dy); + + // keeps nodes from moving any faster than 5 per time unit + xyd.setLocation( + xyd.getX() + Math.max(-5, Math.min(5, vd.dx)), + xyd.getY() + Math.max(-5, Math.min(5, vd.dy))); + + if (xyd.getX() < 0) { + xyd.setLocation(0, xyd.getY()); + } else if (xyd.getX() > width) { + xyd.setLocation(width, xyd.getY()); + } + + // (JY addition: These two lines replaced 0 with minY) + if (xyd.getY() < minY) { + xyd.setLocation(xyd.getX(), minY); + // (JY addition: replace height with maxY) + } else if (xyd.getY() > maxY) { + xyd.setLocation(xyd.getX(), maxY); + } + + // (JY addition: if there's only one root, anchor it in the + // middle-top of the screen) + if (numRoots == 1 && level == 0) { + xyd.setLocation(width / 2, xyd.getY()); + } + } + } + //System.out.println("MeanSquareAccel="+meanSquareVel); + if (!stoppingIncrements && Math.abs(meanSquareVel - oldMSV) < MSV_THRESHOLD) { + stoppingIncrements = true; + incrementsLeft = COOL_DOWN_INCREMENTS; + } else if (stoppingIncrements && Math.abs(meanSquareVel - oldMSV) <= MSV_THRESHOLD) { + incrementsLeft--; + if (incrementsLeft <= 0) incrementsLeft = 0; + } + } + + /** Override incrementsAreDone so that we can eventually stop. */ + @Override + public boolean done() { + if (stoppingIncrements && incrementsLeft == 0) return true; + else return false; + } + + /** + * Override forceMove so that if someone moves a node, we can re-layout everything. + * + * @param picked the node whose location is to be set + * @param x the x coordinate of the location to set + * @param y the y coordinate of the location to set + */ + @Override + public void setLocation(N picked, double x, double y) { + Point2D coord = apply(picked); + coord.setLocation(x, y); + stoppingIncrements = false; + } + + /** + * Override forceMove so that if someone moves a node, we can re-layout everything. + * + * @param picked the node whose location is to be set + * @param p the location to set + */ + @Override + public void setLocation(N picked, Point2D p) { + setLocation(picked, p.getX(), p.getY()); + } + + /** + * Overridden relaxEdges. This one reduces the effect of edges between greatly different levels. + */ + @Override + protected void relaxEdges() { + for (EndpointPair endpoints : graph.edges()) { + N node1 = endpoints.nodeU(); + N node2 = endpoints.nodeV(); + + Point2D p1 = apply(node1); + Point2D p2 = apply(node2); + double vx = p1.getX() - p2.getX(); + double vy = p1.getY() - p2.getY(); + double len = Math.sqrt(vx * vx + vy * vy); + + // JY addition. + int level1 = minLevels.get(node1).intValue(); + int level2 = minLevels.get(node2).intValue(); + + double desiredLen = lengthFunction.apply(endpoints); + + // round from zero, if needed [zero would be Bad.]. + len = (len == 0) ? .0001 : len; + + // force factor: optimal length minus actual length, + // is made smaller as the current actual length gets larger. + // why? + + double f = force_multiplier * (desiredLen - len) / len; + + f = f * Math.pow(stretch / 100.0, (graph.degree(node1) + graph.degree(node2) - 2)); + + // JY addition. If this is an edge which stretches a long way, + // don't be so concerned about it. + if (level1 != level2) f = f / Math.pow(Math.abs(level2 - level1), 1.5); + + // the actual movement distance 'dx' is the force multiplied by the + // distance to go. + double dx = f * vx; + double dy = f * vy; + SpringNodeData v1D, v2D; + v1D = springNodeData.getUnchecked(node1); + v2D = springNodeData.getUnchecked(node2); + + v1D.edgedx += dx; + v1D.edgedy += dy; + v2D.edgedx += -dx; + v2D.edgedy += -dy; + } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/FRLayout.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/FRLayout.java index 8f648673..5068361a 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/FRLayout.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/FRLayout.java @@ -7,313 +7,299 @@ */ package edu.uci.ics.jung.algorithms.layout; -import java.awt.Dimension; -import java.awt.geom.Point2D; -import java.util.ConcurrentModificationException; - import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.graph.EndpointPair; import com.google.common.graph.Graph; - import edu.uci.ics.jung.algorithms.layout.util.RandomLocationTransformer; import edu.uci.ics.jung.algorithms.util.IterativeContext; +import java.awt.Dimension; +import java.awt.geom.Point2D; +import java.util.ConcurrentModificationException; /** * Implements the Fruchterman-Reingold force-directed algorithm for node layout. - * + * *

Behavior is determined by the following settable parameters: + * *

    - *
  • attraction multiplier: how much edges try to keep their nodes together - *
  • repulsion multiplier: how much nodes try to push each other apart - *
  • maximum iterations: how many iterations this algorithm will use before stopping + *
  • attraction multiplier: how much edges try to keep their nodes together + *
  • repulsion multiplier: how much nodes try to push each other apart + *
  • maximum iterations: how many iterations this algorithm will use before stopping *
+ * * Each of the first two defaults to 0.75; the maximum number of iterations defaults to 700. * * @see "Fruchterman and Reingold, 'Graph Drawing by Force-directed Placement'" - * @see "http://i11www.ilkd.uni-karlsruhe.de/teaching/SS_04/visualisierung/papers/fruchterman91graph.pdf" + * @see + * "http://i11www.ilkd.uni-karlsruhe.de/teaching/SS_04/visualisierung/papers/fruchterman91graph.pdf" * @author Scott White, Yan-Biao Boey, Danyel Fisher */ public class FRLayout extends AbstractLayout implements IterativeContext { - private double forceConstant; + private double forceConstant; - private double temperature; + private double temperature; - private int currentIteration; + private int currentIteration; - private int mMaxIterations = 700; + private int mMaxIterations = 700; - protected LoadingCache frNodeData = - CacheBuilder.newBuilder().build(new CacheLoader() { - public FRNodeData load(N node) { - return new FRNodeData(); - } - }); + protected LoadingCache frNodeData = + CacheBuilder.newBuilder() + .build( + new CacheLoader() { + public FRNodeData load(N node) { + return new FRNodeData(); + } + }); - private double attraction_multiplier = 0.75; + private double attraction_multiplier = 0.75; - private double attraction_constant; + private double attraction_constant; - private double repulsion_multiplier = 0.75; + private double repulsion_multiplier = 0.75; - private double repulsion_constant; + private double repulsion_constant; - private double max_dimension; - - private final Graph graph; + private double max_dimension; - public FRLayout(Graph g) { - super(g); - this.graph = g; - } + private final Graph graph; - public FRLayout(Graph g, Dimension d) { - super(g, new RandomLocationTransformer(d), d); - this.graph = g; - initialize(); - max_dimension = Math.max(d.height, d.width); - } + public FRLayout(Graph g) { + super(g); + this.graph = g; + } - @Override - public void setSize(Dimension size) { - if(initialized == false) { - setInitializer(new RandomLocationTransformer(size)); - } - super.setSize(size); - max_dimension = Math.max(size.height, size.width); - } - - public void setAttractionMultiplier(double attraction) { - this.attraction_multiplier = attraction; - } + public FRLayout(Graph g, Dimension d) { + super(g, new RandomLocationTransformer(d), d); + this.graph = g; + initialize(); + max_dimension = Math.max(d.height, d.width); + } - public void setRepulsionMultiplier(double repulsion) { - this.repulsion_multiplier = repulsion; + @Override + public void setSize(Dimension size) { + if (initialized == false) { + setInitializer(new RandomLocationTransformer(size)); } + super.setSize(size); + max_dimension = Math.max(size.height, size.width); + } - public void reset() { - doInit(); - } + public void setAttractionMultiplier(double attraction) { + this.attraction_multiplier = attraction; + } - public void initialize() { - doInit(); - } + public void setRepulsionMultiplier(double repulsion) { + this.repulsion_multiplier = repulsion; + } - private void doInit() { - Dimension d = getSize(); - if(graph != null && d != null) { - currentIteration = 0; - temperature = d.getWidth() / 10; - - forceConstant = - Math - .sqrt(d.getHeight() - * d.getWidth() - / graph.nodes().size()); - - attraction_constant = attraction_multiplier * forceConstant; - repulsion_constant = repulsion_multiplier * forceConstant; - } - } + public void reset() { + doInit(); + } - private double EPSILON = 0.000001D; + public void initialize() { + doInit(); + } - /** - * Moves the iteration forward one notch, calculation attraction and - * repulsion between nodes and edges and cooling the temperature. - */ - public synchronized void step() { - currentIteration++; + private void doInit() { + Dimension d = getSize(); + if (graph != null && d != null) { + currentIteration = 0; + temperature = d.getWidth() / 10; - /** - * Calculate repulsion - */ - while(true) { + forceConstant = Math.sqrt(d.getHeight() * d.getWidth() / graph.nodes().size()); - try { - for(N node1 : graph.nodes()) { - calcRepulsion(node1); - } - break; - } catch(ConcurrentModificationException cme) {} - } + attraction_constant = attraction_multiplier * forceConstant; + repulsion_constant = repulsion_multiplier * forceConstant; + } + } - /** - * Calculate attraction - */ - while(true) { - try { - for(EndpointPair endpoints : graph.edges()) { - calcAttraction(endpoints); - } - break; - } catch(ConcurrentModificationException cme) {} - } + private double EPSILON = 0.000001D; + /** + * Moves the iteration forward one notch, calculation attraction and repulsion between nodes and + * edges and cooling the temperature. + */ + public synchronized void step() { + currentIteration++; - while(true) { - try { - for(N node : graph.nodes()) { - if (isLocked(node)) continue; - calcPositions(node); - } - break; - } catch(ConcurrentModificationException cme) {} + /** Calculate repulsion */ + while (true) { + + try { + for (N node1 : graph.nodes()) { + calcRepulsion(node1); } - cool(); + break; + } catch (ConcurrentModificationException cme) { + } } - protected synchronized void calcPositions(N node) { - FRNodeData fvd = getFRData(node); - if(fvd == null) return; - Point2D xyd = apply(node); - double deltaLength = Math.max(EPSILON, fvd.norm()); - - double newXDisp = fvd.getX() / deltaLength - * Math.min(deltaLength, temperature); - - if (Double.isNaN(newXDisp)) { - throw new IllegalArgumentException( - "Unexpected mathematical result in FRLayout:calcPositions [xdisp]"); } - - double newYDisp = fvd.getY() / deltaLength - * Math.min(deltaLength, temperature); - xyd.setLocation(xyd.getX()+newXDisp, xyd.getY()+newYDisp); - - double borderWidth = getSize().getWidth() / 50.0; - double newXPos = xyd.getX(); - if (newXPos < borderWidth) { - newXPos = borderWidth + Math.random() * borderWidth * 2.0; - } else if (newXPos > (getSize().getWidth() - borderWidth)) { - newXPos = getSize().getWidth() - borderWidth - Math.random() - * borderWidth * 2.0; + /** Calculate attraction */ + while (true) { + try { + for (EndpointPair endpoints : graph.edges()) { + calcAttraction(endpoints); } + break; + } catch (ConcurrentModificationException cme) { + } + } - double newYPos = xyd.getY(); - if (newYPos < borderWidth) { - newYPos = borderWidth + Math.random() * borderWidth * 2.0; - } else if (newYPos > (getSize().getHeight() - borderWidth)) { - newYPos = getSize().getHeight() - borderWidth - - Math.random() * borderWidth * 2.0; + while (true) { + try { + for (N node : graph.nodes()) { + if (isLocked(node)) continue; + calcPositions(node); } - - xyd.setLocation(newXPos, newYPos); + break; + } catch (ConcurrentModificationException cme) { + } } + cool(); + } - protected void calcAttraction(EndpointPair endpoints) { - N node1 = endpoints.nodeU(); - N node2 = endpoints.nodeV(); - boolean v1_locked = isLocked(node1); - boolean v2_locked = isLocked(node2); + protected synchronized void calcPositions(N node) { + FRNodeData fvd = getFRData(node); + if (fvd == null) return; + Point2D xyd = apply(node); + double deltaLength = Math.max(EPSILON, fvd.norm()); - if(v1_locked && v2_locked) { - // both locked, do nothing - return; - } - Point2D p1 = apply(node1); - Point2D p2 = apply(node2); - if(p1 == null || p2 == null) return; - double xDelta = p1.getX() - p2.getX(); - double yDelta = p1.getY() - p2.getY(); - - double deltaLength = Math.max(EPSILON, Math.sqrt((xDelta * xDelta) - + (yDelta * yDelta))); + double newXDisp = fvd.getX() / deltaLength * Math.min(deltaLength, temperature); - double force = (deltaLength * deltaLength) / attraction_constant; + if (Double.isNaN(newXDisp)) { + throw new IllegalArgumentException( + "Unexpected mathematical result in FRLayout:calcPositions [xdisp]"); + } - if (Double.isNaN(force)) { throw new IllegalArgumentException( - "Unexpected mathematical result in FRLayout:calcPositions [force]"); } + double newYDisp = fvd.getY() / deltaLength * Math.min(deltaLength, temperature); + xyd.setLocation(xyd.getX() + newXDisp, xyd.getY() + newYDisp); - double dx = (xDelta / deltaLength) * force; - double dy = (yDelta / deltaLength) * force; - if (v1_locked == false) { - FRNodeData fvd1 = getFRData(node1); - fvd1.offset(-dx, -dy); - } - if (v2_locked == false) { - FRNodeData fvd2 = getFRData(node2); - fvd2.offset(dx, dy); - } + double borderWidth = getSize().getWidth() / 50.0; + double newXPos = xyd.getX(); + if (newXPos < borderWidth) { + newXPos = borderWidth + Math.random() * borderWidth * 2.0; + } else if (newXPos > (getSize().getWidth() - borderWidth)) { + newXPos = getSize().getWidth() - borderWidth - Math.random() * borderWidth * 2.0; } - protected void calcRepulsion(N node1) { - FRNodeData fvd1 = getFRData(node1); - if(fvd1 == null) - return; - fvd1.setLocation(0, 0); + double newYPos = xyd.getY(); + if (newYPos < borderWidth) { + newYPos = borderWidth + Math.random() * borderWidth * 2.0; + } else if (newYPos > (getSize().getHeight() - borderWidth)) { + newYPos = getSize().getHeight() - borderWidth - Math.random() * borderWidth * 2.0; + } - try { - for(N node2 : graph.nodes()) { + xyd.setLocation(newXPos, newYPos); + } -// if (isLocked(node2)) continue; - if (node1 != node2) { - Point2D p1 = apply(node1); - Point2D p2 = apply(node2); - if(p1 == null || p2 == null) continue; - double xDelta = p1.getX() - p2.getX(); - double yDelta = p1.getY() - p2.getY(); + protected void calcAttraction(EndpointPair endpoints) { + N node1 = endpoints.nodeU(); + N node2 = endpoints.nodeV(); + boolean v1_locked = isLocked(node1); + boolean v2_locked = isLocked(node2); - double deltaLength = Math.max(EPSILON, Math - .sqrt((xDelta * xDelta) + (yDelta * yDelta))); + if (v1_locked && v2_locked) { + // both locked, do nothing + return; + } + Point2D p1 = apply(node1); + Point2D p2 = apply(node2); + if (p1 == null || p2 == null) return; + double xDelta = p1.getX() - p2.getX(); + double yDelta = p1.getY() - p2.getY(); - double force = (repulsion_constant * repulsion_constant) / deltaLength; + double deltaLength = Math.max(EPSILON, Math.sqrt((xDelta * xDelta) + (yDelta * yDelta))); - if (Double.isNaN(force)) { throw new RuntimeException( - "Unexpected mathematical result in FRLayout:calcPositions [repulsion]"); } + double force = (deltaLength * deltaLength) / attraction_constant; - fvd1.offset((xDelta / deltaLength) * force, - (yDelta / deltaLength) * force); - } - } - } catch(ConcurrentModificationException cme) { - calcRepulsion(node1); - } + if (Double.isNaN(force)) { + throw new IllegalArgumentException( + "Unexpected mathematical result in FRLayout:calcPositions [force]"); } - private void cool() { - temperature *= (1.0 - currentIteration / (double) mMaxIterations); + double dx = (xDelta / deltaLength) * force; + double dy = (yDelta / deltaLength) * force; + if (v1_locked == false) { + FRNodeData fvd1 = getFRData(node1); + fvd1.offset(-dx, -dy); } - - public void setMaxIterations(int maxIterations) { - mMaxIterations = maxIterations; + if (v2_locked == false) { + FRNodeData fvd2 = getFRData(node2); + fvd2.offset(dx, dy); } + } - protected FRNodeData getFRData(N node) { - return frNodeData.getUnchecked(node); - } + protected void calcRepulsion(N node1) { + FRNodeData fvd1 = getFRData(node1); + if (fvd1 == null) return; + fvd1.setLocation(0, 0); - /** - * @return true - */ - public boolean isIncremental() { - return true; - } + try { + for (N node2 : graph.nodes()) { + + // if (isLocked(node2)) continue; + if (node1 != node2) { + Point2D p1 = apply(node1); + Point2D p2 = apply(node2); + if (p1 == null || p2 == null) continue; + double xDelta = p1.getX() - p2.getX(); + double yDelta = p1.getY() - p2.getY(); - /** - * @return true once the current iteration has passed the maximum count. - */ - public boolean done() { - if (currentIteration > mMaxIterations || temperature < 1.0/max_dimension) - { - return true; + double deltaLength = Math.max(EPSILON, Math.sqrt((xDelta * xDelta) + (yDelta * yDelta))); + + double force = (repulsion_constant * repulsion_constant) / deltaLength; + + if (Double.isNaN(force)) { + throw new RuntimeException( + "Unexpected mathematical result in FRLayout:calcPositions [repulsion]"); + } + + fvd1.offset((xDelta / deltaLength) * force, (yDelta / deltaLength) * force); } - return false; + } + } catch (ConcurrentModificationException cme) { + calcRepulsion(node1); } + } - @SuppressWarnings("serial") - protected static class FRNodeData extends Point2D.Double - { - protected void offset(double x, double y) - { - this.x += x; - this.y += y; - } + private void cool() { + temperature *= (1.0 - currentIteration / (double) mMaxIterations); + } - protected double norm() - { - return Math.sqrt(x*x + y*y); - } - } -} \ No newline at end of file + public void setMaxIterations(int maxIterations) { + mMaxIterations = maxIterations; + } + + protected FRNodeData getFRData(N node) { + return frNodeData.getUnchecked(node); + } + + /** @return true */ + public boolean isIncremental() { + return true; + } + + /** @return true once the current iteration has passed the maximum count. */ + public boolean done() { + if (currentIteration > mMaxIterations || temperature < 1.0 / max_dimension) { + return true; + } + return false; + } + + @SuppressWarnings("serial") + protected static class FRNodeData extends Point2D.Double { + protected void offset(double x, double y) { + this.x += x; + this.y += y; + } + + protected double norm() { + return Math.sqrt(x * x + y * y); + } + } +} diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/FRLayout2.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/FRLayout2.java index f84ab966..7a1b8006 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/FRLayout2.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/FRLayout2.java @@ -1,319 +1,309 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. */ package edu.uci.ics.jung.algorithms.layout; -import java.awt.Dimension; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.util.ConcurrentModificationException; - import com.google.common.base.Preconditions; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.graph.EndpointPair; import com.google.common.graph.Graph; - import edu.uci.ics.jung.algorithms.layout.util.RandomLocationTransformer; import edu.uci.ics.jung.algorithms.util.IterativeContext; +import java.awt.Dimension; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ConcurrentModificationException; /** - * Implements the Fruchterman-Reingold force-directed algorithm for node layout. - * This is an experimental attempt at optimizing {@code FRLayout}; if it is successful - * it will be folded back into {@code FRLayout} (and this class will disappear). - * + * Implements the Fruchterman-Reingold force-directed algorithm for node layout. This is an + * experimental attempt at optimizing {@code FRLayout}; if it is successful it will be folded back + * into {@code FRLayout} (and this class will disappear). + * *

Behavior is determined by the following settable parameters: + * *

    - *
  • attraction multiplier: how much edges try to keep their nodes together - *
  • repulsion multiplier: how much nodes try to push each other apart - *
  • maximum iterations: how many iterations this algorithm will use before stopping + *
  • attraction multiplier: how much edges try to keep their nodes together + *
  • repulsion multiplier: how much nodes try to push each other apart + *
  • maximum iterations: how many iterations this algorithm will use before stopping *
+ * * Each of the first two defaults to 0.75; the maximum number of iterations defaults to 700. - - * + * * @see "Fruchterman and Reingold, 'Graph Drawing by Force-directed Placement'" - * @see "http://i11www.ilkd.uni-karlsruhe.de/teaching/SS_04/visualisierung/papers/fruchterman91graph.pdf" - * + * @see + * "http://i11www.ilkd.uni-karlsruhe.de/teaching/SS_04/visualisierung/papers/fruchterman91graph.pdf" * @author Tom Nelson * @author Scott White, Yan-Biao Boey, Danyel Fisher */ public class FRLayout2 extends AbstractLayout implements IterativeContext { - private double forceConstant; - - private double temperature; - - private int currentIteration; - - private int maxIterations = 700; - - protected LoadingCache frNodeData = - CacheBuilder.newBuilder().build(new CacheLoader() { - public Point2D load(N node) { - return new Point2D.Double(); - } - }); - - private double attraction_multiplier = 0.75; - - private double attraction_constant; - - private double repulsion_multiplier = 0.75; - - private double repulsion_constant; - - private double max_dimension; - - private Rectangle2D innerBounds = new Rectangle2D.Double(); - - private boolean checked = false; - - private final Graph graph; - - public FRLayout2(Graph g) { - super(g); - this.graph = g; - } - - public FRLayout2(Graph g, Dimension d) { - super(g, new RandomLocationTransformer(d), d); - this.graph = g; - max_dimension = Math.max(d.height, d.width); - initialize(); - } - - @Override - public void setSize(Dimension size) { - if(initialized == false) - setInitializer(new RandomLocationTransformer(size)); - super.setSize(size); - double t = size.width/50.0; - innerBounds.setFrameFromDiagonal(t,t,size.width-t,size.height-t); - max_dimension = Math.max(size.height, size.width); - } - - public void setAttractionMultiplier(double attraction) { - this.attraction_multiplier = attraction; - } - - public void setRepulsionMultiplier(double repulsion) { - this.repulsion_multiplier = repulsion; - } - - public void reset() { - doInit(); - } - - public void initialize() { - doInit(); - } + private double forceConstant; - private void doInit() { - Dimension d = getSize(); - if(graph != null && d != null) { - currentIteration = 0; - temperature = d.getWidth() / 10; - - forceConstant = - Math - .sqrt(d.getHeight() - * d.getWidth() - / graph.nodes().size()); - - attraction_constant = attraction_multiplier * forceConstant; - repulsion_constant = repulsion_multiplier * forceConstant; - } - } + private double temperature; - private double EPSILON = 0.000001D; - - /** - * Moves the iteration forward one notch, calculation attraction and - * repulsion between nodes and edges and cooling the temperature. - */ - public synchronized void step() { - currentIteration++; - - /** - * Calculate repulsion - */ - while(true) { - - try { - for(N node1 : graph.nodes()) { - calcRepulsion(node1); - } - break; - } catch(ConcurrentModificationException cme) {} - } + private int currentIteration; - /** - * Calculate attraction - */ - while(true) { - try { - for(EndpointPair endpoints : graph.edges()) { - calcAttraction(endpoints); + private int maxIterations = 700; + + protected LoadingCache frNodeData = + CacheBuilder.newBuilder() + .build( + new CacheLoader() { + public Point2D load(N node) { + return new Point2D.Double(); } - break; - } catch(ConcurrentModificationException cme) {} - } + }); + private double attraction_multiplier = 0.75; - while(true) { - try { - for(N node : graph.nodes()) { - if (isLocked(node)) continue; - calcPositions(node); - } - break; - } catch(ConcurrentModificationException cme) {} - } - cool(); - } + private double attraction_constant; + + private double repulsion_multiplier = 0.75; + + private double repulsion_constant; + + private double max_dimension; + + private Rectangle2D innerBounds = new Rectangle2D.Double(); + + private boolean checked = false; + + private final Graph graph; - protected synchronized void calcPositions(N node) { - Point2D fvd = this.frNodeData.getUnchecked(node); - if(fvd == null) return; - Point2D xyd = apply(node); - double deltaLength = Math.max(EPSILON, - Math.sqrt(fvd.getX()*fvd.getX()+fvd.getY()*fvd.getY())); - - double newXDisp = fvd.getX() / deltaLength - * Math.min(deltaLength, temperature); - - Preconditions.checkState(!Double.isNaN(newXDisp), - "Unexpected mathematical result in FRLayout:calcPositions [xdisp]"); - - double newYDisp = fvd.getY() / deltaLength - * Math.min(deltaLength, temperature); - double newX = xyd.getX()+Math.max(-5, Math.min(5,newXDisp)); - double newY = xyd.getY()+Math.max(-5, Math.min(5,newYDisp)); - - newX = Math.max(innerBounds.getMinX(), Math.min(newX, innerBounds.getMaxX())); - newY = Math.max(innerBounds.getMinY(), Math.min(newY, innerBounds.getMaxY())); - - xyd.setLocation(newX, newY); + public FRLayout2(Graph g) { + super(g); + this.graph = g; + } + public FRLayout2(Graph g, Dimension d) { + super(g, new RandomLocationTransformer(d), d); + this.graph = g; + max_dimension = Math.max(d.height, d.width); + initialize(); + } + + @Override + public void setSize(Dimension size) { + if (initialized == false) setInitializer(new RandomLocationTransformer(size)); + super.setSize(size); + double t = size.width / 50.0; + innerBounds.setFrameFromDiagonal(t, t, size.width - t, size.height - t); + max_dimension = Math.max(size.height, size.width); + } + + public void setAttractionMultiplier(double attraction) { + this.attraction_multiplier = attraction; + } + + public void setRepulsionMultiplier(double repulsion) { + this.repulsion_multiplier = repulsion; + } + + public void reset() { + doInit(); + } + + public void initialize() { + doInit(); + } + + private void doInit() { + Dimension d = getSize(); + if (graph != null && d != null) { + currentIteration = 0; + temperature = d.getWidth() / 10; + + forceConstant = Math.sqrt(d.getHeight() * d.getWidth() / graph.nodes().size()); + + attraction_constant = attraction_multiplier * forceConstant; + repulsion_constant = repulsion_multiplier * forceConstant; } + } - protected void calcAttraction(EndpointPair endpoints) { - N node1 = endpoints.nodeU(); - N node2 = endpoints.nodeV(); - boolean v1_locked = isLocked(node1); - boolean v2_locked = isLocked(node2); - - if (v1_locked && v2_locked) { - // both locked, do nothing - return; - } - Point2D p1 = apply(node1); - Point2D p2 = apply(node2); - if(p1 == null || p2 == null) return; - double xDelta = p1.getX() - p2.getX(); - double yDelta = p1.getY() - p2.getY(); - - double deltaLength = Math.max(EPSILON, p1.distance(p2)); - - double force = deltaLength / attraction_constant; - - Preconditions.checkState(!Double.isNaN(force), - "Unexpected mathematical result in FRLayout:calcPositions [force]"); - - double dx = xDelta * force; - double dy = yDelta * force; - Point2D fvd1 = frNodeData.getUnchecked(node1); - Point2D fvd2 = frNodeData.getUnchecked(node2); - if (v2_locked) { - // double the offset for v1, as v2 will not be moving in - // the opposite direction - fvd1.setLocation(fvd1.getX()-2*dx, fvd1.getY()-2*dy); - } else { - fvd1.setLocation(fvd1.getX()-dx, fvd1.getY()-dy); + private double EPSILON = 0.000001D; + + /** + * Moves the iteration forward one notch, calculation attraction and repulsion between nodes and + * edges and cooling the temperature. + */ + public synchronized void step() { + currentIteration++; + + /** Calculate repulsion */ + while (true) { + + try { + for (N node1 : graph.nodes()) { + calcRepulsion(node1); } - if (v1_locked) { - // double the offset for v2, as v1 will not be moving in - // the opposite direction - fvd2.setLocation(fvd2.getX()+2*dx, fvd2.getY()+2*dy); - } else { - fvd2.setLocation(fvd2.getX()+dx, fvd2.getY()+dy); - } + break; + } catch (ConcurrentModificationException cme) { + } } - protected void calcRepulsion(N node1) { - Point2D fvd1 = frNodeData.getUnchecked(node1); - if(fvd1 == null) return; - fvd1.setLocation(0, 0); - boolean v1_locked = isLocked(node1); - - try { - for(N node2 : graph.nodes()) { - - boolean v2_locked = isLocked(node2); - if (v1_locked && v2_locked) continue; - if (node1 != node2) { - Point2D p1 = apply(node1); - Point2D p2 = apply(node2); - if(p1 == null || p2 == null) continue; - double xDelta = p1.getX() - p2.getX(); - double yDelta = p1.getY() - p2.getY(); - - double deltaLength = Math.max(EPSILON, p1.distanceSq(p2)); - - double force = (repulsion_constant * repulsion_constant);// / deltaLength; - - double forceOverDeltaLength = force / deltaLength; - - Preconditions.checkState(!Double.isNaN(force), - "Unexpected mathematical result in FRLayout:calcPositions [repulsion]"); - - if(v2_locked) { - // double the offset for v1, as v2 will not be moving in - // the opposite direction - fvd1.setLocation(fvd1.getX()+2 * xDelta * forceOverDeltaLength, - fvd1.getY()+ 2 * yDelta * forceOverDeltaLength); - } else { - fvd1.setLocation(fvd1.getX()+xDelta * forceOverDeltaLength, - fvd1.getY()+yDelta * forceOverDeltaLength); - } - } - } - } catch(ConcurrentModificationException cme) { - calcRepulsion(node1); + /** Calculate attraction */ + while (true) { + try { + for (EndpointPair endpoints : graph.edges()) { + calcAttraction(endpoints); } + break; + } catch (ConcurrentModificationException cme) { + } } - private void cool() { - temperature *= (1.0 - currentIteration / (double) maxIterations); + while (true) { + try { + for (N node : graph.nodes()) { + if (isLocked(node)) continue; + calcPositions(node); + } + break; + } catch (ConcurrentModificationException cme) { + } } + cool(); + } - public void setMaxIterations(int maxIterations) { - this.maxIterations = maxIterations; - } + protected synchronized void calcPositions(N node) { + Point2D fvd = this.frNodeData.getUnchecked(node); + if (fvd == null) return; + Point2D xyd = apply(node); + double deltaLength = + Math.max(EPSILON, Math.sqrt(fvd.getX() * fvd.getX() + fvd.getY() * fvd.getY())); + + double newXDisp = fvd.getX() / deltaLength * Math.min(deltaLength, temperature); + + Preconditions.checkState( + !Double.isNaN(newXDisp), + "Unexpected mathematical result in FRLayout:calcPositions [xdisp]"); + + double newYDisp = fvd.getY() / deltaLength * Math.min(deltaLength, temperature); + double newX = xyd.getX() + Math.max(-5, Math.min(5, newXDisp)); + double newY = xyd.getY() + Math.max(-5, Math.min(5, newYDisp)); + + newX = Math.max(innerBounds.getMinX(), Math.min(newX, innerBounds.getMaxX())); + newY = Math.max(innerBounds.getMinY(), Math.min(newY, innerBounds.getMaxY())); + + xyd.setLocation(newX, newY); + } + + protected void calcAttraction(EndpointPair endpoints) { + N node1 = endpoints.nodeU(); + N node2 = endpoints.nodeV(); + boolean v1_locked = isLocked(node1); + boolean v2_locked = isLocked(node2); - /** - * @return true - */ - public boolean isIncremental() { - return true; + if (v1_locked && v2_locked) { + // both locked, do nothing + return; } + Point2D p1 = apply(node1); + Point2D p2 = apply(node2); + if (p1 == null || p2 == null) return; + double xDelta = p1.getX() - p2.getX(); + double yDelta = p1.getY() - p2.getY(); + + double deltaLength = Math.max(EPSILON, p1.distance(p2)); + + double force = deltaLength / attraction_constant; + + Preconditions.checkState( + !Double.isNaN(force), "Unexpected mathematical result in FRLayout:calcPositions [force]"); + + double dx = xDelta * force; + double dy = yDelta * force; + Point2D fvd1 = frNodeData.getUnchecked(node1); + Point2D fvd2 = frNodeData.getUnchecked(node2); + if (v2_locked) { + // double the offset for v1, as v2 will not be moving in + // the opposite direction + fvd1.setLocation(fvd1.getX() - 2 * dx, fvd1.getY() - 2 * dy); + } else { + fvd1.setLocation(fvd1.getX() - dx, fvd1.getY() - dy); + } + if (v1_locked) { + // double the offset for v2, as v1 will not be moving in + // the opposite direction + fvd2.setLocation(fvd2.getX() + 2 * dx, fvd2.getY() + 2 * dy); + } else { + fvd2.setLocation(fvd2.getX() + dx, fvd2.getY() + dy); + } + } + + protected void calcRepulsion(N node1) { + Point2D fvd1 = frNodeData.getUnchecked(node1); + if (fvd1 == null) return; + fvd1.setLocation(0, 0); + boolean v1_locked = isLocked(node1); + + try { + for (N node2 : graph.nodes()) { - /** - * @return true once the current iteration has passed the maximum count. - */ - public boolean done() { - if (currentIteration > maxIterations || temperature < 1.0/max_dimension) { - if (!checked) - { - checked = true; - } - return true; - } - return false; + boolean v2_locked = isLocked(node2); + if (v1_locked && v2_locked) continue; + if (node1 != node2) { + Point2D p1 = apply(node1); + Point2D p2 = apply(node2); + if (p1 == null || p2 == null) continue; + double xDelta = p1.getX() - p2.getX(); + double yDelta = p1.getY() - p2.getY(); + + double deltaLength = Math.max(EPSILON, p1.distanceSq(p2)); + + double force = (repulsion_constant * repulsion_constant); // / deltaLength; + + double forceOverDeltaLength = force / deltaLength; + + Preconditions.checkState( + !Double.isNaN(force), + "Unexpected mathematical result in FRLayout:calcPositions [repulsion]"); + + if (v2_locked) { + // double the offset for v1, as v2 will not be moving in + // the opposite direction + fvd1.setLocation( + fvd1.getX() + 2 * xDelta * forceOverDeltaLength, + fvd1.getY() + 2 * yDelta * forceOverDeltaLength); + } else { + fvd1.setLocation( + fvd1.getX() + xDelta * forceOverDeltaLength, + fvd1.getY() + yDelta * forceOverDeltaLength); + } + } + } + } catch (ConcurrentModificationException cme) { + calcRepulsion(node1); + } + } + + private void cool() { + temperature *= (1.0 - currentIteration / (double) maxIterations); + } + + public void setMaxIterations(int maxIterations) { + this.maxIterations = maxIterations; + } + + /** @return true */ + public boolean isIncremental() { + return true; + } + + /** @return true once the current iteration has passed the maximum count. */ + public boolean done() { + if (currentIteration > maxIterations || temperature < 1.0 / max_dimension) { + if (!checked) { + checked = true; + } + return true; } + return false; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/ISOMLayout.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/ISOMLayout.java index acaa41f0..92eb224d 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/ISOMLayout.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/ISOMLayout.java @@ -1,227 +1,226 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.layout; -import java.awt.geom.Point2D; -import java.util.ArrayList; -import java.util.Collection; -import java.util.ConcurrentModificationException; -import java.util.List; - import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.util.RandomLocationTransformer; import edu.uci.ics.jung.algorithms.util.IterativeContext; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.List; /** - * Implements a self-organizing map layout algorithm, based on Meyer's - * self-organizing graph methods. + * Implements a self-organizing map layout algorithm, based on Meyer's self-organizing graph + * methods. * * @author Yan Biao Boey */ public class ISOMLayout extends AbstractLayout implements IterativeContext { - protected LoadingCache isomNodeData = - CacheBuilder.newBuilder().build(new CacheLoader() { - public ISOMNodeData load(N node) { - return new ISOMNodeData(); - } - }); - - private int maxEpoch; - private int epoch; - - private int radiusConstantTime; - private int radius; - private int minRadius; - - private double adaption; - private double initialAdaption; - private double minAdaption; - - private final NetworkElementAccessor elementAccessor; - - private double coolingFactor; - - private List queue = new ArrayList(); - private String status = null; - private final Network graph; - - /** - * @return the current number of epochs and execution status, as a string. - */ - public String getStatus() { - return status; - } - - public ISOMLayout(Network g) { - super(g.asGraph()); - this.elementAccessor = new RadiusNetworkElementAccessor(g, this); - this.graph = g; - } - - public void initialize() { - - setInitializer(new RandomLocationTransformer(getSize())); - maxEpoch = 2000; - epoch = 1; - - radiusConstantTime = 100; - radius = 5; - minRadius = 1; - - initialAdaption = 90.0D / 100.0D; - adaption = initialAdaption; - minAdaption = 0; - - //factor = 0; //Will be set later on - coolingFactor = 2; - - //temperature = 0.03; - //initialJumpRadius = 100; - //jumpRadius = initialJumpRadius; - - //delay = 100; - } - - - /** - * Advances the current positions of the graph elements. - */ - public void step() { - status = "epoch: " + epoch + "; "; - if (epoch < maxEpoch) { - adjust(); - updateParameters(); - status += " status: running"; - } else { - status += "adaption: " + adaption + "; "; - status += "status: done"; -// done = true; - } - } - - private synchronized void adjust() { - //Generate random position in graph space - Point2D tempXYD = new Point2D.Double(); - - // creates a new XY data location - tempXYD.setLocation(10 + Math.random() * getSize().getWidth(), - 10 + Math.random() * getSize().getHeight()); - - //Get closest node to random position - N winner = elementAccessor.getNode(tempXYD.getX(), tempXYD.getY()); - - while(true) { - try { - for(N node : nodes()) { - ISOMNodeData ivd = getISOMNodeData(node); - ivd.distance = 0; - ivd.visited = false; - } - break; - } catch(ConcurrentModificationException cme) {} + protected LoadingCache isomNodeData = + CacheBuilder.newBuilder() + .build( + new CacheLoader() { + public ISOMNodeData load(N node) { + return new ISOMNodeData(); + } + }); + + private int maxEpoch; + private int epoch; + + private int radiusConstantTime; + private int radius; + private int minRadius; + + private double adaption; + private double initialAdaption; + private double minAdaption; + + private final NetworkElementAccessor elementAccessor; + + private double coolingFactor; + + private List queue = new ArrayList(); + private String status = null; + private final Network graph; + + /** @return the current number of epochs and execution status, as a string. */ + public String getStatus() { + return status; + } + + public ISOMLayout(Network g) { + super(g.asGraph()); + this.elementAccessor = new RadiusNetworkElementAccessor(g, this); + this.graph = g; + } + + public void initialize() { + + setInitializer(new RandomLocationTransformer(getSize())); + maxEpoch = 2000; + epoch = 1; + + radiusConstantTime = 100; + radius = 5; + minRadius = 1; + + initialAdaption = 90.0D / 100.0D; + adaption = initialAdaption; + minAdaption = 0; + + //factor = 0; //Will be set later on + coolingFactor = 2; + + //temperature = 0.03; + //initialJumpRadius = 100; + //jumpRadius = initialJumpRadius; + + //delay = 100; + } + + /** Advances the current positions of the graph elements. */ + public void step() { + status = "epoch: " + epoch + "; "; + if (epoch < maxEpoch) { + adjust(); + updateParameters(); + status += " status: running"; + } else { + status += "adaption: " + adaption + "; "; + status += "status: done"; + // done = true; + } + } + + private synchronized void adjust() { + //Generate random position in graph space + Point2D tempXYD = new Point2D.Double(); + + // creates a new XY data location + tempXYD.setLocation( + 10 + Math.random() * getSize().getWidth(), 10 + Math.random() * getSize().getHeight()); + + //Get closest node to random position + N winner = elementAccessor.getNode(tempXYD.getX(), tempXYD.getY()); + + while (true) { + try { + for (N node : nodes()) { + ISOMNodeData ivd = getISOMNodeData(node); + ivd.distance = 0; + ivd.visited = false; + } + break; + } catch (ConcurrentModificationException cme) { + } + } + adjustNode(winner, tempXYD); + } + + private synchronized void updateParameters() { + epoch++; + double factor = Math.exp(-1 * coolingFactor * (1.0 * epoch / maxEpoch)); + adaption = Math.max(minAdaption, factor * initialAdaption); + //jumpRadius = (int) factor * jumpRadius; + //temperature = factor * temperature; + if ((radius > minRadius) && (epoch % radiusConstantTime == 0)) { + radius--; + } + } + + private synchronized void adjustNode(N node, Point2D tempXYD) { + queue.clear(); + ISOMNodeData ivd = getISOMNodeData(node); + ivd.distance = 0; + ivd.visited = true; + queue.add(node); + N current; + + while (!queue.isEmpty()) { + current = queue.remove(0); + ISOMNodeData currData = getISOMNodeData(current); + Point2D currXYData = apply(current); + + double dx = tempXYD.getX() - currXYData.getX(); + double dy = tempXYD.getY() - currXYData.getY(); + double factor = adaption / Math.pow(2, currData.distance); + + currXYData.setLocation(currXYData.getX() + (factor * dx), currXYData.getY() + (factor * dy)); + + if (currData.distance < radius) { + Collection s = graph.adjacentNodes(current); + while (true) { + try { + for (N child : s) { + ISOMNodeData childData = getISOMNodeData(child); + if (childData != null && !childData.visited) { + childData.visited = true; + childData.distance = currData.distance + 1; + queue.add(child); + } + } + break; + } catch (ConcurrentModificationException cme) { + } } - adjustNode(winner, tempXYD); - } - - private synchronized void updateParameters() { - epoch++; - double factor = Math.exp(-1 * coolingFactor * (1.0 * epoch / maxEpoch)); - adaption = Math.max(minAdaption, factor * initialAdaption); - //jumpRadius = (int) factor * jumpRadius; - //temperature = factor * temperature; - if ((radius > minRadius) && (epoch % radiusConstantTime == 0)) { - radius--; - } - } - - private synchronized void adjustNode(N node, Point2D tempXYD) { - queue.clear(); - ISOMNodeData ivd = getISOMNodeData(node); - ivd.distance = 0; - ivd.visited = true; - queue.add(node); - N current; - - while (!queue.isEmpty()) { - current = queue.remove(0); - ISOMNodeData currData = getISOMNodeData(current); - Point2D currXYData = apply(current); - - double dx = tempXYD.getX() - currXYData.getX(); - double dy = tempXYD.getY() - currXYData.getY(); - double factor = adaption / Math.pow(2, currData.distance); - - currXYData.setLocation(currXYData.getX()+(factor*dx), currXYData.getY()+(factor*dy)); - - if (currData.distance < radius) { - Collection s = graph.adjacentNodes(current); - while(true) { - try { - for(N child : s) { - ISOMNodeData childData = getISOMNodeData(child); - if (childData != null && !childData.visited) { - childData.visited = true; - childData.distance = currData.distance + 1; - queue.add(child); - } - } - break; - } catch(ConcurrentModificationException cme) {} - } - } - } - } - - protected ISOMNodeData getISOMNodeData(N node) { - return isomNodeData.getUnchecked(node); - } - - /** - * This one is an incremental visualization. - * @return true is the layout algorithm is incremental, false otherwise - */ - public boolean isIncremental() { - return true; - } - - /** - * Returns true if the node positions are no longer being - * updated. Currently ISOMLayout stops updating node - * positions after a certain number of iterations have taken place. - * @return true if the node position updates have stopped, - * false otherwise - */ - public boolean done() { - return epoch >= maxEpoch; - } - - protected static class ISOMNodeData { - int distance; - boolean visited; - - protected ISOMNodeData() { - distance = 0; - visited = false; - } - } - - /** - * Resets the layout iteration count to 0, which allows the layout algorithm to - * continue updating node positions. - */ - public void reset() { - epoch = 0; - } -} \ No newline at end of file + } + } + } + + protected ISOMNodeData getISOMNodeData(N node) { + return isomNodeData.getUnchecked(node); + } + + /** + * This one is an incremental visualization. + * + * @return true is the layout algorithm is incremental, false otherwise + */ + public boolean isIncremental() { + return true; + } + + /** + * Returns true if the node positions are no longer being updated. Currently + * ISOMLayout stops updating node positions after a certain number of iterations have taken + * place. + * + * @return true if the node position updates have stopped, false + * otherwise + */ + public boolean done() { + return epoch >= maxEpoch; + } + + protected static class ISOMNodeData { + int distance; + boolean visited; + + protected ISOMNodeData() { + distance = 0; + visited = false; + } + } + + /** + * Resets the layout iteration count to 0, which allows the layout algorithm to continue updating + * node positions. + */ + public void reset() { + epoch = 0; + } +} diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/KKLayout.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/KKLayout.java index 448ea45b..0e36379b 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/KKLayout.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/KKLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, The JUNG Authors + * Copyright (c) 2003, The JUNG Authors * * All rights reserved. * @@ -13,410 +13,378 @@ * https://github.com/jrtom/jung/blob/master/LICENSE for a description. */ -import java.awt.Dimension; -import java.awt.geom.Point2D; -import java.util.ConcurrentModificationException; -import java.util.function.BiFunction; - import com.google.common.graph.Graph; - import edu.uci.ics.jung.algorithms.layout.util.RandomLocationTransformer; import edu.uci.ics.jung.algorithms.shortestpath.Distance; import edu.uci.ics.jung.algorithms.shortestpath.DistanceStatistics; import edu.uci.ics.jung.algorithms.shortestpath.UnweightedShortestPath; import edu.uci.ics.jung.algorithms.util.IterativeContext; +import java.awt.Dimension; +import java.awt.geom.Point2D; +import java.util.ConcurrentModificationException; +import java.util.function.BiFunction; /** - * Implements the Kamada-Kawai algorithm for node layout. - * Does not respect filter calls, and sometimes crashes when the view changes to it. - * - * @see "Tomihisa Kamada and Satoru Kawai: An algorithm for drawing general indirect graphs. Information Processing Letters 31(1):7-15, 1989" - * @see "Tomihisa Kamada: On visualization of abstract objects and relations. Ph.D. dissertation, Dept. of Information Science, Univ. of Tokyo, Dec. 1988." + * Implements the Kamada-Kawai algorithm for node layout. Does not respect filter calls, and + * sometimes crashes when the view changes to it. * + * @see "Tomihisa Kamada and Satoru Kawai: An algorithm for drawing general indirect graphs. + * Information Processing Letters 31(1):7-15, 1989" + * @see "Tomihisa Kamada: On visualization of abstract objects and relations. Ph.D. dissertation, + * Dept. of Information Science, Univ. of Tokyo, Dec. 1988." * @author Masanori Harada */ public class KKLayout extends AbstractLayout implements IterativeContext { - private double EPSILON = 0.1d; - - private int currentIteration; - private int maxIterations = 2000; - private String status = "KKLayout"; - - private double L; // the ideal length of an edge - private double K = 1; // arbitrary const number - private double[][] dm; // distance matrix - - private boolean adjustForGravity = true; - private boolean exchangenodes = true; - - private N[] nodes; - private Point2D[] xydata; - - private final Graph graph; - - /** - * Retrieves graph distances between nodes of the visible graph - */ - protected BiFunction distance; - - /** - * The diameter of the visible graph. In other words, the maximum over all pairs - * of nodes of the length of the shortest path between a and bf the visible graph. - */ - protected double diameter; - - /** - * A multiplicative factor which partly specifies the "preferred" length of an edge (L). - */ - private double length_factor = 0.9; - - /** - * A multiplicative factor which specifies the fraction of the graph's diameter to be - * used as the inter-node distance between disconnected nodes. - */ - private double disconnected_multiplier = 0.5; - - public KKLayout(Graph g) - { - this(g, new UnweightedShortestPath(g)); - } - - /** - * Creates an instance for the specified graph and distance metric. - * @param g the graph on which the layout algorithm is to operate - * @param distance specifies the distance between pairs of nodes - */ - public KKLayout(Graph g, Distance distance){ - super(g); - this.graph = g; - this.distance = (x, y) -> distance.getDistance(x, y); + private double EPSILON = 0.1d; + + private int currentIteration; + private int maxIterations = 2000; + private String status = "KKLayout"; + + private double L; // the ideal length of an edge + private double K = 1; // arbitrary const number + private double[][] dm; // distance matrix + + private boolean adjustForGravity = true; + private boolean exchangenodes = true; + + private N[] nodes; + private Point2D[] xydata; + + private final Graph graph; + + /** Retrieves graph distances between nodes of the visible graph */ + protected BiFunction distance; + + /** + * The diameter of the visible graph. In other words, the maximum over all pairs of nodes of the + * length of the shortest path between a and bf the visible graph. + */ + protected double diameter; + + /** A multiplicative factor which partly specifies the "preferred" length of an edge (L). */ + private double length_factor = 0.9; + + /** + * A multiplicative factor which specifies the fraction of the graph's diameter to be used as the + * inter-node distance between disconnected nodes. + */ + private double disconnected_multiplier = 0.5; + + public KKLayout(Graph g) { + this(g, new UnweightedShortestPath(g)); + } + + /** + * Creates an instance for the specified graph and distance metric. + * + * @param g the graph on which the layout algorithm is to operate + * @param distance specifies the distance between pairs of nodes + */ + public KKLayout(Graph g, Distance distance) { + super(g); + this.graph = g; + this.distance = (x, y) -> distance.getDistance(x, y); + } + + /** + * @param length_factor a multiplicative factor which partially specifies the preferred length of + * an edge + */ + public void setLengthFactor(double length_factor) { + this.length_factor = length_factor; + } + + /** + * @param disconnected_multiplier a multiplicative factor that specifies the fraction of the + * graph's diameter to be used as the inter-node distance between disconnected nodes + */ + public void setDisconnectedDistanceMultiplier(double disconnected_multiplier) { + this.disconnected_multiplier = disconnected_multiplier; + } + + /** @return a string with information about the current status of the algorithm. */ + public String getStatus() { + return status + this.getSize(); + } + + public void setMaxIterations(int maxIterations) { + this.maxIterations = maxIterations; + } + + /** @return true */ + public boolean isIncremental() { + return true; + } + + /** @return true if the current iteration has passed the maximum count. */ + public boolean done() { + if (currentIteration > maxIterations) { + return true; } - - /** - * @param length_factor a multiplicative factor which partially specifies - * the preferred length of an edge - */ - public void setLengthFactor(double length_factor){ - this.length_factor = length_factor; + return false; + } + + @SuppressWarnings("unchecked") + public void initialize() { + currentIteration = 0; + + if (graph != null && size != null) { + + double height = size.getHeight(); + double width = size.getWidth(); + + int n = graph.nodes().size(); + dm = new double[n][n]; + nodes = (N[]) graph.nodes().toArray(); + xydata = new Point2D[n]; + + // assign IDs to all visible nodes + while (true) { + try { + int index = 0; + for (N node : graph.nodes()) { + Point2D xyd = apply(node); + nodes[index] = node; + xydata[index] = xyd; + index++; + } + break; + } catch (ConcurrentModificationException cme) { + } + } + + diameter = DistanceStatistics.diameter(graph, distance, true); + + double L0 = Math.min(height, width); + L = (L0 / diameter) * length_factor; // length_factor used to be hardcoded to 0.9 + //L = 0.75 * Math.sqrt(height * width / n); + + for (int i = 0; i < n - 1; i++) { + for (int j = i + 1; j < n; j++) { + Number d_ij = distance.apply(nodes[i], nodes[j]); + Number d_ji = distance.apply(nodes[j], nodes[i]); + double dist = diameter * disconnected_multiplier; + if (d_ij != null) dist = Math.min(d_ij.doubleValue(), dist); + if (d_ji != null) dist = Math.min(d_ji.doubleValue(), dist); + dm[i][j] = dm[j][i] = dist; + } + } + } + } + + public void step() { + try { + currentIteration++; + double energy = calcEnergy(); + status = + "Kamada-Kawai N=" + + graph.nodes().size() + + "(" + + graph.nodes().size() + + ")" + + " IT: " + + currentIteration + + " E=" + + energy; + + int n = graph.nodes().size(); + if (n == 0) return; + + double maxDeltaM = 0; + int pm = -1; // the node having max deltaM + for (int i = 0; i < n; i++) { + if (isLocked(nodes[i])) continue; + double deltam = calcDeltaM(i); + + if (maxDeltaM < deltam) { + maxDeltaM = deltam; + pm = i; + } + } + if (pm == -1) return; + + for (int i = 0; i < 100; i++) { + double[] dxy = calcDeltaXY(pm); + xydata[pm].setLocation(xydata[pm].getX() + dxy[0], xydata[pm].getY() + dxy[1]); + + double deltam = calcDeltaM(pm); + if (deltam < EPSILON) break; + } + + if (adjustForGravity) adjustForGravity(); + + if (exchangenodes && maxDeltaM < EPSILON) { + energy = calcEnergy(); + for (int i = 0; i < n - 1; i++) { + if (isLocked(nodes[i])) continue; + for (int j = i + 1; j < n; j++) { + if (isLocked(nodes[j])) continue; + double xenergy = calcEnergyIfExchanged(i, j); + if (energy > xenergy) { + double sx = xydata[i].getX(); + double sy = xydata[i].getY(); + xydata[i].setLocation(xydata[j]); + xydata[j].setLocation(sx, sy); + return; + } + } + } + } + } finally { + // fireStateChanged(); + } + } + + /** Shift all nodes so that the center of gravity is located at the center of the screen. */ + public void adjustForGravity() { + Dimension d = getSize(); + double height = d.getHeight(); + double width = d.getWidth(); + double gx = 0; + double gy = 0; + for (int i = 0; i < xydata.length; i++) { + gx += xydata[i].getX(); + gy += xydata[i].getY(); + } + gx /= xydata.length; + gy /= xydata.length; + double diffx = width / 2 - gx; + double diffy = height / 2 - gy; + for (int i = 0; i < xydata.length; i++) { + xydata[i].setLocation(xydata[i].getX() + diffx, xydata[i].getY() + diffy); + } + } + + @Override + public void setSize(Dimension size) { + if (initialized == false) setInitializer(new RandomLocationTransformer(size)); + super.setSize(size); + } + + public void setAdjustForGravity(boolean on) { + adjustForGravity = on; + } + + public boolean getAdjustForGravity() { + return adjustForGravity; + } + + /** + * Enable or disable the local minimum escape technique by exchanging nodes. + * + * @param on iff the local minimum escape technique is to be enabled + */ + public void setExchangenodes(boolean on) { + exchangenodes = on; + } + + public boolean getExchangenodes() { + return exchangenodes; + } + + /** Determines a step to new position of the node m. */ + private double[] calcDeltaXY(int m) { + double dE_dxm = 0; + double dE_dym = 0; + double d2E_d2xm = 0; + double d2E_dxmdym = 0; + double d2E_dymdxm = 0; + double d2E_d2ym = 0; + + for (int i = 0; i < nodes.length; i++) { + if (i != m) { + + double dist = dm[m][i]; + double l_mi = L * dist; + double k_mi = K / (dist * dist); + double dx = xydata[m].getX() - xydata[i].getX(); + double dy = xydata[m].getY() - xydata[i].getY(); + double d = Math.sqrt(dx * dx + dy * dy); + double ddd = d * d * d; + + dE_dxm += k_mi * (1 - l_mi / d) * dx; + dE_dym += k_mi * (1 - l_mi / d) * dy; + d2E_d2xm += k_mi * (1 - l_mi * dy * dy / ddd); + d2E_dxmdym += k_mi * l_mi * dx * dy / ddd; + d2E_d2ym += k_mi * (1 - l_mi * dx * dx / ddd); + } + } + // d2E_dymdxm equals to d2E_dxmdym. + d2E_dymdxm = d2E_dxmdym; + + double denomi = d2E_d2xm * d2E_d2ym - d2E_dxmdym * d2E_dymdxm; + double deltaX = (d2E_dxmdym * dE_dym - d2E_d2ym * dE_dxm) / denomi; + double deltaY = (d2E_dymdxm * dE_dxm - d2E_d2xm * dE_dym) / denomi; + return new double[] {deltaX, deltaY}; + } + + /** Calculates the gradient of energy function at the node m. */ + private double calcDeltaM(int m) { + double dEdxm = 0; + double dEdym = 0; + for (int i = 0; i < nodes.length; i++) { + if (i != m) { + double dist = dm[m][i]; + double l_mi = L * dist; + double k_mi = K / (dist * dist); + + double dx = xydata[m].getX() - xydata[i].getX(); + double dy = xydata[m].getY() - xydata[i].getY(); + double d = Math.sqrt(dx * dx + dy * dy); + + double common = k_mi * (1 - l_mi / d); + dEdxm += common * dx; + dEdym += common * dy; + } } - - /** - * @param disconnected_multiplier a multiplicative factor that specifies the fraction of the - * graph's diameter to be used as the inter-node distance between disconnected nodes - */ - public void setDisconnectedDistanceMultiplier(double disconnected_multiplier){ - this.disconnected_multiplier = disconnected_multiplier; + return Math.sqrt(dEdxm * dEdxm + dEdym * dEdym); + } + + /** Calculates the energy function E. */ + private double calcEnergy() { + double energy = 0; + for (int i = 0; i < nodes.length - 1; i++) { + for (int j = i + 1; j < nodes.length; j++) { + double dist = dm[i][j]; + double l_ij = L * dist; + double k_ij = K / (dist * dist); + double dx = xydata[i].getX() - xydata[j].getX(); + double dy = xydata[i].getY() - xydata[j].getY(); + double d = Math.sqrt(dx * dx + dy * dy); + + energy += k_ij / 2 * (dx * dx + dy * dy + l_ij * l_ij - 2 * l_ij * d); + } } - - /** - * @return a string with information about the current status of the algorithm. - */ - public String getStatus() { - return status + this.getSize(); - } - - public void setMaxIterations(int maxIterations) { - this.maxIterations = maxIterations; + return energy; + } + + /** Calculates the energy function E as if positions of the specified nodes are exchanged. */ + private double calcEnergyIfExchanged(int p, int q) { + if (p >= q) throw new RuntimeException("p should be < q"); + double energy = 0; // < 0 + for (int i = 0; i < nodes.length - 1; i++) { + for (int j = i + 1; j < nodes.length; j++) { + int ii = i; + int jj = j; + if (i == p) ii = q; + if (j == q) jj = p; + + double dist = dm[i][j]; + double l_ij = L * dist; + double k_ij = K / (dist * dist); + double dx = xydata[ii].getX() - xydata[jj].getX(); + double dy = xydata[ii].getY() - xydata[jj].getY(); + double d = Math.sqrt(dx * dx + dy * dy); + + energy += k_ij / 2 * (dx * dx + dy * dy + l_ij * l_ij - 2 * l_ij * d); + } } + return energy; + } - /** - * @return true - */ - public boolean isIncremental() { - return true; - } - - /** - * @return true if the current iteration has passed the maximum count. - */ - public boolean done() { - if (currentIteration > maxIterations) { - return true; - } - return false; - } - - @SuppressWarnings("unchecked") - public void initialize() { - currentIteration = 0; - - if(graph != null && size != null) { - - double height = size.getHeight(); - double width = size.getWidth(); - - int n = graph.nodes().size(); - dm = new double[n][n]; - nodes = (N[])graph.nodes().toArray(); - xydata = new Point2D[n]; - - // assign IDs to all visible nodes - while(true) { - try { - int index = 0; - for(N node : graph.nodes()) { - Point2D xyd = apply(node); - nodes[index] = node; - xydata[index] = xyd; - index++; - } - break; - } catch(ConcurrentModificationException cme) {} - } - - diameter = DistanceStatistics.diameter(graph, distance, true); - - double L0 = Math.min(height, width); - L = (L0 / diameter) * length_factor; // length_factor used to be hardcoded to 0.9 - //L = 0.75 * Math.sqrt(height * width / n); - - for (int i = 0; i < n - 1; i++) { - for (int j = i + 1; j < n; j++) { - Number d_ij = distance.apply(nodes[i], nodes[j]); - Number d_ji = distance.apply(nodes[j], nodes[i]); - double dist = diameter * disconnected_multiplier; - if (d_ij != null) - dist = Math.min(d_ij.doubleValue(), dist); - if (d_ji != null) - dist = Math.min(d_ji.doubleValue(), dist); - dm[i][j] = dm[j][i] = dist; - } - } - } - } - - public void step() { - try { - currentIteration++; - double energy = calcEnergy(); - status = "Kamada-Kawai N=" + graph.nodes().size() - + "(" + graph.nodes().size() + ")" - + " IT: " + currentIteration - + " E=" + energy - ; - - int n = graph.nodes().size(); - if (n == 0) - return; - - double maxDeltaM = 0; - int pm = -1; // the node having max deltaM - for (int i = 0; i < n; i++) { - if (isLocked(nodes[i])) - continue; - double deltam = calcDeltaM(i); - - if (maxDeltaM < deltam) { - maxDeltaM = deltam; - pm = i; - } - } - if (pm == -1) - return; - - for (int i = 0; i < 100; i++) { - double[] dxy = calcDeltaXY(pm); - xydata[pm].setLocation(xydata[pm].getX()+dxy[0], xydata[pm].getY()+dxy[1]); - - double deltam = calcDeltaM(pm); - if (deltam < EPSILON) - break; - } - - if (adjustForGravity) - adjustForGravity(); - - if (exchangenodes && maxDeltaM < EPSILON) { - energy = calcEnergy(); - for (int i = 0; i < n - 1; i++) { - if (isLocked(nodes[i])) - continue; - for (int j = i + 1; j < n; j++) { - if (isLocked(nodes[j])) - continue; - double xenergy = calcEnergyIfExchanged(i, j); - if (energy > xenergy) { - double sx = xydata[i].getX(); - double sy = xydata[i].getY(); - xydata[i].setLocation(xydata[j]); - xydata[j].setLocation(sx, sy); - return; - } - } - } - } - } - finally { -// fireStateChanged(); - } - } - - /** - * Shift all nodes so that the center of gravity is located at - * the center of the screen. - */ - public void adjustForGravity() { - Dimension d = getSize(); - double height = d.getHeight(); - double width = d.getWidth(); - double gx = 0; - double gy = 0; - for (int i = 0; i < xydata.length; i++) { - gx += xydata[i].getX(); - gy += xydata[i].getY(); - } - gx /= xydata.length; - gy /= xydata.length; - double diffx = width / 2 - gx; - double diffy = height / 2 - gy; - for (int i = 0; i < xydata.length; i++) { - xydata[i].setLocation(xydata[i].getX()+diffx, xydata[i].getY()+diffy); - } - } - - @Override - public void setSize(Dimension size) { - if(initialized == false) - setInitializer(new RandomLocationTransformer(size)); - super.setSize(size); - } - - public void setAdjustForGravity(boolean on) { - adjustForGravity = on; - } - - public boolean getAdjustForGravity() { - return adjustForGravity; - } - - /** - * Enable or disable the local minimum escape technique by - * exchanging nodes. - * @param on iff the local minimum escape technique is to be enabled - */ - public void setExchangenodes(boolean on) { - exchangenodes = on; - } - - public boolean getExchangenodes() { - return exchangenodes; - } - - /** - * Determines a step to new position of the node m. - */ - private double[] calcDeltaXY(int m) { - double dE_dxm = 0; - double dE_dym = 0; - double d2E_d2xm = 0; - double d2E_dxmdym = 0; - double d2E_dymdxm = 0; - double d2E_d2ym = 0; - - for (int i = 0; i < nodes.length; i++) { - if (i != m) { - - double dist = dm[m][i]; - double l_mi = L * dist; - double k_mi = K / (dist * dist); - double dx = xydata[m].getX() - xydata[i].getX(); - double dy = xydata[m].getY() - xydata[i].getY(); - double d = Math.sqrt(dx * dx + dy * dy); - double ddd = d * d * d; - - dE_dxm += k_mi * (1 - l_mi / d) * dx; - dE_dym += k_mi * (1 - l_mi / d) * dy; - d2E_d2xm += k_mi * (1 - l_mi * dy * dy / ddd); - d2E_dxmdym += k_mi * l_mi * dx * dy / ddd; - d2E_d2ym += k_mi * (1 - l_mi * dx * dx / ddd); - } - } - // d2E_dymdxm equals to d2E_dxmdym. - d2E_dymdxm = d2E_dxmdym; - - double denomi = d2E_d2xm * d2E_d2ym - d2E_dxmdym * d2E_dymdxm; - double deltaX = (d2E_dxmdym * dE_dym - d2E_d2ym * dE_dxm) / denomi; - double deltaY = (d2E_dymdxm * dE_dxm - d2E_d2xm * dE_dym) / denomi; - return new double[]{deltaX, deltaY}; - } - - /** - * Calculates the gradient of energy function at the node m. - */ - private double calcDeltaM(int m) { - double dEdxm = 0; - double dEdym = 0; - for (int i = 0; i < nodes.length; i++) { - if (i != m) { - double dist = dm[m][i]; - double l_mi = L * dist; - double k_mi = K / (dist * dist); - - double dx = xydata[m].getX() - xydata[i].getX(); - double dy = xydata[m].getY() - xydata[i].getY(); - double d = Math.sqrt(dx * dx + dy * dy); - - double common = k_mi * (1 - l_mi / d); - dEdxm += common * dx; - dEdym += common * dy; - } - } - return Math.sqrt(dEdxm * dEdxm + dEdym * dEdym); - } - - /** - * Calculates the energy function E. - */ - private double calcEnergy() { - double energy = 0; - for (int i = 0; i < nodes.length - 1; i++) { - for (int j = i + 1; j < nodes.length; j++) { - double dist = dm[i][j]; - double l_ij = L * dist; - double k_ij = K / (dist * dist); - double dx = xydata[i].getX() - xydata[j].getX(); - double dy = xydata[i].getY() - xydata[j].getY(); - double d = Math.sqrt(dx * dx + dy * dy); - - - energy += k_ij / 2 * (dx * dx + dy * dy + l_ij * l_ij - - 2 * l_ij * d); - } - } - return energy; - } - - /** - * Calculates the energy function E as if positions of the - * specified nodes are exchanged. - */ - private double calcEnergyIfExchanged(int p, int q) { - if (p >= q) - throw new RuntimeException("p should be < q"); - double energy = 0; // < 0 - for (int i = 0; i < nodes.length - 1; i++) { - for (int j = i + 1; j < nodes.length; j++) { - int ii = i; - int jj = j; - if (i == p) ii = q; - if (j == q) jj = p; - - double dist = dm[i][j]; - double l_ij = L * dist; - double k_ij = K / (dist * dist); - double dx = xydata[ii].getX() - xydata[jj].getX(); - double dy = xydata[ii].getY() - xydata[jj].getY(); - double d = Math.sqrt(dx * dx + dy * dy); - - energy += k_ij / 2 * (dx * dx + dy * dy + l_ij * l_ij - - 2 * l_ij * d); - } - } - return energy; - } - - public void reset() { - currentIteration = 0; - } + public void reset() { + currentIteration = 0; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/Layout.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/Layout.java index 0e5785a4..d59e6843 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/Layout.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/Layout.java @@ -1,79 +1,69 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.layout; +import com.google.common.base.Function; import java.awt.Dimension; import java.awt.geom.Point2D; import java.util.Set; -import com.google.common.base.Function; - /** - * A generalized interface for mechanisms that associate (x,y) coordinates - * with nodes. + * A generalized interface for mechanisms that associate (x,y) coordinates with nodes. + * *

+ * * @author danyelf * @author tom nelson */ public interface Layout extends Function { - /** - * Initializes fields in the node that may not have - * been set during the constructor. Must be called before - * the iterations begin. - */ - void initialize(); - - /** - * @param initializer a function that specifies initial locations for all nodes - */ - void setInitializer(Function initializer); - - /** - * @return the set of nodes for which this Layout assigns positions - */ - Set nodes(); - - void reset(); + /** + * Initializes fields in the node that may not have been set during the constructor. Must be + * called before the iterations begin. + */ + void initialize(); + + /** @param initializer a function that specifies initial locations for all nodes */ + void setInitializer(Function initializer); + + /** @return the set of nodes for which this Layout assigns positions */ + Set nodes(); + + void reset(); - /** - * @param d the space to use to lay out this graph - */ - void setSize(Dimension d); - - /** - * @return the current size of the visualization's space - */ - Dimension getSize(); + /** @param d the space to use to lay out this graph */ + void setSize(Dimension d); + /** @return the current size of the visualization's space */ + Dimension getSize(); - /** - * Locks or unlocks the specified node. Locking the node fixes it at its current position, - * so that it will not be affected by the layout algorithm. Unlocking it allows the layout - * algorithm to change the node's position. - * - * @param v the node to lock/unlock - * @param state {@code true} to lock the node, {@code false} to unlock it - */ - void lock(N n, boolean state); + /** + * Locks or unlocks the specified node. Locking the node fixes it at its current position, so that + * it will not be affected by the layout algorithm. Unlocking it allows the layout algorithm to + * change the node's position. + * + * @param v the node to lock/unlock + * @param state {@code true} to lock the node, {@code false} to unlock it + */ + void lock(N n, boolean state); - /** - * @param v the node whose locked state is being queried - * @return true if the position of node v is locked - */ - boolean isLocked(N n); + /** + * @param v the node whose locked state is being queried + * @return true if the position of node v is locked + */ + boolean isLocked(N n); - /** - * Changes the layout coordinates of {@code node} to {@code location}. - * @param v the node whose location is to be specified - * @param location the coordinates of the specified location - */ - void setLocation(N n, Point2D location); - + /** + * Changes the layout coordinates of {@code node} to {@code location}. + * + * @param v the node whose location is to be specified + * @param location the coordinates of the specified location + */ + void setLocation(N n, Point2D location); } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/LayoutDecorator.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/LayoutDecorator.java index 390895a1..8038f6a1 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/LayoutDecorator.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/LayoutDecorator.java @@ -10,89 +10,85 @@ package edu.uci.ics.jung.algorithms.layout; -import java.awt.Dimension; -import java.awt.geom.Point2D; - import com.google.common.base.Function; - import edu.uci.ics.jung.algorithms.util.IterativeContext; +import java.awt.Dimension; +import java.awt.geom.Point2D; /** - * a pure decorator for the Layout interface. Intended to be overridden - * to provide specific behavior decoration - * - * @author Tom Nelson + * a pure decorator for the Layout interface. Intended to be overridden to provide specific behavior + * decoration * + * @author Tom Nelson */ public abstract class LayoutDecorator implements Layout, IterativeContext { - - protected Layout delegate; - - /** - * Creates an instance backed by the specified {@code delegate}. - * @param delegate the layout to which this instance is delegating - */ - public LayoutDecorator(Layout delegate) { - this.delegate = delegate; - } - - /** - * @return the backing (delegate) layout. - */ - public Layout getDelegate() { - return delegate; - } - public void setDelegate(Layout delegate) { - this.delegate = delegate; + protected Layout delegate; + + /** + * Creates an instance backed by the specified {@code delegate}. + * + * @param delegate the layout to which this instance is delegating + */ + public LayoutDecorator(Layout delegate) { + this.delegate = delegate; + } + + /** @return the backing (delegate) layout. */ + public Layout getDelegate() { + return delegate; + } + + public void setDelegate(Layout delegate) { + this.delegate = delegate; + } + + public void step() { + if (delegate instanceof IterativeContext) { + ((IterativeContext) delegate).step(); } + } - public void step() { - if(delegate instanceof IterativeContext) { - ((IterativeContext)delegate).step(); - } - } + public void initialize() { + delegate.initialize(); + } - public void initialize() { - delegate.initialize(); - } + public void setInitializer(Function initializer) { + delegate.setInitializer(initializer); + } - public void setInitializer(Function initializer) { - delegate.setInitializer(initializer); - } + public void setLocation(N node, Point2D location) { + delegate.setLocation(node, location); + } - public void setLocation(N node, Point2D location) { - delegate.setLocation(node, location); - } + public Dimension getSize() { + return delegate.getSize(); + } - public Dimension getSize() { - return delegate.getSize(); - } + public Point2D transform(N node) { + return delegate.apply(node); + } - public Point2D transform(N node) { - return delegate.apply(node); + public boolean done() { + if (delegate instanceof IterativeContext) { + return ((IterativeContext) delegate).done(); } + return true; + } - public boolean done() { - if(delegate instanceof IterativeContext) { - return ((IterativeContext)delegate).done(); - } - return true; - } + public void lock(N node, boolean state) { + delegate.lock(node, state); + } - public void lock(N node, boolean state) { - delegate.lock(node, state); - } + public boolean isLocked(N node) { + return delegate.isLocked(node); + } - public boolean isLocked(N node) { - return delegate.isLocked(node); - } - - public void setSize(Dimension d) { - delegate.setSize(d); - } + public void setSize(Dimension d) { + delegate.setSize(d); + } - public void reset() { - delegate.reset(); - } + public void reset() { + delegate.reset(); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/NetworkElementAccessor.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/NetworkElementAccessor.java index ab3b6882..43efd355 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/NetworkElementAccessor.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/NetworkElementAccessor.java @@ -1,10 +1,10 @@ /* * Copyright (c) 2005, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * * * Created on Apr 12, 2005 */ @@ -15,6 +15,7 @@ /** * Interface for coordinate-based selection of graph components. + * * @author Tom Nelson * @author Joshua O'Madadhain */ @@ -22,29 +23,28 @@ // TODO: consider splitting node-only methods out into a separate Graph-centric interface // (and figure out how visualization is supposed to work for Graphs vs. Networks) -public interface NetworkElementAccessor -{ - /** - * Returns the node, if any, associated with (x, y). - * @param x the x coordinate of the pick point - * @param y the y coordinate of the pick point - * - * @return the node associated with (x, y) - */ - N getNode(double x, double y); - - /** - * @param rectangle the region in which the returned nodes are located - * @return the nodes whose locations given by {@code layout} - * are contained within {@code rectangle} - */ - Collection getNodes(Shape rectangle); +public interface NetworkElementAccessor { + /** + * Returns the node, if any, associated with (x, y). + * + * @param x the x coordinate of the pick point + * @param y the y coordinate of the pick point + * @return the node associated with (x, y) + */ + N getNode(double x, double y); + + /** + * @param rectangle the region in which the returned nodes are located + * @return the nodes whose locations given by {@code layout} are contained within {@code + * rectangle} + */ + Collection getNodes(Shape rectangle); - /** - * @param x the x coordinate of the location - * @param y the y coordinate of the location - * @return an edge which is associated with the location {@code (x,y)} - * as given by {@code layout}, generally by reference to the edge's endpoints - */ - E getEdge(double x, double y); -} \ No newline at end of file + /** + * @param x the x coordinate of the location + * @param y the y coordinate of the location + * @return an edge which is associated with the location {@code (x,y)} as given by {@code layout}, + * generally by reference to the edge's endpoints + */ + E getEdge(double x, double y); +} diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/PolarPoint.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/PolarPoint.java index bd7d1b6f..a5e2ab24 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/PolarPoint.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/PolarPoint.java @@ -1,106 +1,108 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.layout; import java.awt.geom.Point2D; /** - * Represents a point in polar coordinates: distance and angle from the origin. - * Includes conversions between polar and Cartesian - * coordinates (Point2D). - * + * Represents a point in polar coordinates: distance and angle from the origin. Includes conversions + * between polar and Cartesian coordinates (Point2D). + * * @author Tom Nelson - tomnelson@dev.java.net */ -public class PolarPoint -{ - double theta; - double radius; - - /** - * Creates a new instance with radius and angle each 0. - */ - public PolarPoint() { - this(0,0); - } +public class PolarPoint { + double theta; + double radius; + + /** Creates a new instance with radius and angle each 0. */ + public PolarPoint() { + this(0, 0); + } + + /** + * Creates a new instance with the specified radius and angle. + * + * @param theta the angle of the point to create + * @param radius the distance from the origin of the point to create + */ + public PolarPoint(double theta, double radius) { + this.theta = theta; + this.radius = radius; + } + + /** @return the angle for this point */ + public double getTheta() { + return theta; + } + + /** @return the radius for this point */ + public double getRadius() { + return radius; + } + + public void setTheta(double theta) { + this.theta = theta; + } + + public void setRadius(double radius) { + this.radius = radius; + } - /** - * Creates a new instance with the specified radius and angle. - * @param theta the angle of the point to create - * @param radius the distance from the origin of the point to create - */ - public PolarPoint(double theta, double radius) { - this.theta = theta; - this.radius = radius; - } - - /** - * @return the angle for this point - */ - public double getTheta() { return theta; } + /** + * @param polar the input location to convert + * @return the result of converting polar to Cartesian coordinates. + */ + public static Point2D polarToCartesian(PolarPoint polar) { + return polarToCartesian(polar.getTheta(), polar.getRadius()); + } - /** - * @return the radius for this point - */ - public double getRadius() { return radius; } - - public void setTheta(double theta) { this.theta = theta; } - - public void setRadius(double radius) { this.radius = radius; } + /** + * @param theta the angle of the input location + * @param radius the distance from the origin of the input location + * @return the result of converting (theta, radius) to Cartesian coordinates. + */ + public static Point2D polarToCartesian(double theta, double radius) { + return new Point2D.Double(radius * Math.cos(theta), radius * Math.sin(theta)); + } - /** - * @param polar the input location to convert - * @return the result of converting polar to Cartesian coordinates. - */ - public static Point2D polarToCartesian(PolarPoint polar) { - return polarToCartesian(polar.getTheta(), polar.getRadius()); - } + /** + * @param point the input location + * @return the result of converting point to polar coordinates. + */ + public static PolarPoint cartesianToPolar(Point2D point) { + return cartesianToPolar(point.getX(), point.getY()); + } - /** - * @param theta the angle of the input location - * @param radius the distance from the origin of the input location - * @return the result of converting (theta, radius) to Cartesian coordinates. - */ - public static Point2D polarToCartesian(double theta, double radius) { - return new Point2D.Double(radius*Math.cos(theta), radius*Math.sin(theta)); - } + /** + * @param x the x coordinate of the input location + * @param y the y coordinate of the input location + * @return the result of converting (x, y) to polar coordinates. + */ + public static PolarPoint cartesianToPolar(double x, double y) { + double theta = Math.atan2(y, x); + double radius = Math.sqrt(x * x + y * y); + return new PolarPoint(theta, radius); + } - /** - * @param point the input location - * @return the result of converting point to polar coordinates. - */ - public static PolarPoint cartesianToPolar(Point2D point) { - return cartesianToPolar(point.getX(), point.getY()); - } + @Override + public String toString() { + return "PolarPoint[" + radius + "," + theta + "]"; + } - /** - * @param x the x coordinate of the input location - * @param y the y coordinate of the input location - * @return the result of converting (x, y) to polar coordinates. - */ - public static PolarPoint cartesianToPolar(double x, double y) { - double theta = Math.atan2(y,x); - double radius = Math.sqrt(x*x+y*y); - return new PolarPoint(theta, radius); - } - - @Override - public String toString() { - return "PolarPoint[" + radius + "," + theta +"]"; - } - - /** - * Sets the angle and radius of this point to those of {@code p}. - * @param p the point whose location is copied into this instance - */ - public void setLocation(PolarPoint p) { - this.theta = p.getTheta(); - this.radius = p.getRadius(); - } -} \ No newline at end of file + /** + * Sets the angle and radius of this point to those of {@code p}. + * + * @param p the point whose location is copied into this instance + */ + public void setLocation(PolarPoint p) { + this.theta = p.getTheta(); + this.radius = p.getRadius(); + } +} diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/RadialTreeLayout.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/RadialTreeLayout.java index 7920d882..82a99406 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/RadialTreeLayout.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/RadialTreeLayout.java @@ -9,108 +9,101 @@ */ package edu.uci.ics.jung.algorithms.layout; + +import com.google.common.graph.Graph; import java.awt.Dimension; import java.awt.geom.Point2D; import java.util.HashMap; import java.util.Map; -import com.google.common.graph.Graph; - /** * A radial layout for Tree or Forest graphs. - * - * @author Tom Nelson - * + * + * @author Tom Nelson */ public class RadialTreeLayout extends TreeLayout { - protected Map polarLocations; + protected Map polarLocations; - public RadialTreeLayout(Graph g) { - this(g, DEFAULT_DISTX, DEFAULT_DISTY); - } + public RadialTreeLayout(Graph g) { + this(g, DEFAULT_DISTX, DEFAULT_DISTY); + } - public RadialTreeLayout(Graph g, int distx) { - this(g, distx, DEFAULT_DISTY); - } + public RadialTreeLayout(Graph g, int distx) { + this(g, distx, DEFAULT_DISTY); + } - public RadialTreeLayout(Graph g, int distx, int disty) { - super(g, distx, disty); - } - - @Override - protected void buildTree() { - super.buildTree(); - this.polarLocations = new HashMap(); - setRadialLocations(); - } + public RadialTreeLayout(Graph g, int distx, int disty) { + super(g, distx, disty); + } - @Override - public void setSize(Dimension size) { - this.size = size; - buildTree(); - } + @Override + protected void buildTree() { + super.buildTree(); + this.polarLocations = new HashMap(); + setRadialLocations(); + } + + @Override + public void setSize(Dimension size) { + this.size = size; + buildTree(); + } + + @Override + protected void setCurrentPositionFor(N node) { + locations.getUnchecked(node).setLocation(m_currentPoint); + } + + @Override + public void setLocation(N node, Point2D location) { + Point2D c = getCenter(); + Point2D pv = new Point2D.Double(location.getX() - c.getX(), location.getY() - c.getY()); + PolarPoint newLocation = PolarPoint.cartesianToPolar(pv); + PolarPoint currentLocation = polarLocations.get(node); + if (currentLocation == null) polarLocations.put(node, newLocation); + else currentLocation.setLocation(newLocation); + } - @Override - protected void setCurrentPositionFor(N node) { - locations.getUnchecked(node).setLocation(m_currentPoint); + /** @return a map from nodes to their locations in polar coordinates. */ + public Map getPolarLocations() { + return polarLocations; + } + + @Override + public Point2D apply(N node) { + PolarPoint pp = polarLocations.get(node); + double centerX = getSize().getWidth() / 2; + double centerY = getSize().getHeight() / 2; + Point2D cartesian = PolarPoint.polarToCartesian(pp); + cartesian.setLocation(cartesian.getX() + centerX, cartesian.getY() + centerY); + return cartesian; + } + + private Point2D getMaxXY() { + double maxx = 0; + double maxy = 0; + for (Point2D p : locations.asMap().values()) { + maxx = Math.max(maxx, p.getX()); + maxy = Math.max(maxy, p.getY()); } + return new Point2D.Double(maxx, maxy); + } - @Override - public void setLocation(N node, Point2D location) - { - Point2D c = getCenter(); - Point2D pv = new Point2D.Double(location.getX() - c.getX(), - location.getY() - c.getY()); - PolarPoint newLocation = PolarPoint.cartesianToPolar(pv); - PolarPoint currentLocation = polarLocations.get(node); - if (currentLocation == null) - polarLocations.put(node, newLocation); - else - currentLocation.setLocation(newLocation); - } - - /** - * @return a map from nodes to their locations in polar coordinates. - */ - public Map getPolarLocations() { - return polarLocations; - } - - @Override - public Point2D apply(N node) { - PolarPoint pp = polarLocations.get(node); - double centerX = getSize().getWidth()/2; - double centerY = getSize().getHeight()/2; - Point2D cartesian = PolarPoint.polarToCartesian(pp); - cartesian.setLocation(cartesian.getX()+centerX,cartesian.getY()+centerY); - return cartesian; - } - - private Point2D getMaxXY() { - double maxx = 0; - double maxy = 0; - for(Point2D p : locations.asMap().values()) { - maxx = Math.max(maxx, p.getX()); - maxy = Math.max(maxy, p.getY()); - } - return new Point2D.Double(maxx,maxy); - } - - private void setRadialLocations() { - Point2D max = getMaxXY(); - double maxx = max.getX(); - double maxy = max.getY(); - maxx = Math.max(maxx, size.width); - double theta = 2*Math.PI/maxx; - - double deltaRadius = size.width/2/maxy; - for(Map.Entry entry : locations.asMap().entrySet()) { - N node = entry.getKey(); - Point2D p = entry.getValue(); - PolarPoint polarPoint = - new PolarPoint(p.getX()*theta, (p.getY() - this.distY)*deltaRadius); - polarLocations.put(node, polarPoint); - } - } + private void setRadialLocations() { + Point2D max = getMaxXY(); + double maxx = max.getX(); + double maxy = max.getY(); + maxx = Math.max(maxx, size.width); + double theta = 2 * Math.PI / maxx; + + double deltaRadius = size.width / 2 / maxy; + for (Map.Entry entry : locations.asMap().entrySet()) { + N node = entry.getKey(); + Point2D p = entry.getValue(); + PolarPoint polarPoint = + new PolarPoint(p.getX() * theta, (p.getY() - this.distY) * deltaRadius); + polarLocations.put(node, polarPoint); + } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/RadiusNetworkElementAccessor.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/RadiusNetworkElementAccessor.java index 29ca9e60..93aab22e 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/RadiusNetworkElementAccessor.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/RadiusNetworkElementAccessor.java @@ -1,168 +1,159 @@ /* * Copyright (c) 2005, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * * * Created on Apr 12, 2005 */ package edu.uci.ics.jung.algorithms.layout; +import com.google.common.graph.EndpointPair; +import com.google.common.graph.Network; import java.awt.Shape; import java.awt.geom.Point2D; import java.util.ConcurrentModificationException; import java.util.HashSet; import java.util.Set; -import com.google.common.graph.EndpointPair; -import com.google.common.graph.Network; - - /** - * Simple implementation of PickSupport that returns the node or edge - * that is closest to the specified location. This implementation - * provides the same picking options that were available in + * Simple implementation of PickSupport that returns the node or edge that is closest to the + * specified location. This implementation provides the same picking options that were available in * previous versions of AbstractLayout. - * - *

No element will be returned that is farther away than the specified - * maximum distance. - * + * + *

No element will be returned that is farther away than the specified maximum distance. + * * @author Tom Nelson * @author Joshua O'Madadhain */ public class RadiusNetworkElementAccessor implements NetworkElementAccessor { - private final Network network; - private final Layout layout; - protected double maxDistance; - - /** - * Creates an instance with an effectively infinite default maximum distance. - */ - public RadiusNetworkElementAccessor(Network network, Layout layout) { - this(network, layout, Math.sqrt(Double.MAX_VALUE - 1000)); + private final Network network; + private final Layout layout; + protected double maxDistance; + + /** Creates an instance with an effectively infinite default maximum distance. */ + public RadiusNetworkElementAccessor(Network network, Layout layout) { + this(network, layout, Math.sqrt(Double.MAX_VALUE - 1000)); + } + + /** + * Creates an instance with the specified default maximum distance. + * + * @param maxDistance the maximum distance at which any element can be from a specified location + * and still be returned + */ + public RadiusNetworkElementAccessor(Network network, Layout layout, double maxDistance) { + this.network = network; + this.layout = layout; + this.maxDistance = maxDistance; + } + + /** + * Gets the node nearest to the location of the (x,y) location selected, within a distance of + * {@code this.maxDistance}. Iterates through all visible nodes and checks their distance from the + * location. Override this method to provide a more efficient implementation. + * + * @param x the x coordinate of the location + * @param y the y coordinate of the location + * @return a node which is associated with the location {@code (x,y)} as given by {@code layout} + */ + @Override + public N getNode(double x, double y) { + double minDistance = maxDistance * maxDistance; + N closest = null; + while (true) { + try { + for (N node : layout.nodes()) { + + Point2D p = layout.apply(node); + double dx = p.getX() - x; + double dy = p.getY() - y; + double dist = dx * dx + dy * dy; + if (dist < minDistance) { + minDistance = dist; + closest = node; + } + } + break; + } catch (ConcurrentModificationException cme) { + } } - - /** - * Creates an instance with the specified default maximum distance. - * @param maxDistance the maximum distance at which any element can be from a specified location - * and still be returned - */ - public RadiusNetworkElementAccessor(Network network, Layout layout, double maxDistance) { - this.network = network; - this.layout = layout; - this.maxDistance = maxDistance; + return closest; + } + + public Set getNodes(Shape rectangle) { + Set pickednodes = new HashSet(); + while (true) { + try { + for (N node : layout.nodes()) { + Point2D p = layout.apply(node); + if (rectangle.contains(p)) { + pickednodes.add(node); + } + } + break; + } catch (ConcurrentModificationException cme) { + } } - - /** - * Gets the node nearest to the location of the (x,y) location selected, - * within a distance of {@code this.maxDistance}. Iterates through all - * visible nodes and checks their distance from the location. Override this - * method to provide a more efficient implementation. - * - * @param x the x coordinate of the location - * @param y the y coordinate of the location - * @return a node which is associated with the location {@code (x,y)} - * as given by {@code layout} - */ - @Override - public N getNode(double x, double y) { - double minDistance = maxDistance * maxDistance; - N closest = null; - while(true) { - try { - for(N node : layout.nodes()) { + return pickednodes; + } - Point2D p = layout.apply(node); - double dx = p.getX() - x; - double dy = p.getY() - y; - double dist = dx * dx + dy * dy; - if (dist < minDistance) { - minDistance = dist; - closest = node; - } - } - break; - } catch(ConcurrentModificationException cme) {} - } - return closest; - } - - public Set getNodes(Shape rectangle) { - Set pickednodes = new HashSet(); - while(true) { - try { - for(N node : layout.nodes()) { - Point2D p = layout.apply(node); - if (rectangle.contains(p)) { - pickednodes.add(node); - } - } - break; - } catch(ConcurrentModificationException cme) {} - } - return pickednodes; - } - - /** - * Gets the edge nearest to the location of the (x,y) location selected, - * whose endpoints are < {@code maxDistance}. Iterates through all - * visible nodes and checks their distance from the location. Override this - * method to provide a more efficient implementation. - * - * @param layout the context in which the location is defined - * @param x the x coordinate of the location - * @param y the y coordinate of the location - * @param maxDistance the maximum distance at which any element can be from a specified location - * and still be returned - * @return an edge which is associated with the location {@code (x,y)} - * as given by {@code layout} - */ - @Override - public E getEdge(double x, double y) { - double minDistance = maxDistance * maxDistance; - E closest = null; - while(true) { - try { - for(E edge : network.edges()) { - EndpointPair endpoints = network.incidentNodes(edge); - N node1 = endpoints.nodeU(); - N node2 = endpoints.nodeV(); - // Get coords - Point2D p1 = layout.apply(node1); - Point2D p2 = layout.apply(node2); - double x1 = p1.getX(); - double y1 = p1.getY(); - double x2 = p2.getX(); - double y2 = p2.getY(); - // Calculate location on line closest to (x,y) - // First, check that v1 and v2 are not coincident. - if (x1 == x2 && y1 == y2) - continue; - double b = - ((y - y1) * (y2 - y1) + (x - x1) * (x2 - x1)) - / ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); - // - double distance2; // square of the distance - if (b <= 0) - distance2 = (x - x1) * (x - x1) + (y - y1) * (y - y1); - else if (b >= 1) - distance2 = (x - x2) * (x - x2) + (y - y2) * (y - y2); - else { - double x3 = x1 + b * (x2 - x1); - double y3 = y1 + b * (y2 - y1); - distance2 = (x - x3) * (x - x3) + (y - y3) * (y - y3); - } - - if (distance2 < minDistance) { - minDistance = distance2; - closest = edge; - } - } - break; - } catch(ConcurrentModificationException cme) {} - } - return closest; - } + /** + * Gets the edge nearest to the location of the (x,y) location selected, whose endpoints are < + * {@code maxDistance}. Iterates through all visible nodes and checks their distance from the + * location. Override this method to provide a more efficient implementation. + * + * @param layout the context in which the location is defined + * @param x the x coordinate of the location + * @param y the y coordinate of the location + * @param maxDistance the maximum distance at which any element can be from a specified location + * and still be returned + * @return an edge which is associated with the location {@code (x,y)} as given by {@code layout} + */ + @Override + public E getEdge(double x, double y) { + double minDistance = maxDistance * maxDistance; + E closest = null; + while (true) { + try { + for (E edge : network.edges()) { + EndpointPair endpoints = network.incidentNodes(edge); + N node1 = endpoints.nodeU(); + N node2 = endpoints.nodeV(); + // Get coords + Point2D p1 = layout.apply(node1); + Point2D p2 = layout.apply(node2); + double x1 = p1.getX(); + double y1 = p1.getY(); + double x2 = p2.getX(); + double y2 = p2.getY(); + // Calculate location on line closest to (x,y) + // First, check that v1 and v2 are not coincident. + if (x1 == x2 && y1 == y2) continue; + double b = + ((y - y1) * (y2 - y1) + (x - x1) * (x2 - x1)) + / ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + // + double distance2; // square of the distance + if (b <= 0) distance2 = (x - x1) * (x - x1) + (y - y1) * (y - y1); + else if (b >= 1) distance2 = (x - x2) * (x - x2) + (y - y2) * (y - y2); + else { + double x3 = x1 + b * (x2 - x1); + double y3 = y1 + b * (y2 - y1); + distance2 = (x - x3) * (x - x3) + (y - y3) * (y - y3); + } + + if (distance2 < minDistance) { + minDistance = distance2; + closest = edge; + } + } + break; + } catch (ConcurrentModificationException cme) { + } + } + return closest; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/SpringLayout.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/SpringLayout.java index d2820634..035863e7 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/SpringLayout.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/SpringLayout.java @@ -7,12 +7,6 @@ */ package edu.uci.ics.jung.algorithms.layout; -import java.awt.Dimension; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.geom.Point2D; -import java.util.ConcurrentModificationException; - import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.cache.CacheBuilder; @@ -20,308 +14,293 @@ import com.google.common.cache.LoadingCache; import com.google.common.graph.EndpointPair; import com.google.common.graph.Graph; - import edu.uci.ics.jung.algorithms.layout.util.RandomLocationTransformer; import edu.uci.ics.jung.algorithms.util.IterativeContext; +import java.awt.Dimension; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.geom.Point2D; +import java.util.ConcurrentModificationException; /** - * The SpringLayout package represents a visualization of a set of nodes. The - * SpringLayout, which is initialized with a Graph, assigns X/Y locations to - * each node. When called relax(), the SpringLayout moves the - * visualization forward one step. + * The SpringLayout package represents a visualization of a set of nodes. The SpringLayout, which is + * initialized with a Graph, assigns X/Y locations to each node. When called relax(), + * the SpringLayout moves the visualization forward one step. * * @author Danyel Fisher * @author Joshua O'Madadhain */ public class SpringLayout extends AbstractLayout implements IterativeContext { - // FIXME(jrtom): add a parameter to allow the max change to be different than 5 - // FIXME(jrtom): modify this to use a builder instead of a bunch of constructors/setters - protected double stretch = 0.70; - protected Function, Integer> lengthFunction; - protected int repulsion_range_sq = 100 * 100; - protected double force_multiplier = 1.0 / 3.0; - protected final Graph graph; - - protected LoadingCache springNodeData = - CacheBuilder.newBuilder().build(new CacheLoader() { - public SpringNodeData load(N node) { - return new SpringNodeData(); - } - }); - - /** - * Constructor for a SpringLayout for a raw graph with associated - * dimension--the input knows how big the graph is. Defaults to the unit - * length function. - * @param g the graph on which the layout algorithm is to operate - */ - public SpringLayout(Graph g) { - this(g, Functions.constant(30)); - } - - /** - * Constructor for a SpringLayout for a raw graph with associated component. - * - * @param g the graph on which the layout algorithm is to operate - * @param length_function provides a length for each edge - */ - public SpringLayout(Graph g, Function, Integer> length_function) - { - super(g); - this.graph = g; - this.lengthFunction = length_function; - } - - /** - * @return the current value for the stretch parameter - */ - public double getStretch() { - return stretch; - } - - @Override - public void setSize(Dimension size) { - if(initialized == false) - setInitializer(new RandomLocationTransformer(size)); - super.setSize(size); - } - - /** - *

Sets the stretch parameter for this instance. This value - * specifies how much the degrees of an edge's incident nodes - * should influence how easily the endpoints of that edge - * can move (that is, that edge's tendency to change its length). - * - *

The default value is 0.70. Positive values less than 1 cause - * high-degree nodes to move less than low-degree nodes, and - * values > 1 cause high-degree nodes to move more than - * low-degree nodes. Negative values will have unpredictable - * and inconsistent results. - * @param stretch the stretch parameter - */ - public void setStretch(double stretch) { - this.stretch = stretch; - } - - public int getRepulsionRange() { - return (int)(Math.sqrt(repulsion_range_sq)); - } - - /** - * Sets the node repulsion range (in drawing area units) for this instance. - * Outside this range, nodes do not repel each other. The default value - * is 100. Negative values are treated as their positive equivalents. - * @param range the maximum repulsion range - */ - public void setRepulsionRange(int range) { - this.repulsion_range_sq = range * range; - } - - public double getForceMultiplier() { - return force_multiplier; - } - - /** - * Sets the force multiplier for this instance. This value is used to - * specify how strongly an edge "wants" to be its default length - * (higher values indicate a greater attraction for the default length), - * which affects how much its endpoints move at each timestep. - * The default value is 1/3. A value of 0 turns off any attempt by the - * layout to cause edges to conform to the default length. Negative - * values cause long edges to get longer and short edges to get shorter; use - * at your own risk. - * @param force an energy field created by all living things that binds the galaxy together - */ - public void setForceMultiplier(double force) { - this.force_multiplier = force; - } - - public void initialize() { - } - - /** - * Relaxation step. Moves all nodes a smidge. - */ - public void step() { - try { - for(N node : graph.nodes()) { - SpringNodeData svd = springNodeData.getUnchecked(node); - if (svd == null) { - continue; - } - svd.dx /= 4; - svd.dy /= 4; - svd.edgedx = svd.edgedy = 0; - svd.repulsiondx = svd.repulsiondy = 0; - } - } catch(ConcurrentModificationException cme) { - step(); - } - - relaxEdges(); - calculateRepulsion(); - moveNodes(); + // FIXME(jrtom): add a parameter to allow the max change to be different than 5 + // FIXME(jrtom): modify this to use a builder instead of a bunch of constructors/setters + protected double stretch = 0.70; + protected Function, Integer> lengthFunction; + protected int repulsion_range_sq = 100 * 100; + protected double force_multiplier = 1.0 / 3.0; + protected final Graph graph; + + protected LoadingCache springNodeData = + CacheBuilder.newBuilder() + .build( + new CacheLoader() { + public SpringNodeData load(N node) { + return new SpringNodeData(); + } + }); + + /** + * Constructor for a SpringLayout for a raw graph with associated dimension--the input knows how + * big the graph is. Defaults to the unit length function. + * + * @param g the graph on which the layout algorithm is to operate + */ + public SpringLayout(Graph g) { + this(g, Functions.constant(30)); + } + + /** + * Constructor for a SpringLayout for a raw graph with associated component. + * + * @param g the graph on which the layout algorithm is to operate + * @param length_function provides a length for each edge + */ + public SpringLayout(Graph g, Function, Integer> length_function) { + super(g); + this.graph = g; + this.lengthFunction = length_function; + } + + /** @return the current value for the stretch parameter */ + public double getStretch() { + return stretch; + } + + @Override + public void setSize(Dimension size) { + if (initialized == false) setInitializer(new RandomLocationTransformer(size)); + super.setSize(size); + } + + /** + * Sets the stretch parameter for this instance. This value specifies how much the degrees of an + * edge's incident nodes should influence how easily the endpoints of that edge can move (that is, + * that edge's tendency to change its length). + * + *

The default value is 0.70. Positive values less than 1 cause high-degree nodes to move less + * than low-degree nodes, and values > 1 cause high-degree nodes to move more than low-degree + * nodes. Negative values will have unpredictable and inconsistent results. + * + * @param stretch the stretch parameter + */ + public void setStretch(double stretch) { + this.stretch = stretch; + } + + public int getRepulsionRange() { + return (int) (Math.sqrt(repulsion_range_sq)); + } + + /** + * Sets the node repulsion range (in drawing area units) for this instance. Outside this range, + * nodes do not repel each other. The default value is 100. Negative values are treated as their + * positive equivalents. + * + * @param range the maximum repulsion range + */ + public void setRepulsionRange(int range) { + this.repulsion_range_sq = range * range; + } + + public double getForceMultiplier() { + return force_multiplier; + } + + /** + * Sets the force multiplier for this instance. This value is used to specify how strongly an edge + * "wants" to be its default length (higher values indicate a greater attraction for the default + * length), which affects how much its endpoints move at each timestep. The default value is 1/3. + * A value of 0 turns off any attempt by the layout to cause edges to conform to the default + * length. Negative values cause long edges to get longer and short edges to get shorter; use at + * your own risk. + * + * @param force an energy field created by all living things that binds the galaxy together + */ + public void setForceMultiplier(double force) { + this.force_multiplier = force; + } + + public void initialize() {} + + /** Relaxation step. Moves all nodes a smidge. */ + public void step() { + try { + for (N node : graph.nodes()) { + SpringNodeData svd = springNodeData.getUnchecked(node); + if (svd == null) { + continue; + } + svd.dx /= 4; + svd.dy /= 4; + svd.edgedx = svd.edgedy = 0; + svd.repulsiondx = svd.repulsiondy = 0; + } + } catch (ConcurrentModificationException cme) { + step(); } - protected void relaxEdges() { - try { - for (EndpointPair endpoints : graph.edges()) { - N node1 = endpoints.nodeU(); - N node2 = endpoints.nodeV(); - - Point2D p1 = apply(node1); - Point2D p2 = apply(node2); - if(p1 == null || p2 == null) continue; - double vx = p1.getX() - p2.getX(); - double vy = p1.getY() - p2.getY(); - double len = Math.sqrt(vx * vx + vy * vy); - - double desiredLen = lengthFunction.apply(endpoints); - - // round from zero, if needed [zero would be Bad.]. - len = (len == 0) ? .0001 : len; - - double f = force_multiplier * (desiredLen - len) / len; - - f = f * Math.pow(stretch, (graph.degree(node1) + graph.degree(node2) - 2)); - - // the actual movement distance 'dx' is the force multiplied by the - // distance to go. - double dx = f * vx; - double dy = f * vy; - SpringNodeData v1D, v2D; - v1D = springNodeData.getUnchecked(node1); - v2D = springNodeData.getUnchecked(node2); - - v1D.edgedx += dx; - v1D.edgedy += dy; - v2D.edgedx += -dx; - v2D.edgedy += -dy; - } - } catch(ConcurrentModificationException cme) { - relaxEdges(); - } + relaxEdges(); + calculateRepulsion(); + moveNodes(); + } + + protected void relaxEdges() { + try { + for (EndpointPair endpoints : graph.edges()) { + N node1 = endpoints.nodeU(); + N node2 = endpoints.nodeV(); + + Point2D p1 = apply(node1); + Point2D p2 = apply(node2); + if (p1 == null || p2 == null) continue; + double vx = p1.getX() - p2.getX(); + double vy = p1.getY() - p2.getY(); + double len = Math.sqrt(vx * vx + vy * vy); + + double desiredLen = lengthFunction.apply(endpoints); + + // round from zero, if needed [zero would be Bad.]. + len = (len == 0) ? .0001 : len; + + double f = force_multiplier * (desiredLen - len) / len; + + f = f * Math.pow(stretch, (graph.degree(node1) + graph.degree(node2) - 2)); + + // the actual movement distance 'dx' is the force multiplied by the + // distance to go. + double dx = f * vx; + double dy = f * vy; + SpringNodeData v1D, v2D; + v1D = springNodeData.getUnchecked(node1); + v2D = springNodeData.getUnchecked(node2); + + v1D.edgedx += dx; + v1D.edgedy += dy; + v2D.edgedx += -dx; + v2D.edgedy += -dy; + } + } catch (ConcurrentModificationException cme) { + relaxEdges(); } - - protected void calculateRepulsion() { - try { - for (N node : graph.nodes()) { - if (isLocked(node)) continue; - - SpringNodeData svd = springNodeData.getUnchecked(node); - if(svd == null) continue; - double dx = 0, dy = 0; - - for (N node2 : graph.nodes()) { - if (node == node2) continue; - Point2D p = apply(node); - Point2D p2 = apply(node2); - if(p == null || p2 == null) continue; - double vx = p.getX() - p2.getX(); - double vy = p.getY() - p2.getY(); - double distanceSq = p.distanceSq(p2); - if (distanceSq == 0) { - dx += Math.random(); - dy += Math.random(); - } else if (distanceSq < repulsion_range_sq) { - double factor = 1; - dx += factor * vx / distanceSq; - dy += factor * vy / distanceSq; - } - } - double dlen = dx * dx + dy * dy; - if (dlen > 0) { - dlen = Math.sqrt(dlen) / 2; - svd.repulsiondx += dx / dlen; - svd.repulsiondy += dy / dlen; - } + } + + protected void calculateRepulsion() { + try { + for (N node : graph.nodes()) { + if (isLocked(node)) continue; + + SpringNodeData svd = springNodeData.getUnchecked(node); + if (svd == null) continue; + double dx = 0, dy = 0; + + for (N node2 : graph.nodes()) { + if (node == node2) continue; + Point2D p = apply(node); + Point2D p2 = apply(node2); + if (p == null || p2 == null) continue; + double vx = p.getX() - p2.getX(); + double vy = p.getY() - p2.getY(); + double distanceSq = p.distanceSq(p2); + if (distanceSq == 0) { + dx += Math.random(); + dy += Math.random(); + } else if (distanceSq < repulsion_range_sq) { + double factor = 1; + dx += factor * vx / distanceSq; + dy += factor * vy / distanceSq; + } } - } catch(ConcurrentModificationException cme) { - calculateRepulsion(); + double dlen = dx * dx + dy * dy; + if (dlen > 0) { + dlen = Math.sqrt(dlen) / 2; + svd.repulsiondx += dx / dlen; + svd.repulsiondy += dy / dlen; } + } + } catch (ConcurrentModificationException cme) { + calculateRepulsion(); } + } - protected void moveNodes() - { - synchronized (getSize()) { - try { - for (N node : graph.nodes()) { - if (isLocked(node)) continue; - SpringNodeData vd = springNodeData.getUnchecked(node); - if (vd == null) continue; - Point2D xyd = apply(node); - - vd.dx += vd.repulsiondx + vd.edgedx; - vd.dy += vd.repulsiondy + vd.edgedy; - - // keeps nodes from moving any faster than 5 per time unit - xyd.setLocation(xyd.getX()+Math.max(-5, Math.min(5, vd.dx)), - xyd.getY()+Math.max(-5, Math.min(5, vd.dy))); - - Dimension d = getSize(); - int width = d.width; - int height = d.height; - - if (xyd.getX() < 0) { - xyd.setLocation(0, xyd.getY()); - } else if (xyd.getX() > width) { - xyd.setLocation(width, xyd.getY()); - } - if (xyd.getY() < 0) { - xyd.setLocation(xyd.getX(), 0); - } else if (xyd.getY() > height) { - xyd.setLocation(xyd.getX(), height); - } - - } - } catch(ConcurrentModificationException cme) { - moveNodes(); - } + protected void moveNodes() { + synchronized (getSize()) { + try { + for (N node : graph.nodes()) { + if (isLocked(node)) continue; + SpringNodeData vd = springNodeData.getUnchecked(node); + if (vd == null) continue; + Point2D xyd = apply(node); + + vd.dx += vd.repulsiondx + vd.edgedx; + vd.dy += vd.repulsiondy + vd.edgedy; + + // keeps nodes from moving any faster than 5 per time unit + xyd.setLocation( + xyd.getX() + Math.max(-5, Math.min(5, vd.dx)), + xyd.getY() + Math.max(-5, Math.min(5, vd.dy))); + + Dimension d = getSize(); + int width = d.width; + int height = d.height; + + if (xyd.getX() < 0) { + xyd.setLocation(0, xyd.getY()); + } else if (xyd.getX() > width) { + xyd.setLocation(width, xyd.getY()); + } + if (xyd.getY() < 0) { + xyd.setLocation(xyd.getX(), 0); + } else if (xyd.getY() > height) { + xyd.setLocation(xyd.getX(), height); + } } + } catch (ConcurrentModificationException cme) { + moveNodes(); + } } - - protected static class SpringNodeData { - protected double edgedx; - protected double edgedy; - protected double repulsiondx; - protected double repulsiondy; - - /** movement speed, x */ - protected double dx; - - /** movement speed, y */ - protected double dy; + } + + protected static class SpringNodeData { + protected double edgedx; + protected double edgedy; + protected double repulsiondx; + protected double repulsiondy; + + /** movement speed, x */ + protected double dx; + + /** movement speed, y */ + protected double dy; + } + + /** Used for changing the size of the layout in response to a component's size. */ + public class SpringDimensionChecker extends ComponentAdapter { + @Override + public void componentResized(ComponentEvent e) { + setSize(e.getComponent().getSize()); } + } + /** @return true */ + public boolean isIncremental() { + return true; + } - /** - * Used for changing the size of the layout in response to a component's size. - */ - public class SpringDimensionChecker extends ComponentAdapter { - @Override - public void componentResized(ComponentEvent e) { - setSize(e.getComponent().getSize()); - } - } - - /** - * @return true - */ - public boolean isIncremental() { - return true; - } - - /** - * @return false - */ - public boolean done() { - return false; - } + /** @return false */ + public boolean done() { + return false; + } - /** - * No effect. - */ - public void reset() { - } -} \ No newline at end of file + /** No effect. */ + public void reset() {} +} diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/SpringLayout2.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/SpringLayout2.java index 310c1caa..8290d032 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/SpringLayout2.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/SpringLayout2.java @@ -1,139 +1,129 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. */ package edu.uci.ics.jung.algorithms.layout; -import java.awt.Dimension; -import java.awt.geom.Point2D; -import java.util.ConcurrentModificationException; - import com.google.common.base.Function; import com.google.common.graph.EndpointPair; import com.google.common.graph.Graph; +import java.awt.Dimension; +import java.awt.geom.Point2D; +import java.util.ConcurrentModificationException; /** - * The SpringLayout package represents a visualization of a set of nodes. The - * SpringLayout, which is initialized with a Graph, assigns X/Y locations to - * each node. When called relax(), the SpringLayout moves the - * visualization forward one step. - * - * - * + * The SpringLayout package represents a visualization of a set of nodes. The SpringLayout, which is + * initialized with a Graph, assigns X/Y locations to each node. When called relax(), + * the SpringLayout moves the visualization forward one step. + * * @author Danyel Fisher * @author Joshua O'Madadhain */ -public class SpringLayout2 extends SpringLayout -{ - protected int currentIteration; - protected int averageCounter; - protected int loopCountMax = 4; - protected boolean done; - - protected Point2D averageDelta = new Point2D.Double(); - - /** - * Constructor for a SpringLayout for a raw graph with associated - * dimension--the input knows how big the graph is. Defaults to the unit - * length function. - * @param g the graph on which the layout algorithm is to operate - */ - public SpringLayout2(Graph g) { - super(g); - } +public class SpringLayout2 extends SpringLayout { + protected int currentIteration; + protected int averageCounter; + protected int loopCountMax = 4; + protected boolean done; - /** - * Constructor for a SpringLayout for a raw graph with associated component. - * - * @param g the {@code Graph} to lay out - * @param length_function provides a length for each edge - */ - public SpringLayout2(Graph g, Function, Integer> length_function) - { - super(g, length_function); - } + protected Point2D averageDelta = new Point2D.Double(); + + /** + * Constructor for a SpringLayout for a raw graph with associated dimension--the input knows how + * big the graph is. Defaults to the unit length function. + * + * @param g the graph on which the layout algorithm is to operate + */ + public SpringLayout2(Graph g) { + super(g); + } - /** - * Relaxation step. Moves all nodes a smidge. - */ - @Override - public void step() { - super.step(); - currentIteration++; - testAverageDeltas(); + /** + * Constructor for a SpringLayout for a raw graph with associated component. + * + * @param g the {@code Graph} to lay out + * @param length_function provides a length for each edge + */ + public SpringLayout2(Graph g, Function, Integer> length_function) { + super(g, length_function); + } + + /** Relaxation step. Moves all nodes a smidge. */ + @Override + public void step() { + super.step(); + currentIteration++; + testAverageDeltas(); + } + + private void testAverageDeltas() { + double dx = this.averageDelta.getX(); + double dy = this.averageDelta.getY(); + if (Math.abs(dx) < .001 && Math.abs(dy) < .001) { + done = true; + System.err.println("done, dx=" + dx + ", dy=" + dy); } - - private void testAverageDeltas() { - double dx = this.averageDelta.getX(); - double dy = this.averageDelta.getY(); - if(Math.abs(dx) < .001 && Math.abs(dy) < .001) { - done = true; - System.err.println("done, dx="+dx+", dy="+dy); - } - if(currentIteration > loopCountMax) { - this.averageDelta.setLocation(0,0); - averageCounter = 0; - currentIteration = 0; - } + if (currentIteration > loopCountMax) { + this.averageDelta.setLocation(0, 0); + averageCounter = 0; + currentIteration = 0; } + } - @Override - protected void moveNodes() { - synchronized (getSize()) { - try { - for (N node : graph.nodes()) { - if (isLocked(node)) continue; - SpringNodeData vd = springNodeData.getUnchecked(node); - if(vd == null) continue; - Point2D xyd = apply(node); - - vd.dx += vd.repulsiondx + vd.edgedx; - vd.dy += vd.repulsiondy + vd.edgedy; - -// int currentCount = currentIteration % this.loopCountMax; -// System.err.println(averageCounter+" --- vd.dx="+vd.dx+", vd.dy="+vd.dy); -// System.err.println("averageDelta was "+averageDelta); + @Override + protected void moveNodes() { + synchronized (getSize()) { + try { + for (N node : graph.nodes()) { + if (isLocked(node)) continue; + SpringNodeData vd = springNodeData.getUnchecked(node); + if (vd == null) continue; + Point2D xyd = apply(node); - averageDelta.setLocation( - ((averageDelta.getX() * averageCounter) + vd.dx) / (averageCounter+1), - ((averageDelta.getY() * averageCounter) + vd.dy) / (averageCounter+1) - ); -// System.err.println("averageDelta now "+averageDelta); -// System.err.println(); - averageCounter++; - - // keeps nodes from moving any faster than 5 per time unit - xyd.setLocation(xyd.getX()+Math.max(-5, Math.min(5, vd.dx)), - xyd.getY()+Math.max(-5, Math.min(5, vd.dy))); - - Dimension d = getSize(); - int width = d.width; - int height = d.height; - - if (xyd.getX() < 0) { - xyd.setLocation(0, xyd.getY());// setX(0); - } else if (xyd.getX() > width) { - xyd.setLocation(width, xyd.getY()); //setX(width); - } - if (xyd.getY() < 0) { - xyd.setLocation(xyd.getX(),0);//setY(0); - } else if (xyd.getY() > height) { - xyd.setLocation(xyd.getX(), height); //setY(height); - } - - } - } catch(ConcurrentModificationException cme) { - moveNodes(); - } - } - } + vd.dx += vd.repulsiondx + vd.edgedx; + vd.dy += vd.repulsiondy + vd.edgedy; - @Override - public boolean done() { - return done; + // int currentCount = currentIteration % this.loopCountMax; + // System.err.println(averageCounter+" --- vd.dx="+vd.dx+", vd.dy="+vd.dy); + // System.err.println("averageDelta was "+averageDelta); + + averageDelta.setLocation( + ((averageDelta.getX() * averageCounter) + vd.dx) / (averageCounter + 1), + ((averageDelta.getY() * averageCounter) + vd.dy) / (averageCounter + 1)); + // System.err.println("averageDelta now "+averageDelta); + // System.err.println(); + averageCounter++; + + // keeps nodes from moving any faster than 5 per time unit + xyd.setLocation( + xyd.getX() + Math.max(-5, Math.min(5, vd.dx)), + xyd.getY() + Math.max(-5, Math.min(5, vd.dy))); + + Dimension d = getSize(); + int width = d.width; + int height = d.height; + + if (xyd.getX() < 0) { + xyd.setLocation(0, xyd.getY()); // setX(0); + } else if (xyd.getX() > width) { + xyd.setLocation(width, xyd.getY()); //setX(width); + } + if (xyd.getY() < 0) { + xyd.setLocation(xyd.getX(), 0); //setY(0); + } else if (xyd.getY() > height) { + xyd.setLocation(xyd.getX(), height); //setY(height); + } + } + } catch (ConcurrentModificationException cme) { + moveNodes(); + } } + } -} \ No newline at end of file + @Override + public boolean done() { + return done; + } +} diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/StaticLayout.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/StaticLayout.java index 27375ef4..1ead740a 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/StaticLayout.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/StaticLayout.java @@ -1,7 +1,7 @@ /* * Created on Jul 21, 2005 * - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,38 +11,37 @@ */ package edu.uci.ics.jung.algorithms.layout; -import java.awt.Dimension; -import java.awt.geom.Point2D; - import com.google.common.base.Function; import com.google.common.graph.Graph; +import java.awt.Dimension; +import java.awt.geom.Point2D; /** - * StaticLayout places the nodes in the locations specified by its initializer, - * and has no other behavior. - * node locations can be placed in a {@code Map} and then supplied to - * this layout as follows: {@code Function nodeLocations = Functions.forMap(map);} + * StaticLayout places the nodes in the locations specified by its initializer, and has no other + * behavior. node locations can be placed in a {@code Map} and then supplied to this + * layout as follows: {@code Function nodeLocations = Functions.forMap(map);} + * * @author Tom Nelson - tomnelson@dev.java.net */ public class StaticLayout extends AbstractLayout { - - public StaticLayout(Graph graph, Function initializer, Dimension size) { - super(graph, initializer, size); - } - - public StaticLayout(Graph graph, Function initializer) { - super(graph, initializer); - } - - public StaticLayout(Graph graph) { - super(graph); - } - - public StaticLayout(Graph graph, Dimension size) { - super(graph, size); - } - - public void initialize() {} - - public void reset() {} + + public StaticLayout(Graph graph, Function initializer, Dimension size) { + super(graph, initializer, size); + } + + public StaticLayout(Graph graph, Function initializer) { + super(graph, initializer); + } + + public StaticLayout(Graph graph) { + super(graph); + } + + public StaticLayout(Graph graph, Dimension size) { + super(graph, size); + } + + public void initialize() {} + + public void reset() {} } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/TreeLayout.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/TreeLayout.java index bddfb165..31d0f9a6 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/TreeLayout.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/TreeLayout.java @@ -9,14 +9,6 @@ */ package edu.uci.ics.jung.algorithms.layout; -import java.awt.Dimension; -import java.awt.Point; -import java.awt.geom.Point2D; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; import com.google.common.base.Function; import com.google.common.base.Preconditions; @@ -25,8 +17,15 @@ import com.google.common.cache.LoadingCache; import com.google.common.graph.Graph; import com.google.common.graph.Graphs; - import edu.uci.ics.jung.graph.util.TreeUtils; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.geom.Point2D; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; /** * @author Karlheinz Toni @@ -34,211 +33,198 @@ */ public class TreeLayout implements Layout { - protected Dimension size = new Dimension(600,600); - protected Graph graph; - protected Map basePositions = new HashMap(); - - protected LoadingCache locations = - CacheBuilder.newBuilder().build(new CacheLoader() { - public Point2D load(N node) { - return new Point2D.Double(); - } - }); - - protected transient Set alreadyDone = new HashSet(); - - /** - * The default horizontal node spacing. Initialized to 50. - */ - public static int DEFAULT_DISTX = 50; - - /** - * The default vertical node spacing. Initialized to 50. - */ - public static int DEFAULT_DISTY = 50; - - /** - * The horizontal node spacing. Defaults to {@code DEFAULT_XDIST}. - */ - protected int distX = 50; - - /** - * The vertical node spacing. Defaults to {@code DEFAULT_YDIST}. - */ - protected int distY = 50; - - protected transient Point m_currentPoint = new Point(); - - /** - * Creates an instance for the specified graph with default X and Y distances. - * @param g the graph on which the layout algorithm is to operate - */ - public TreeLayout(Graph g) { - this(g, DEFAULT_DISTX, DEFAULT_DISTY); + protected Dimension size = new Dimension(600, 600); + protected Graph graph; + protected Map basePositions = new HashMap(); + + protected LoadingCache locations = + CacheBuilder.newBuilder() + .build( + new CacheLoader() { + public Point2D load(N node) { + return new Point2D.Double(); + } + }); + + protected transient Set alreadyDone = new HashSet(); + + /** The default horizontal node spacing. Initialized to 50. */ + public static int DEFAULT_DISTX = 50; + + /** The default vertical node spacing. Initialized to 50. */ + public static int DEFAULT_DISTY = 50; + + /** The horizontal node spacing. Defaults to {@code DEFAULT_XDIST}. */ + protected int distX = 50; + + /** The vertical node spacing. Defaults to {@code DEFAULT_YDIST}. */ + protected int distY = 50; + + protected transient Point m_currentPoint = new Point(); + + /** + * Creates an instance for the specified graph with default X and Y distances. + * + * @param g the graph on which the layout algorithm is to operate + */ + public TreeLayout(Graph g) { + this(g, DEFAULT_DISTX, DEFAULT_DISTY); + } + + /** + * Creates an instance for the specified graph and X distance with default Y distance. + * + * @param g the graph on which the layout algorithm is to operate + * @param distx the horizontal spacing between adjacent siblings + */ + public TreeLayout(Graph g, int distx) { + this(g, distx, DEFAULT_DISTY); + } + + /** + * Creates an instance for the specified graph, X distance, and Y distance. + * + * @param g the graph on which the layout algorithm is to operate + * @param distx the horizontal spacing between adjacent siblings + * @param disty the vertical spacing between adjacent siblings + */ + public TreeLayout(Graph g, int distx, int disty) { + this.graph = Preconditions.checkNotNull(g); + Preconditions.checkArgument(distx >= 1, "X distance must be positive"); + Preconditions.checkArgument(disty >= 1, "Y distance must be positive"); + // Preconditions.checkArgument(TreeUtils.isForestShaped(g), "Input graph must be forest-shaped: \n%s", g); + Preconditions.checkArgument( + !Graphs.hasCycle(g), "Input graph must not contain cycles: \n%s", g); + this.distX = distx; + this.distY = disty; + buildTree(); + } + + protected void buildTree() { + this.m_currentPoint = new Point(0, 20); + Set roots = TreeUtils.roots(graph); + Preconditions.checkArgument(roots.size() > 0); + calculateDimensionX(roots); + for (N node : roots) { + calculateDimensionX(node); + m_currentPoint.x += this.basePositions.get(node) / 2 + this.distX; + buildTree(node, this.m_currentPoint.x); } + } - /** - * Creates an instance for the specified graph and X distance with - * default Y distance. - * @param g the graph on which the layout algorithm is to operate - * @param distx the horizontal spacing between adjacent siblings - */ - public TreeLayout(Graph g, int distx) { - this(g, distx, DEFAULT_DISTY); - } + protected void buildTree(N node, int x) { - /** - * Creates an instance for the specified graph, X distance, and Y distance. - * @param g the graph on which the layout algorithm is to operate - * @param distx the horizontal spacing between adjacent siblings - * @param disty the vertical spacing between adjacent siblings - */ - public TreeLayout(Graph g, int distx, int disty) { - this.graph = Preconditions.checkNotNull(g); - Preconditions.checkArgument(distx >= 1, "X distance must be positive"); - Preconditions.checkArgument(disty >= 1, "Y distance must be positive"); -// Preconditions.checkArgument(TreeUtils.isForestShaped(g), "Input graph must be forest-shaped: \n%s", g); - Preconditions.checkArgument(!Graphs.hasCycle(g), "Input graph must not contain cycles: \n%s", g); - this.distX = distx; - this.distY = disty; - buildTree(); - } - - protected void buildTree() { - this.m_currentPoint = new Point(0, 20); - Set roots = TreeUtils.roots(graph); - Preconditions.checkArgument(roots.size() > 0); - calculateDimensionX(roots); - for (N node : roots) { - calculateDimensionX(node); - m_currentPoint.x += this.basePositions.get(node)/2 + this.distX; - buildTree(node, this.m_currentPoint.x); - } + if (alreadyDone.add(node)) { + //go one level further down + this.m_currentPoint.y += this.distY; + this.m_currentPoint.x = x; + + this.setCurrentPositionFor(node); + + int sizeXofCurrent = basePositions.get(node); + + int lastX = x - sizeXofCurrent / 2; + + int sizeXofChild; + int startXofChild; + + for (N element : graph.successors(node)) { + sizeXofChild = this.basePositions.get(element); + startXofChild = lastX + sizeXofChild / 2; + buildTree(element, startXofChild); + lastX = lastX + sizeXofChild + distX; + } + this.m_currentPoint.y -= this.distY; } + } - protected void buildTree(N node, int x) { + private int calculateDimensionX(N node) { - if (alreadyDone.add(node)) { - //go one level further down - this.m_currentPoint.y += this.distY; - this.m_currentPoint.x = x; + int size = 0; + int childrenNum = graph.successors(node).size(); - this.setCurrentPositionFor(node); + if (childrenNum != 0) { + for (N element : graph.successors(node)) { + size += calculateDimensionX(element) + distX; + } + } + size = Math.max(0, size - distX); + basePositions.put(node, size); - int sizeXofCurrent = basePositions.get(node); + return size; + } - int lastX = x - sizeXofCurrent / 2; + private int calculateDimensionX(Collection roots) { - int sizeXofChild; - int startXofChild; + int size = 0; + for (N node : roots) { + int childrenNum = graph.successors(node).size(); - for (N element : graph.successors(node)) { - sizeXofChild = this.basePositions.get(element); - startXofChild = lastX + sizeXofChild / 2; - buildTree(element, startXofChild); - lastX = lastX + sizeXofChild + distX; - } - this.m_currentPoint.y -= this.distY; + if (childrenNum != 0) { + for (N element : graph.successors(node)) { + size += calculateDimensionX(element) + distX; } + } + size = Math.max(0, size - distX); + basePositions.put(node, size); } - - private int calculateDimensionX(N node) { - int size = 0; - int childrenNum = graph.successors(node).size(); + return size; + } - if (childrenNum != 0) { - for (N element : graph.successors(node)) { - size += calculateDimensionX(element) + distX; - } - } - size = Math.max(0, size - distX); - basePositions.put(node, size); + /** + * This method is not supported by this class. The size of the layout is determined by the + * topology of the tree, and by the horizontal and vertical spacing (optionally set by the + * constructor). + */ + public void setSize(Dimension size) { + throw new UnsupportedOperationException( + "Size of TreeLayout is set" + " by node spacing in constructor"); + } - return size; - } + protected void setCurrentPositionFor(N node) { + int x = m_currentPoint.x; + int y = m_currentPoint.y; + if (x < 0) size.width -= x; - private int calculateDimensionX(Collection roots) { + if (x > size.width - distX) size.width = x + distX; - int size = 0; - for(N node : roots) { - int childrenNum = graph.successors(node).size(); + if (y < 0) size.height -= y; + if (y > size.height - distY) size.height = y + distY; + locations.getUnchecked(node).setLocation(m_currentPoint); + } - if (childrenNum != 0) { - for (N element : graph.successors(node)) { - size += calculateDimensionX(element) + distX; - } - } - size = Math.max(0, size - distX); - basePositions.put(node, size); - } + public Dimension getSize() { + return size; + } - return size; - } - - /** - * This method is not supported by this class. The size of the layout - * is determined by the topology of the tree, and by the horizontal - * and vertical spacing (optionally set by the constructor). - */ - public void setSize(Dimension size) { - throw new UnsupportedOperationException("Size of TreeLayout is set" + - " by node spacing in constructor"); - } + public void initialize() {} - protected void setCurrentPositionFor(N node) { - int x = m_currentPoint.x; - int y = m_currentPoint.y; - if(x < 0) size.width -= x; - - if(x > size.width-distX) - size.width = x + distX; - - if(y < 0) size.height -= y; - if(y > size.height-distY) - size.height = y + distY; - locations.getUnchecked(node).setLocation(m_currentPoint); + public boolean isLocked(N node) { + return false; + } - } + public void lock(N node, boolean state) {} + + public void reset() {} + + public void setInitializer(Function initializer) {} + + /** @return the center of this layout's area. */ + public Point2D getCenter() { + return new Point2D.Double(size.getWidth() / 2, size.getHeight() / 2); + } + + public void setLocation(N node, Point2D location) { + locations.getUnchecked(node).setLocation(location); + } + + public Point2D apply(N node) { + return locations.getUnchecked(node); + } - public Dimension getSize() { - return size; - } - - public void initialize() { - - } - - public boolean isLocked(N node) { - return false; - } - - public void lock(N node, boolean state) { - } - - public void reset() { - } - - public void setInitializer(Function initializer) { - } - - /** - * @return the center of this layout's area. - */ - public Point2D getCenter() { - return new Point2D.Double(size.getWidth()/2,size.getHeight()/2); - } - - public void setLocation(N node, Point2D location) { - locations.getUnchecked(node).setLocation(location); - } - - public Point2D apply(N node) { - return locations.getUnchecked(node); - } - - @Override - public Set nodes() { - return graph.nodes(); - } + @Override + public Set nodes() { + return graph.nodes(); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/util/RandomLocationTransformer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/util/RandomLocationTransformer.java index 72e8feb3..8c9c7b2e 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/util/RandomLocationTransformer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/util/RandomLocationTransformer.java @@ -1,7 +1,7 @@ /* * Created on Jul 19, 2005 * - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,52 +11,49 @@ */ package edu.uci.ics.jung.algorithms.layout.util; +import com.google.common.base.Function; +import edu.uci.ics.jung.algorithms.layout.StaticLayout; import java.awt.Dimension; import java.awt.geom.Point2D; import java.util.Date; import java.util.Random; -import com.google.common.base.Function; - -import edu.uci.ics.jung.algorithms.layout.StaticLayout; - /** - * Provides a random node location within the bounds of the Dimension property. - * This provides a random location for unmapped nodes - * the first time they are accessed. - * + * Provides a random node location within the bounds of the Dimension property. This provides a + * random location for unmapped nodes the first time they are accessed. + * *

Note: the generated values are not cached, so apply() will generate a new random - * location for the passed node every time it is called. If you want a consistent value, - * wrap this layout's generated values in a {@link StaticLayout} instance. - * - * @author Tom Nelson + * location for the passed node every time it is called. If you want a consistent value, wrap this + * layout's generated values in a {@link StaticLayout} instance. * + * @author Tom Nelson * @param the node type */ -public class RandomLocationTransformer implements Function { - Dimension d; - Random random; - - /** - * Creates an instance with the specified size which uses the current time - * as the random seed. - * @param d the size of the layout area - */ - public RandomLocationTransformer(Dimension d) { - this(d, new Date().getTime()); - } - - /** - * Creates an instance with the specified dimension and random seed. - * @param d the size of the layout area - * @param seed the seed for the internal random number generator - */ - public RandomLocationTransformer(final Dimension d, long seed) { - this.d = d; - this.random = new Random(seed); - } - - public Point2D apply(N node) { - return new Point2D.Double(random.nextDouble() * d.width, random.nextDouble() * d.height); - } +public class RandomLocationTransformer implements Function { + Dimension d; + Random random; + + /** + * Creates an instance with the specified size which uses the current time as the random seed. + * + * @param d the size of the layout area + */ + public RandomLocationTransformer(Dimension d) { + this(d, new Date().getTime()); + } + + /** + * Creates an instance with the specified dimension and random seed. + * + * @param d the size of the layout area + * @param seed the seed for the internal random number generator + */ + public RandomLocationTransformer(final Dimension d, long seed) { + this.d = d; + this.random = new Random(seed); + } + + public Point2D apply(N node) { + return new Point2D.Double(random.nextDouble() * d.width, random.nextDouble() * d.height); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/util/Relaxer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/util/Relaxer.java index 79dba9f8..6f37a4c5 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/util/Relaxer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/util/Relaxer.java @@ -2,42 +2,26 @@ /** * Interface for operating the relax iterations on a layout. - * - * @author Tom Nelson - tomnelson@dev.java.net * + * @author Tom Nelson - tomnelson@dev.java.net */ public interface Relaxer { - - /** - * Execute a loop of steps in a new Thread, - * firing an event after each step. - */ - void relax(); - - /** - * Execute a loop of steps in the calling - * thread, firing no events. - */ - void prerelax(); - - /** - * Make the relaxer thread wait. - */ - void pause(); - - /** - * Make the relaxer thread resume. - * - */ - void resume(); - - /** - * Set flags to stop the relaxer thread. - */ - void stop(); - /** - * @param i the sleep time between iterations, in milliseconds - */ - void setSleepTime(long i); + /** Execute a loop of steps in a new Thread, firing an event after each step. */ + void relax(); + + /** Execute a loop of steps in the calling thread, firing no events. */ + void prerelax(); + + /** Make the relaxer thread wait. */ + void pause(); + + /** Make the relaxer thread resume. */ + void resume(); + + /** Set flags to stop the relaxer thread. */ + void stop(); + + /** @param i the sleep time between iterations, in milliseconds */ + void setSleepTime(long i); } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/util/VisRunner.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/util/VisRunner.java index 4fbb9051..d61854d0 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/util/VisRunner.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/layout/util/VisRunner.java @@ -5,142 +5,133 @@ * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. * - * + * */ package edu.uci.ics.jung.algorithms.layout.util; import edu.uci.ics.jung.algorithms.util.IterativeContext; /** - * - * Implementation of a relaxer thread for layouts. - * Extracted from the {@code VisualizationModel} in previous - * versions of JUNG. - * - * @author Tom Nelson - tomnelson@dev.java.net + * Implementation of a relaxer thread for layouts. Extracted from the {@code VisualizationModel} in + * previous versions of JUNG. * + * @author Tom Nelson - tomnelson@dev.java.net */ public class VisRunner implements Relaxer, Runnable { - - protected boolean running; - protected IterativeContext process; - protected boolean stop; - protected boolean manualSuspend; - protected Thread thread; - - /** - * how long the relaxer thread pauses between iteration loops. - */ - protected long sleepTime = 100L; - - - /** - * Creates an instance for the specified process. - * @param process the process (generally a layout) for which this instance is created - */ - public VisRunner(IterativeContext process) { - this.process = process; - } - - /** - * @return the relaxerThreadSleepTime - */ - public long getSleepTime() { - return sleepTime; - } - - /** - * @param sleepTime the sleep time to set for this thread - */ - public void setSleepTime(long sleepTime) { - this.sleepTime = sleepTime; - } - - public void prerelax() { - manualSuspend = true; - long timeNow = System.currentTimeMillis(); - while (System.currentTimeMillis() - timeNow < 500 && !process.done()) { - process.step(); - } - manualSuspend = false; - } - - public void pause() { - manualSuspend = true; - } - - public void relax() { - // in case its running - stop(); - stop = false; - thread = new Thread(this); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); - } - - /** - * Used for synchronization. - */ - public Object pauseObject = new String("PAUSE OBJECT"); - - public void resume() { - manualSuspend = false; - if(running == false) { - prerelax(); - relax(); - } else { - synchronized(pauseObject) { - pauseObject.notifyAll(); - } - } - } - - public synchronized void stop() { - if(thread != null) { - manualSuspend = false; - stop = true; - // interrupt the relaxer, in case it is paused or sleeping - // this should ensure that visRunnerIsRunning gets set to false - try { thread.interrupt(); } - catch(Exception ex) { - // the applet security manager may have prevented this. - // just sleep for a second to let the thread stop on its own - try { Thread.sleep(1000); } - catch(InterruptedException ie) {} // ignore - } - synchronized (pauseObject) { - pauseObject.notifyAll(); - } - } - } - - public void run() { - running = true; - try { - while (!process.done() && !stop) { - synchronized (pauseObject) { - while (manualSuspend && !stop) { - try { - pauseObject.wait(); - } catch (InterruptedException e) { - // ignore - } - } - } - process.step(); - - if (stop) - return; - - try { - Thread.sleep(sleepTime); - } catch (InterruptedException ie) { - // ignore - } - } - - } finally { - running = false; - } - } + + protected boolean running; + protected IterativeContext process; + protected boolean stop; + protected boolean manualSuspend; + protected Thread thread; + + /** how long the relaxer thread pauses between iteration loops. */ + protected long sleepTime = 100L; + + /** + * Creates an instance for the specified process. + * + * @param process the process (generally a layout) for which this instance is created + */ + public VisRunner(IterativeContext process) { + this.process = process; + } + + /** @return the relaxerThreadSleepTime */ + public long getSleepTime() { + return sleepTime; + } + + /** @param sleepTime the sleep time to set for this thread */ + public void setSleepTime(long sleepTime) { + this.sleepTime = sleepTime; + } + + public void prerelax() { + manualSuspend = true; + long timeNow = System.currentTimeMillis(); + while (System.currentTimeMillis() - timeNow < 500 && !process.done()) { + process.step(); + } + manualSuspend = false; + } + + public void pause() { + manualSuspend = true; + } + + public void relax() { + // in case its running + stop(); + stop = false; + thread = new Thread(this); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + } + + /** Used for synchronization. */ + public Object pauseObject = new String("PAUSE OBJECT"); + + public void resume() { + manualSuspend = false; + if (running == false) { + prerelax(); + relax(); + } else { + synchronized (pauseObject) { + pauseObject.notifyAll(); + } + } + } + + public synchronized void stop() { + if (thread != null) { + manualSuspend = false; + stop = true; + // interrupt the relaxer, in case it is paused or sleeping + // this should ensure that visRunnerIsRunning gets set to false + try { + thread.interrupt(); + } catch (Exception ex) { + // the applet security manager may have prevented this. + // just sleep for a second to let the thread stop on its own + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + } // ignore + } + synchronized (pauseObject) { + pauseObject.notifyAll(); + } + } + } + + public void run() { + running = true; + try { + while (!process.done() && !stop) { + synchronized (pauseObject) { + while (manualSuspend && !stop) { + try { + pauseObject.wait(); + } catch (InterruptedException e) { + // ignore + } + } + } + process.step(); + + if (stop) return; + + try { + Thread.sleep(sleepTime); + } catch (InterruptedException ie) { + // ignore + } + } + + } finally { + running = false; + } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/metrics/Metrics.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/metrics/Metrics.java index a570db15..ec083d62 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/metrics/Metrics.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/metrics/Metrics.java @@ -1,71 +1,63 @@ /** - * Copyright (c) 2008, The JUNG Authors + * Copyright (c) 2008, The JUNG Authors * - * All rights reserved. + *

All rights reserved. * - * This software is open-source under the BSD license; see either - * "license.txt" or - * https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * Created on Jun 7, 2008 - * + *

This software is open-source under the BSD license; see either "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. Created on Jun 7, 2008 */ package edu.uci.ics.jung.algorithms.metrics; import com.google.common.collect.ImmutableMap; import com.google.common.graph.Graph; +/** A class consisting of static methods for calculating graph metrics. */ +public class Metrics { + /** + * Returns a Map of vertices to their clustering coefficients. The clustering + * coefficient cc(v) of a vertex v is defined as follows: + * + *

    + *
  • degree(v) == {0,1}: 0 + *
  • degree(v) == n, n >= 2: given S, the set of neighbors of v: + * cc(v) = (the sum over all w in S of the number of other elements of w that are neighbors + * of w) / ((|S| * (|S| - 1) / 2). Less formally, the fraction of v's neighbors + * that are also neighbors of each other. + *
+ * + *

Note: This algorithm treats its argument as an undirected graph; edge direction is + * ignored. + * + * @param graph the graph whose clustering coefficients are to be calculated + * @param the vertex type + * @param the edge type + * @return the clustering coefficient for each vertex + * @see "The structure and function of complex networks, M.E.J. Newman, + * aps.arxiv.org/abs/cond-mat/0303516" + */ + public static ImmutableMap clusteringCoefficients(Graph graph) { + ImmutableMap.Builder coefficients = ImmutableMap.builder(); -/** - * A class consisting of static methods for calculating graph metrics. - */ -public class Metrics -{ - /** - * Returns a Map of vertices to their clustering coefficients. - * The clustering coefficient cc(v) of a vertex v is defined as follows: - *

    - *
  • degree(v) == {0,1}: 0 - *
  • degree(v) == n, n >= 2: given S, the set of neighbors - * of v: cc(v) = (the sum over all w in S of the number of - * other elements of w that are neighbors of w) / ((|S| * (|S| - 1) / 2). - * Less formally, the fraction of v's neighbors that are also - * neighbors of each other. - *
- *

Note: This algorithm treats its argument as an undirected graph; - * edge direction is ignored. - * @param graph the graph whose clustering coefficients are to be calculated - * @param the vertex type - * @param the edge type - * @return the clustering coefficient for each vertex - * @see "The structure and function of complex networks, M.E.J. Newman, aps.arxiv.org/abs/cond-mat/0303516" - */ - public static ImmutableMap clusteringCoefficients(Graph graph) - { - ImmutableMap.Builder coefficients = ImmutableMap.builder(); - - for (V v : graph.nodes()) - { - int n = graph.degree(v); - if (n < 2) - coefficients.put(v, new Double(0)); - else - { - int edgeCount = 0; - for (V w : graph.adjacentNodes(v)) { - if (!w.equals(v)) { - for (V x : graph.adjacentNodes(v)) { - // TODO: replace with hasEdge() once it's ready - if (!w.equals(x) && graph.adjacentNodes(w).contains(x)) { - edgeCount++; - } - } - } - } - double possible_edges = (n * (n - 1))/2.0; - coefficients.put(v, new Double(edgeCount / possible_edges)); + for (V v : graph.nodes()) { + int n = graph.degree(v); + if (n < 2) coefficients.put(v, new Double(0)); + else { + int edgeCount = 0; + for (V w : graph.adjacentNodes(v)) { + if (!w.equals(v)) { + for (V x : graph.adjacentNodes(v)) { + // TODO: replace with hasEdge() once it's ready + if (!w.equals(x) && graph.adjacentNodes(w).contains(x)) { + edgeCount++; + } } + } } - - return coefficients.build(); + double possible_edges = (n * (n - 1)) / 2.0; + coefficients.put(v, new Double(edgeCount / possible_edges)); + } } + + return coefficients.build(); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/metrics/StructuralHoles.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/metrics/StructuralHoles.java index 9c2ad87c..e2748746 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/metrics/StructuralHoles.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/metrics/StructuralHoles.java @@ -1,7 +1,7 @@ /* * Created on Sep 19, 2005 * - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,343 +11,325 @@ */ package edu.uci.ics.jung.algorithms.metrics; -import java.util.function.BiFunction; - import com.google.common.graph.Graph; - +import java.util.function.BiFunction; /** - * Calculates some of the measures from Burt's text "Structural Holes: - * The Social Structure of Competition". - * - *

Notes: + * Calculates some of the measures from Burt's text "Structural Holes: The Social Structure of + * Competition". + * + *

Notes: + * *

    - *
  • Each of these measures assumes that each edge has an associated - * non-null weight whose value is accessed through the specified - * Transformer instance. - *
  • Nonexistent edges are treated as edges with weight 0 for purposes - * of edge weight calculations. + *
  • Each of these measures assumes that each edge has an associated non-null weight whose value + * is accessed through the specified Transformer instance. + *
  • Nonexistent edges are treated as edges with weight 0 for purposes of edge weight + * calculations. *
- * - *

Based on code donated by Jasper Voskuilen and - * Diederik van Liere of the Department of Information and Decision Sciences - * at Erasmus University. - * + * + *

Based on code donated by Jasper Voskuilen and Diederik van Liere of the Department of + * Information and Decision Sciences at Erasmus University. + * * @author Joshua O'Madadhain * @author Jasper Voskuilen * @see "Ronald Burt, Structural Holes: The Social Structure of Competition" * @author Tom Nelson - converted to jung2 */ public class StructuralHoles { - - protected BiFunction edge_weight; - protected Graph g; - - /** - * @param graph the graph for which the metrics are to be calculated - * @param nev the edge weights - */ - public StructuralHoles(Graph graph, BiFunction nev) - { - this.g = graph; - this.edge_weight = nev; - } - /** - * Burt's measure of the effective size of a vertex's network. Essentially, the - * number of neighbors minus the average degree of those in v's neighbor set, - * not counting ties to v. Formally: - *

-     * effectiveSize(v) = v.degree() - (sum_{u in N(v)} sum_{w in N(u), w !=u,v} p(v,w)*m(u,w))
-     * 
- * where - *
    - *
  • N(a) = a.adjacentNodes() - *
  • p(v,w) = normalized mutual edge weight of v and w - *
  • m(u,w) = maximum-scaled mutual edge weight of u and w - *
- * @param v the vertex whose properties are being measured - * @return the effective size of the vertex's network - * - * @see #normalizedMutualEdgeWeight(Object, Object) - * @see #maxScaledMutualEdgeWeight(Object, Object) - */ - public double effectiveSize(V v) - { - double result = g.degree(v); - for(V u : g.adjacentNodes(v)) { - - for(V w : g.adjacentNodes(u)) { - - if (w != v && w != u) - result -= normalizedMutualEdgeWeight(v,w) * - maxScaledMutualEdgeWeight(u,w); - } - } - return result; - } - - /** - * Returns the effective size of v divided by the number of - * alters in v's network. (In other words, - * effectiveSize(v) / v.degree().) - * If v.degree() == 0, returns 0. - * - * @param v the vertex whose properties are being measured - * @return the effective size of the vertex divided by its degree - */ - public double efficiency(V v) { - double degree = g.degree(v); - - if (degree == 0) - return 0; - else - return effectiveSize(v) / degree; - } + protected BiFunction edge_weight; + protected Graph g; - /** - * Burt's constraint measure (equation 2.4, page 55 of Burt, 1992). Essentially a - * measure of the extent to which v is invested in people who are invested in - * other of v's alters (neighbors). The "constraint" is characterized - * by a lack of primary holes around each neighbor. Formally: - *
-     * constraint(v) = sum_{w in MP(v), w != v} localConstraint(v,w)
-     * 
- * where MP(v) is the subset of v's neighbors that are both predecessors and successors of v. - * @see #localConstraint(Object, Object) - * - * @param v the vertex whose properties are being measured - * @return the constraint of the vertex - */ - public double constraint(V v) { - double result = 0; - for(V w : g.successors(v)) { - - if (v != w && g.predecessors(v).contains(w)) - { - result += localConstraint(v, w); - } - } - - return result; + /** + * @param graph the graph for which the metrics are to be calculated + * @param nev the edge weights + */ + public StructuralHoles(Graph graph, BiFunction nev) { + this.g = graph; + this.edge_weight = nev; + } + + /** + * Burt's measure of the effective size of a vertex's network. Essentially, the number of + * neighbors minus the average degree of those in v's neighbor set, not counting ties + * to v. Formally: + * + *
+   * effectiveSize(v) = v.degree() - (sum_{u in N(v)} sum_{w in N(u), w !=u,v} p(v,w)*m(u,w))
+   * 
+ * + * where + * + *
    + *
  • N(a) = a.adjacentNodes() + *
  • p(v,w) = normalized mutual edge weight of v and w + *
  • m(u,w) = maximum-scaled mutual edge weight of u and w + *
+ * + * @param v the vertex whose properties are being measured + * @return the effective size of the vertex's network + * @see #normalizedMutualEdgeWeight(Object, Object) + * @see #maxScaledMutualEdgeWeight(Object, Object) + */ + public double effectiveSize(V v) { + double result = g.degree(v); + for (V u : g.adjacentNodes(v)) { + + for (V w : g.adjacentNodes(u)) { + + if (w != v && w != u) + result -= normalizedMutualEdgeWeight(v, w) * maxScaledMutualEdgeWeight(u, w); + } } + return result; + } + + /** + * Returns the effective size of v divided by the number of alters in v + * 's network. (In other words, effectiveSize(v) / v.degree().) If + * v.degree() == 0, returns 0. + * + * @param v the vertex whose properties are being measured + * @return the effective size of the vertex divided by its degree + */ + public double efficiency(V v) { + double degree = g.degree(v); + + if (degree == 0) return 0; + else return effectiveSize(v) / degree; + } - - /** - * Calculates the hierarchy value for a given vertex. Returns NaN when - * v's degree is 0, and 1 when v's degree is 1. - * Formally: - *
-     * hierarchy(v) = (sum_{v in N(v), w != v} s(v,w) * log(s(v,w))}) / (v.degree() * Math.log(v.degree()) 
-     * 
- * where - *
    - *
  • N(v) = v.adjacentNodes() - *
  • s(v,w) = localConstraint(v,w) / (aggregateConstraint(v) / v.degree()) - *
- * @see #localConstraint(Object, Object) - * @see #aggregateConstraint(Object) - * - * @param v the vertex whose properties are being measured - * @return the hierarchy value for a given vertex - */ - public double hierarchy(V v) - { - double v_degree = g.degree(v); - - if (v_degree == 0) - return Double.NaN; - if (v_degree == 1) - return 1; - - double v_constraint = aggregateConstraint(v); - - double numerator = 0; - for (V w : g.adjacentNodes(v)) { - - if (v != w) - { - double sl_constraint = localConstraint(v, w) / (v_constraint / v_degree); - numerator += sl_constraint * Math.log(sl_constraint); - } - } - - return numerator / (v_degree * Math.log(v_degree)); + /** + * Burt's constraint measure (equation 2.4, page 55 of Burt, 1992). Essentially a measure of the + * extent to which v is invested in people who are invested in other of v + * 's alters (neighbors). The "constraint" is characterized by a lack of primary holes + * around each neighbor. Formally: + * + *
+   * constraint(v) = sum_{w in MP(v), w != v} localConstraint(v,w)
+   * 
+ * + * where MP(v) is the subset of v's neighbors that are both predecessors and successors of v. + * + * @see #localConstraint(Object, Object) + * @param v the vertex whose properties are being measured + * @return the constraint of the vertex + */ + public double constraint(V v) { + double result = 0; + for (V w : g.successors(v)) { + + if (v != w && g.predecessors(v).contains(w)) { + result += localConstraint(v, w); + } } - /** - * Returns the local constraint on v1 from a lack of primary holes - * around its neighbor v2. - * Based on Burt's equation 2.4. Formally: - *
-     * localConstraint(v1, v2) = ( p(v1,v2) + ( sum_{w in N(v)} p(v1,w) * p(w, v2) ) )^2
-     * 
- * where - *
    - *
  • N(v) = v.adjacentNodes() - *
  • p(v,w) = normalized mutual edge weight of v and w - *
- * @param v1 the first vertex whose local constraint is desired - * @param v2 the second vertex whose local constraint is desired - * @return the local constraint on (v1, v2) - * @see #normalizedMutualEdgeWeight(Object, Object) - */ - public double localConstraint(V v1, V v2) - { - double nmew_vw = normalizedMutualEdgeWeight(v1, v2); - double inner_result = 0; - for (V w : g.adjacentNodes(v1)) { - - inner_result += normalizedMutualEdgeWeight(v1,w) * - normalizedMutualEdgeWeight(w,v2); - } - return (nmew_vw + inner_result) * (nmew_vw + inner_result); + return result; + } + + /** + * Calculates the hierarchy value for a given vertex. Returns NaN when v + * 's degree is 0, and 1 when v's degree is 1. Formally: + * + *
+   * hierarchy(v) = (sum_{v in N(v), w != v} s(v,w) * log(s(v,w))}) / (v.degree() * Math.log(v.degree())
+   * 
+ * + * where + * + *
    + *
  • N(v) = v.adjacentNodes() + *
  • s(v,w) = localConstraint(v,w) / (aggregateConstraint(v) / v.degree()) + *
+ * + * @see #localConstraint(Object, Object) + * @see #aggregateConstraint(Object) + * @param v the vertex whose properties are being measured + * @return the hierarchy value for a given vertex + */ + public double hierarchy(V v) { + double v_degree = g.degree(v); + + if (v_degree == 0) return Double.NaN; + if (v_degree == 1) return 1; + + double v_constraint = aggregateConstraint(v); + + double numerator = 0; + for (V w : g.adjacentNodes(v)) { + + if (v != w) { + double sl_constraint = localConstraint(v, w) / (v_constraint / v_degree); + numerator += sl_constraint * Math.log(sl_constraint); + } } - - /** - * The aggregate constraint on v. Based on Burt's equation 2.7. - * Formally: - *
-     * aggregateConstraint(v) = sum_{w in N(v)} localConstraint(v,w) * O(w)
-     * 
- * where - *
    - *
  • N(v) = v.adjacentNodes() - *
  • O(w) = organizationalMeasure(w) - *
- * - * @param v the vertex whose properties are being measured - * @return the aggregate constraint on v - */ - public double aggregateConstraint(V v) - { - double result = 0; - for (V w : g.adjacentNodes(v)) { - - result += localConstraint(v, w) * organizationalMeasure(g, w); - } - return result; + + return numerator / (v_degree * Math.log(v_degree)); + } + + /** + * Returns the local constraint on v1 from a lack of primary holes around its + * neighbor v2. Based on Burt's equation 2.4. Formally: + * + *
+   * localConstraint(v1, v2) = ( p(v1,v2) + ( sum_{w in N(v)} p(v1,w) * p(w, v2) ) )^2
+   * 
+ * + * where + * + *
    + *
  • N(v) = v.adjacentNodes() + *
  • p(v,w) = normalized mutual edge weight of v and w + *
+ * + * @param v1 the first vertex whose local constraint is desired + * @param v2 the second vertex whose local constraint is desired + * @return the local constraint on (v1, v2) + * @see #normalizedMutualEdgeWeight(Object, Object) + */ + public double localConstraint(V v1, V v2) { + double nmew_vw = normalizedMutualEdgeWeight(v1, v2); + double inner_result = 0; + for (V w : g.adjacentNodes(v1)) { + + inner_result += normalizedMutualEdgeWeight(v1, w) * normalizedMutualEdgeWeight(w, v2); } - - /** - * A measure of the organization of individuals within the subgraph - * centered on v. Burt's text suggests that this is - * in some sense a measure of how "replaceable" v is by - * some other element of this subgraph. Should be a number in the - * closed interval [0,1]. - * - *

This implementation returns 1. Users may wish to override this - * method in order to define their own behavior. - * @param g the subgraph centered on v - * @param v the vertex whose properties are being measured - * @return 1.0 (in this implementation) - */ - protected double organizationalMeasure(Graph g, V v) { - return 1.0; + return (nmew_vw + inner_result) * (nmew_vw + inner_result); + } + + /** + * The aggregate constraint on v. Based on Burt's equation 2.7. Formally: + * + *

+   * aggregateConstraint(v) = sum_{w in N(v)} localConstraint(v,w) * O(w)
+   * 
+ * + * where + * + *
    + *
  • N(v) = v.adjacentNodes() + *
  • O(w) = organizationalMeasure(w) + *
+ * + * @param v the vertex whose properties are being measured + * @return the aggregate constraint on v + */ + public double aggregateConstraint(V v) { + double result = 0; + for (V w : g.adjacentNodes(v)) { + + result += localConstraint(v, w) * organizationalMeasure(g, w); } - - - /** - * Returns the proportion of v1's network time and energy invested - * in the relationship with v2. Formally: - *
-     * normalizedMutualEdgeWeight(a,b) = mutual_weight(a,b) / (sum_c mutual_weight(a,c))
-     * 
- * Returns 0 if either numerator or denominator = 0, or if v1 == v2. - * @see #mutualWeight(Object, Object) - * @param v1 the first vertex of the pair whose property is being measured - * @param v2 the second vertex of the pair whose property is being measured - * @return the normalized mutual edge weight between v1 and v2 - */ - protected double normalizedMutualEdgeWeight(V v1, V v2) - { - if (v1 == v2) - return 0; - - double numerator = mutualWeight(v1, v2); - - if (numerator == 0) - return 0; - - double denominator = 0; - for (V v : g.adjacentNodes(v1)) { - denominator += mutualWeight(v1, v); - } - if (denominator == 0) - return 0; - - return numerator / denominator; + return result; + } + + /** + * A measure of the organization of individuals within the subgraph centered on v. + * Burt's text suggests that this is in some sense a measure of how "replaceable" v + * is by some other element of this subgraph. Should be a number in the closed interval [0,1]. + * + *

This implementation returns 1. Users may wish to override this method in order to define + * their own behavior. + * + * @param g the subgraph centered on v + * @param v the vertex whose properties are being measured + * @return 1.0 (in this implementation) + */ + protected double organizationalMeasure(Graph g, V v) { + return 1.0; + } + + /** + * Returns the proportion of v1's network time and energy invested in the + * relationship with v2. Formally: + * + *

+   * normalizedMutualEdgeWeight(a,b) = mutual_weight(a,b) / (sum_c mutual_weight(a,c))
+   * 
+ * + * Returns 0 if either numerator or denominator = 0, or if v1 == v2. + * + * @see #mutualWeight(Object, Object) + * @param v1 the first vertex of the pair whose property is being measured + * @param v2 the second vertex of the pair whose property is being measured + * @return the normalized mutual edge weight between v1 and v2 + */ + protected double normalizedMutualEdgeWeight(V v1, V v2) { + if (v1 == v2) return 0; + + double numerator = mutualWeight(v1, v2); + + if (numerator == 0) return 0; + + double denominator = 0; + for (V v : g.adjacentNodes(v1)) { + denominator += mutualWeight(v1, v); } - - /** - * Returns the weight of the edge from v1 to v2 - * plus the weight of the edge from v2 to v1; - * if either edge does not exist, it is treated as an edge with weight 0. - * Undirected edges are treated as two antiparallel directed edges (that - * is, if there is one undirected edge with weight w connecting - * v1 to v2, the value returned is 2w). - * Ignores parallel edges; if there are any such, one is chosen at random. - * Throws NullPointerException if either edge is - * present but not assigned a weight by the constructor-specified - * NumberEdgeValue. - * - * @param v1 the first vertex of the pair whose property is being measured - * @param v2 the second vertex of the pair whose property is being measured - * @return the weights of the edges {@code} and {@code } - */ - protected double mutualWeight(V v1, V v2) - { - double weight = 0; - if (g.isDirected()) { - if (g.successors(v1).contains(v2)) { - weight += edge_weight.apply(v1, v2).doubleValue(); - } - if (g.successors(v2).contains(v1)) { - weight += edge_weight.apply(v2, v1).doubleValue(); - } - } else { - if (g.adjacentNodes(v1).contains(v2)) { - weight += edge_weight.apply(v1, v2).doubleValue(); - } - } - - return weight; + if (denominator == 0) return 0; + + return numerator / denominator; + } + + /** + * Returns the weight of the edge from v1 to v2 plus the weight of the + * edge from v2 to v1; if either edge does not exist, it is treated as + * an edge with weight 0. Undirected edges are treated as two antiparallel directed edges (that + * is, if there is one undirected edge with weight w connecting v1 to v2 + * , the value returned is 2w). Ignores parallel edges; if there are any such, one + * is chosen at random. Throws NullPointerException if either edge is present but not + * assigned a weight by the constructor-specified NumberEdgeValue. + * + * @param v1 the first vertex of the pair whose property is being measured + * @param v2 the second vertex of the pair whose property is being measured + * @return the weights of the edges {@code} and {@code } + */ + protected double mutualWeight(V v1, V v2) { + double weight = 0; + if (g.isDirected()) { + if (g.successors(v1).contains(v2)) { + weight += edge_weight.apply(v1, v2).doubleValue(); + } + if (g.successors(v2).contains(v1)) { + weight += edge_weight.apply(v2, v1).doubleValue(); + } + } else { + if (g.adjacentNodes(v1).contains(v2)) { + weight += edge_weight.apply(v1, v2).doubleValue(); + } } - - /** - * The marginal strength of v1's relation with contact v2. - * Formally: - *
-     * normalized_mutual_weight = mutual_weight(a,b) / (max_c mutual_weight(a,c))
-     * 
- * Returns 0 if either numerator or denominator is 0, or if v1 == v2. - * - * @param v1 the first vertex of the pair whose property is being measured - * @param v2 the second vertex of the pair whose property is being measured - * @return the marginal strength of v1's relation with v2 - * - * @see #mutualWeight(Object, Object) - */ - protected double maxScaledMutualEdgeWeight(V v1, V v2) - { - if (v1 == v2) - return 0; - - double numerator = mutualWeight(v1, v2); - - if (numerator == 0) - return 0; - - double denominator = 0; - for (V w : g.adjacentNodes(v1)) { - - if (v2 != w) - denominator = Math.max(numerator, mutualWeight(v1, w)); - } - - if (denominator == 0) - return 0; - - return numerator / denominator; + + return weight; + } + + /** + * The marginal strength of v1's relation with contact v2. Formally: + * + *
+   * normalized_mutual_weight = mutual_weight(a,b) / (max_c mutual_weight(a,c))
+   * 
+ * + * Returns 0 if either numerator or denominator is 0, or if v1 == v2. + * + * @param v1 the first vertex of the pair whose property is being measured + * @param v2 the second vertex of the pair whose property is being measured + * @return the marginal strength of v1's relation with v2 + * @see #mutualWeight(Object, Object) + */ + protected double maxScaledMutualEdgeWeight(V v1, V v2) { + if (v1 == v2) return 0; + + double numerator = mutualWeight(v1, v2); + + if (numerator == 0) return 0; + + double denominator = 0; + for (V w : g.adjacentNodes(v1)) { + + if (v2 != w) denominator = Math.max(numerator, mutualWeight(v1, w)); } + + if (denominator == 0) return 0; + + return numerator / denominator; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/metrics/TriadicCensus.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/metrics/TriadicCensus.java index 9bbc1815..91adedc8 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/metrics/TriadicCensus.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/metrics/TriadicCensus.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, The JUNG Authors + * Copyright (c) 2003, The JUNG Authors * * All rights reserved. * @@ -9,43 +9,37 @@ */ package edu.uci.ics.jung.algorithms.metrics; +import com.google.common.base.Preconditions; +import com.google.common.graph.Graph; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import com.google.common.base.Preconditions; -import com.google.common.graph.Graph; - - - /** - * TriadicCensus is a standard social network tool that counts, for each of the - * different possible configurations of three vertices, the number of times - * that that configuration occurs in the given graph. - * This may then be compared to the set of expected counts for this particular - * graph or to an expected sample. This is often used in p* modeling. - *

- * To use this class, + * TriadicCensus is a standard social network tool that counts, for each of the different possible + * configurations of three vertices, the number of times that that configuration occurs in the given + * graph. This may then be compared to the set of expected counts for this particular graph or to an + * expected sample. This is often used in p* modeling. + * + *

To use this class, + * *

  * long[] triad_counts = TriadicCensus(dg);
  * 
- * where dg is a DirectedGraph. - * ith element of the array (for i in [1,16]) is the number of - * occurrences of the corresponding triad type. - * (The 0th element is not meaningful; this array is effectively 1-based.) - * To get the name of the ith triad (e.g. "003"), - * look at the global constant array c.TRIAD_NAMES[i] - *

- * Triads are named as - * (number of pairs that are mutually tied) - * (number of pairs that are one-way tied) - * (number of non-tied pairs) - * in the triple. Since there are be only three pairs, there is a finite - * set of these possible triads. - *

- * In fact, there are exactly 16, conventionally sorted by the number of - * realized edges in the triad: + * + * where dg is a DirectedGraph. ith element of the array (for i in [1,16]) + * is the number of occurrences of the corresponding triad type. (The 0th element is not meaningful; + * this array is effectively 1-based.) To get the name of the ith triad (e.g. "003"), look at the + * global constant array c.TRIAD_NAMES[i] + * + *

Triads are named as (number of pairs that are mutually tied) (number of pairs that are one-way + * tied) (number of non-tied pairs) in the triple. Since there are be only three pairs, there is a + * finite set of these possible triads. + * + *

In fact, there are exactly 16, conventionally sorted by the number of realized edges in the + * triad: + * * * * @@ -66,146 +60,140 @@ * * *
Descriptions of the different types of triads
Number Configuration Notes
15210
16300The complete
- *

- * This implementation takes O( m ), m is the number of edges in the graph. - *
- * It is based on - * - * A subquadratic triad census algorithm for large sparse networks - * with small maximum degree - * Vladimir Batagelj and Andrej Mrvar, University of Ljubljana - * Published in Social Networks. + * + *

This implementation takes O( m ), m is the number of edges in the graph.
+ * It is based on A + * subquadratic triad census algorithm for large sparse networks with small maximum degree + * Vladimir Batagelj and Andrej Mrvar, University of Ljubljana Published in Social Networks. + * * @author Danyel Fisher * @author Tom Nelson - converted to jung2 - * */ public class TriadicCensus { - // NOTE THAT THIS RETURNS STANDARD 1-16 COUNT! + // NOTE THAT THIS RETURNS STANDARD 1-16 COUNT! + + // and their types + public static final String[] TRIAD_NAMES = { + "N/A", "003", "012", "102", "021D", "021U", "021C", "111D", "111U", "030T", "030C", "201", + "120D", "120U", "120C", "210", "300" + }; - // and their types - public static final String[] TRIAD_NAMES = { "N/A", "003", "012", "102", "021D", - "021U", "021C", "111D", "111U", "030T", "030C", "201", "120D", - "120U", "120C", "210", "300" }; + public static final int MAX_TRIADS = TRIAD_NAMES.length; - public static final int MAX_TRIADS = TRIAD_NAMES.length; + /** + * Returns an array whose ith element (for i in [1,16]) is the number of occurrences of the + * corresponding triad type in g. (The 0th element is not meaningful; this array is + * effectively 1-based.) + * + * @param g the graph whose properties are being measured + * @param the vertex type + * @return an array encoding the number of occurrences of each triad type + */ + public static long[] getCounts(Graph g) { + Preconditions.checkArgument(g.isDirected(), "input graph must be directed"); + long[] count = new long[MAX_TRIADS]; - /** - * Returns an array whose ith element (for i in [1,16]) is the number of - * occurrences of the corresponding triad type in g. - * (The 0th element is not meaningful; this array is effectively 1-based.) - * - * @param g the graph whose properties are being measured - * @param the vertex type - * @return an array encoding the number of occurrences of each triad type - */ - public static long[] getCounts(Graph g) { - Preconditions.checkArgument(g.isDirected(), "input graph must be directed"); - long[] count = new long[MAX_TRIADS]; + // TODO: can we make this more efficient and not require the extra list? + List id = new ArrayList(g.nodes()); - // TODO: can we make this more efficient and not require the extra list? - List id = new ArrayList(g.nodes()); + // apply algorithm to each edge, one at at time + for (int i_v = 0; i_v < id.size(); i_v++) { + V v = id.get(i_v); + for (V u : g.adjacentNodes(v)) { + int triType = -1; + if (id.indexOf(u) <= i_v) continue; + Set neighbors = new HashSet(g.adjacentNodes(u)); + neighbors.addAll(g.adjacentNodes(v)); + neighbors.remove(u); + neighbors.remove(v); + // TODO: use hasEdge() when available + if (g.successors(v).contains(u) && g.successors(u).contains(v)) { + triType = 3; + } else { + triType = 2; + } + count[triType] += id.size() - neighbors.size() - 2; + for (V w : neighbors) { + if (shouldCount(g, id, u, v, w)) { + count[triType(triCode(g, u, v, w))]++; + } + } + } + } + int sum = 0; + for (int i = 2; i <= 16; i++) { + sum += count[i]; + } + int n = id.size(); + count[1] = n * (n - 1) * (n - 2) / 6 - sum; + return count; + } - // apply algorithm to each edge, one at at time - for (int i_v = 0; i_v < id.size(); i_v++) { - V v = id.get(i_v); - for(V u : g.adjacentNodes(v)) { - int triType = -1; - if (id.indexOf(u) <= i_v) - continue; - Set neighbors = new HashSet(g.adjacentNodes(u)); - neighbors.addAll(g.adjacentNodes(v)); - neighbors.remove(u); - neighbors.remove(v); - // TODO: use hasEdge() when available - if (g.successors(v).contains(u) && g.successors(u).contains(v)) { - triType = 3; - } else { - triType = 2; - } - count[triType] += id.size() - neighbors.size() - 2; - for (V w : neighbors) { - if (shouldCount(g, id, u, v, w)) { - count [ triType ( triCode(g, u, v, w) ) ] ++; - } - } - } - } - int sum = 0; - for (int i = 2; i <= 16; i++) { - sum += count[i]; - } - int n = id.size(); - count[1] = n * (n-1) * (n-2) / 6 - sum; - return count; - } + /** + * This is the core of the technique in the paper. Returns an int from 0 to 63 which encodes the + * presence of all possible links between u, v, and w as bit flags: WU = 32, UW = 16, WV = 8, VW = + * 4, UV = 2, VU = 1 + * + * @param g the graph for which the calculation is being made + * @param u a vertex in g + * @param v a vertex in g + * @param w a vertex in g + * @param the vertex type + * @return an int encoding the presence of all links between u, v, and w + */ + public static int triCode(Graph g, V u, V v, V w) { + int i = 0; + i += link(g, v, u) ? 1 : 0; + i += link(g, u, v) ? 2 : 0; + i += link(g, v, w) ? 4 : 0; + i += link(g, w, v) ? 8 : 0; + i += link(g, u, w) ? 16 : 0; + i += link(g, w, u) ? 32 : 0; + return i; + } - /** - * This is the core of the technique in the paper. Returns an int from 0 to - * 63 which encodes the presence of all possible links between u, v, and w - * as bit flags: WU = 32, UW = 16, WV = 8, VW = 4, UV = 2, VU = 1 - * - * @param g the graph for which the calculation is being made - * @param u a vertex in g - * @param v a vertex in g - * @param w a vertex in g - * @param the vertex type - * @return an int encoding the presence of all links between u, v, and w - */ - public static int triCode(Graph g, V u, V v, V w) { - int i = 0; - i += link(g, v, u ) ? 1 : 0; - i += link(g, u, v ) ? 2 : 0; - i += link(g, v, w ) ? 4 : 0; - i += link(g, w, v ) ? 8 : 0; - i += link(g, u, w ) ? 16 : 0; - i += link(g, w, u ) ? 32 : 0; - return i; - } + protected static boolean link(Graph g, V a, V b) { + return g.predecessors(b).contains(a); + } - protected static boolean link(Graph g, V a, V b) { - return g.predecessors(b).contains(a); - } - - - /** - * @param triCode the code returned by {@code triCode()} - * @return the string code associated with the numeric type - */ - public static int triType( int triCode ) { - return codeToType[ triCode ]; - } + /** + * @param triCode the code returned by {@code triCode()} + * @return the string code associated with the numeric type + */ + public static int triType(int triCode) { + return codeToType[triCode]; + } - /** - * For debugging purposes, this is copied straight out of the paper which - * means that they refer to triad types 1-16. - */ - protected static final int[] codeToType = { 1, 2, 2, 3, 2, 4, 6, 8, 2, 6, 5, 7, 3, 8, - 7, 11, 2, 6, 4, 8, 5, 9, 9, 13, 6, 10, 9, 14, 7, 14, 12, 15, 2, 5, - 6, 7, 6, 9, 10, 14, 4, 9, 9, 12, 8, 13, 14, 15, 3, 7, 8, 11, 7, 12, - 14, 15, 8, 14, 13, 15, 11, 15, 15, 16 }; + /** + * For debugging purposes, this is copied straight out of the paper which means that they refer to + * triad types 1-16. + */ + protected static final int[] codeToType = { + 1, 2, 2, 3, 2, 4, 6, 8, 2, 6, 5, 7, 3, 8, 7, 11, 2, 6, 4, 8, 5, 9, 9, 13, 6, 10, 9, 14, 7, 14, + 12, 15, 2, 5, 6, 7, 6, 9, 10, 14, 4, 9, 9, 12, 8, 13, 14, 15, 3, 7, 8, 11, 7, 12, 14, 15, 8, 14, + 13, 15, 11, 15, 15, 16 + }; - /** - * Return true iff this ordering is canonical and therefore we should build statistics for it. - * - * @param g the graph whose properties are being examined - * @param id a list of the vertices in g; used to assign an index to each - * @param u a vertex in g - * @param v a vertex in g - * @param w a vertex in g - * @param the vertex type - * @param the edge type - * @return true if index(u) < index(w), or if index(v) < index(w) < index(u) - * and v doesn't link to w; false otherwise - */ - protected static boolean shouldCount(Graph g, List id, V u, V v, V w) { - int i_u = id.indexOf(u); - int i_w = id.indexOf(w); - if (i_u < i_w) - return true; - int i_v = id.indexOf(v); - if ((i_v < i_w) && (i_w < i_u) && (!g.adjacentNodes(w).contains(v))) - return true; - return false; - } + /** + * Return true iff this ordering is canonical and therefore we should build statistics for it. + * + * @param g the graph whose properties are being examined + * @param id a list of the vertices in g; used to assign an index to each + * @param u a vertex in g + * @param v a vertex in g + * @param w a vertex in g + * @param the vertex type + * @param the edge type + * @return true if index(u) < index(w), or if index(v) < index(w) < index(u) and v + * doesn't link to w; false otherwise + */ + protected static boolean shouldCount(Graph g, List id, V u, V v, V w) { + int i_u = id.indexOf(u); + int i_w = id.indexOf(w); + if (i_u < i_w) return true; + int i_v = id.indexOf(v); + if ((i_v < i_w) && (i_w < i_u) && (!g.adjacentNodes(w).contains(v))) return true; + return false; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/AbstractIterativeScorer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/AbstractIterativeScorer.java index f913aaaf..a89e63b0 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/AbstractIterativeScorer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/AbstractIterativeScorer.java @@ -1,7 +1,7 @@ /* * Created on Jul 6, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -11,334 +11,300 @@ */ package edu.uci.ics.jung.algorithms.scoring; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.scoring.util.DelegateToEdgeTransformer; import edu.uci.ics.jung.algorithms.scoring.util.VEPair; import edu.uci.ics.jung.algorithms.util.IterativeContext; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; /** * An abstract class for algorithms that assign scores to vertices based on iterative methods. * Generally, any (concrete) subclass will function by creating an instance, and then either calling - * evaluate (if the user wants to iterate until the algorithms is 'done') or - * repeatedly call step (if the user wants to observe the values at each step). + * evaluate (if the user wants to iterate until the algorithms is 'done') or repeatedly + * call step (if the user wants to observe the values at each step). */ -public abstract class AbstractIterativeScorer implements IterativeContext, VertexScorer -{ - /** - * Maximum number of iterations to use before terminating. Defaults to 100. - */ - protected int max_iterations; - - /** - * Minimum change from one step to the next; if all changes are ≤ tolerance, - * no further updates will occur. - * Defaults to 0.001. - */ - protected double tolerance; - - /** - * The graph on which the calculations are to be made. - */ - protected Network graph; - - /** - * The total number of iterations used so far. - */ - protected int total_iterations; - - /** - * The edge weights used by this algorithm. - */ - protected Function, ? extends Number> edge_weights; - - /** - * The map in which the output values are stored. - */ - private Map output; - - /** - * The map in which the current values are stored. - */ - private Map current_values; - - /** - * A flag representing whether this instance tolerates disconnected graphs. - * Instances that do not accept disconnected graphs may have unexpected behavior - * on disconnected graphs; they are not guaranteed to do an explicit check. - * Defaults to true. - */ - private boolean accept_disconnected_graph; - - /** - * Sets the output value for this vertex. - * @param v the vertex whose output value is to be set - * @param value the value to set - */ - protected void setOutputValue(V v, T value) - { - output.put(v, value); - } - - /** - * Gets the output value for this vertex. - * @param v the vertex whose output value is to be retrieved - * @return the output value for this vertex - */ - protected T getOutputValue(V v) - { - return output.get(v); - } - - /** - * Gets the current value for this vertex - * @param v the vertex whose current value is to be retrieved - * @return the current value for this vertex - */ - protected T getCurrentValue(V v) - { - return current_values.get(v); - } - - /** - * Sets the current value for this vertex. - * @param v the vertex whose current value is to be set - * @param value the current value to set - */ - protected void setCurrentValue(V v, T value) - { - current_values.put(v, value); - } - - /** - * The largest change seen so far among all vertex scores. - */ - protected double max_delta; - - /** - * Creates an instance for the specified graph and edge weights. - * @param g the graph for which the instance is to be created - * @param edge_weights the edge weights for this instance - */ - public AbstractIterativeScorer(Network g, - Function edge_weights) - { - this.graph = g; - this.max_iterations = 100; - this.tolerance = 0.001; - this.accept_disconnected_graph = true; - setEdgeWeights(edge_weights); - } - - /** - * Creates an instance for the specified graph g. - * NOTE: This constructor does not set the internal - * edge_weights variable. If this variable is used by - * the subclass which invoked this constructor, it must be initialized - * by that subclass. - * @param g the graph for which the instance is to be created - */ - public AbstractIterativeScorer(Network g) - { - this.graph = g; - this.max_iterations = 100; - this.tolerance = 0.001; - this.accept_disconnected_graph = true; - } - - /** - * Initializes the internal state for this instance. - */ - protected void initialize() - { - this.total_iterations = 0; - this.max_delta = Double.MIN_VALUE; - this.current_values = new HashMap(); - this.output = new HashMap(); - } - - /** - * Steps through this scoring algorithm until a termination condition is reached. - */ - public void evaluate() - { - do - step(); - while (!done()); - } - - /** - * Returns true if the total number of iterations is greater than or equal to - * max_iterations - * or if the maximum value change observed is less than tolerance. - */ - public boolean done() - { - return total_iterations >= max_iterations || max_delta < tolerance; - } +public abstract class AbstractIterativeScorer + implements IterativeContext, VertexScorer { + /** Maximum number of iterations to use before terminating. Defaults to 100. */ + protected int max_iterations; - /** - * Performs one step of this algorithm; updates the state (value) for each vertex. - */ - public void step() - { - swapOutputForCurrent(); - max_delta = 0; - - for (V v : graph.nodes()) - { - double diff = update(v); - updateMaxDelta(v, diff); - } - total_iterations++; - afterStep(); - } + /** + * Minimum change from one step to the next; if all changes are ≤ tolerance, no further updates + * will occur. Defaults to 0.001. + */ + protected double tolerance; - /** - * - */ - protected void swapOutputForCurrent() - { - Map tmp = output; - output = current_values; - current_values = tmp; - } + /** The graph on which the calculations are to be made. */ + protected Network graph; - /** - * Updates the value for v. - * @param v the vertex whose value is to be updated - * @return the updated value - */ - protected abstract double update(V v); + /** The total number of iterations used so far. */ + protected int total_iterations; - protected void updateMaxDelta(V v, double diff) - { - max_delta = Math.max(max_delta, diff); - } - - protected void afterStep() {} - - @Override - public T getVertexScore(V v) - { - Preconditions.checkArgument(graph.nodes().contains(v), - "Vertex %s not an element of this graph", v.toString()); - - return output.get(v); - } + /** The edge weights used by this algorithm. */ + protected Function, ? extends Number> edge_weights; - @Override - public Map vertexScores() { - return Collections.unmodifiableMap(output); - } - - /** - * Returns the maximum number of iterations that this instance will use. - * @return the maximum number of iterations that evaluate will use - * prior to terminating - */ - public int getMaxIterations() - { - return max_iterations; - } + /** The map in which the output values are stored. */ + private Map output; - /** - * Returns the number of iterations that this instance has used so far. - * @return the number of iterations that this instance has used so far - */ - public int getIterations() - { - return total_iterations; - } - - /** - * Sets the maximum number of times that evaluate will call step. - * @param max_iterations the maximum - */ - public void setMaxIterations(int max_iterations) - { - this.max_iterations = max_iterations; - } + /** The map in which the current values are stored. */ + private Map current_values; - /** - * Gets the size of the largest change (difference between the current and previous values) - * for any vertex that can be tolerated. Once all changes are less than this value, - * evaluate will terminate. - * @return the size of the largest change that evaluate() will permit - */ - public double getTolerance() - { - return tolerance; - } + /** + * A flag representing whether this instance tolerates disconnected graphs. Instances that do not + * accept disconnected graphs may have unexpected behavior on disconnected graphs; they are not + * guaranteed to do an explicit check. Defaults to true. + */ + private boolean accept_disconnected_graph; - /** - * Sets the size of the largest change (difference between the current and previous values) - * for any vertex that can be tolerated. - * @param tolerance the size of the largest change that evaluate() will permit - */ - public void setTolerance(double tolerance) - { - this.tolerance = tolerance; - } - - /** - * Returns the Function that this instance uses to associate edge weights with each edge. - * @return the Function that associates an edge weight with each edge - */ - public Function, ? extends Number> getEdgeWeights() - { - return edge_weights; - } + /** + * Sets the output value for this vertex. + * + * @param v the vertex whose output value is to be set + * @param value the value to set + */ + protected void setOutputValue(V v, T value) { + output.put(v, value); + } - /** - * Sets the Function that this instance uses to associate edge weights with each edge - * @param edge_weights the Function to use to associate an edge weight with each edge - * @see edu.uci.ics.jung.algorithms.scoring.util.UniformDegreeWeight - */ - public void setEdgeWeights(Function edge_weights) - { - this.edge_weights = new DelegateToEdgeTransformer(edge_weights); - } - - /** - * Gets the edge weight for e in the context of its (incident) vertex v. - * @param v the vertex incident to e as a context in which the edge weight is to be calculated - * @param e the edge whose weight is to be returned - * @return the edge weight for e in the context of its (incident) vertex v - */ - protected Number getEdgeWeight(V v, E e) - { - return edge_weights.apply(new VEPair(v,e)); - } - - /** - * Collects the 'potential' from v (its current value) if it has no outgoing edges; this - * can then be redistributed among the other vertices as a means of normalization. - * @param v the vertex whose potential is being collected - */ - protected void collectDisappearingPotential(V v) {} - - /** - * Specifies whether this instance should accept vertices with no outgoing edges. - * @param accept true if this instance should accept vertices with no outgoing edges, false otherwise - */ - public void acceptDisconnectedGraph(boolean accept) - { - this.accept_disconnected_graph = accept; - } - - /** - * Returns true if this instance accepts vertices with no outgoing edges, and false otherwise. - * @return true if this instance accepts vertices with no outgoing edges, otherwise false - */ - public boolean isDisconnectedGraphOK() - { - return this.accept_disconnected_graph; + /** + * Gets the output value for this vertex. + * + * @param v the vertex whose output value is to be retrieved + * @return the output value for this vertex + */ + protected T getOutputValue(V v) { + return output.get(v); + } + + /** + * Gets the current value for this vertex + * + * @param v the vertex whose current value is to be retrieved + * @return the current value for this vertex + */ + protected T getCurrentValue(V v) { + return current_values.get(v); + } + + /** + * Sets the current value for this vertex. + * + * @param v the vertex whose current value is to be set + * @param value the current value to set + */ + protected void setCurrentValue(V v, T value) { + current_values.put(v, value); + } + + /** The largest change seen so far among all vertex scores. */ + protected double max_delta; + + /** + * Creates an instance for the specified graph and edge weights. + * + * @param g the graph for which the instance is to be created + * @param edge_weights the edge weights for this instance + */ + public AbstractIterativeScorer( + Network g, Function edge_weights) { + this.graph = g; + this.max_iterations = 100; + this.tolerance = 0.001; + this.accept_disconnected_graph = true; + setEdgeWeights(edge_weights); + } + + /** + * Creates an instance for the specified graph g. NOTE: This constructor does not set + * the internal edge_weights variable. If this variable is used by the subclass which + * invoked this constructor, it must be initialized by that subclass. + * + * @param g the graph for which the instance is to be created + */ + public AbstractIterativeScorer(Network g) { + this.graph = g; + this.max_iterations = 100; + this.tolerance = 0.001; + this.accept_disconnected_graph = true; + } + + /** Initializes the internal state for this instance. */ + protected void initialize() { + this.total_iterations = 0; + this.max_delta = Double.MIN_VALUE; + this.current_values = new HashMap(); + this.output = new HashMap(); + } + + /** Steps through this scoring algorithm until a termination condition is reached. */ + public void evaluate() { + do step(); + while (!done()); + } + + /** + * Returns true if the total number of iterations is greater than or equal to max_iterations + * or if the maximum value change observed is less than tolerance. + */ + public boolean done() { + return total_iterations >= max_iterations || max_delta < tolerance; + } + + /** Performs one step of this algorithm; updates the state (value) for each vertex. */ + public void step() { + swapOutputForCurrent(); + max_delta = 0; + + for (V v : graph.nodes()) { + double diff = update(v); + updateMaxDelta(v, diff); } + total_iterations++; + afterStep(); + } + + /** */ + protected void swapOutputForCurrent() { + Map tmp = output; + output = current_values; + current_values = tmp; + } + + /** + * Updates the value for v. + * + * @param v the vertex whose value is to be updated + * @return the updated value + */ + protected abstract double update(V v); + + protected void updateMaxDelta(V v, double diff) { + max_delta = Math.max(max_delta, diff); + } + + protected void afterStep() {} + + @Override + public T getVertexScore(V v) { + Preconditions.checkArgument( + graph.nodes().contains(v), "Vertex %s not an element of this graph", v.toString()); + + return output.get(v); + } + + @Override + public Map vertexScores() { + return Collections.unmodifiableMap(output); + } + + /** + * Returns the maximum number of iterations that this instance will use. + * + * @return the maximum number of iterations that evaluate will use prior to + * terminating + */ + public int getMaxIterations() { + return max_iterations; + } + + /** + * Returns the number of iterations that this instance has used so far. + * + * @return the number of iterations that this instance has used so far + */ + public int getIterations() { + return total_iterations; + } + + /** + * Sets the maximum number of times that evaluate will call step. + * + * @param max_iterations the maximum + */ + public void setMaxIterations(int max_iterations) { + this.max_iterations = max_iterations; + } + + /** + * Gets the size of the largest change (difference between the current and previous values) for + * any vertex that can be tolerated. Once all changes are less than this value, evaluate + * will terminate. + * + * @return the size of the largest change that evaluate() will permit + */ + public double getTolerance() { + return tolerance; + } + + /** + * Sets the size of the largest change (difference between the current and previous values) for + * any vertex that can be tolerated. + * + * @param tolerance the size of the largest change that evaluate() will permit + */ + public void setTolerance(double tolerance) { + this.tolerance = tolerance; + } + + /** + * Returns the Function that this instance uses to associate edge weights with each edge. + * + * @return the Function that associates an edge weight with each edge + */ + public Function, ? extends Number> getEdgeWeights() { + return edge_weights; + } + + /** + * Sets the Function that this instance uses to associate edge weights with each edge + * + * @param edge_weights the Function to use to associate an edge weight with each edge + * @see edu.uci.ics.jung.algorithms.scoring.util.UniformDegreeWeight + */ + public void setEdgeWeights(Function edge_weights) { + this.edge_weights = new DelegateToEdgeTransformer(edge_weights); + } + + /** + * Gets the edge weight for e in the context of its (incident) vertex v. + * + * @param v the vertex incident to e as a context in which the edge weight is to be calculated + * @param e the edge whose weight is to be returned + * @return the edge weight for e in the context of its (incident) vertex v + * + */ + protected Number getEdgeWeight(V v, E e) { + return edge_weights.apply(new VEPair(v, e)); + } + + /** + * Collects the 'potential' from v (its current value) if it has no outgoing edges; this can then + * be redistributed among the other vertices as a means of normalization. + * + * @param v the vertex whose potential is being collected + */ + protected void collectDisappearingPotential(V v) {} + + /** + * Specifies whether this instance should accept vertices with no outgoing edges. + * + * @param accept true if this instance should accept vertices with no outgoing edges, false + * otherwise + */ + public void acceptDisconnectedGraph(boolean accept) { + this.accept_disconnected_graph = accept; + } + + /** + * Returns true if this instance accepts vertices with no outgoing edges, and false otherwise. + * + * @return true if this instance accepts vertices with no outgoing edges, otherwise false + */ + public boolean isDisconnectedGraphOK() { + return this.accept_disconnected_graph; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/AbstractIterativeScorerWithPriors.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/AbstractIterativeScorerWithPriors.java index 9095b90e..6bd8024f 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/AbstractIterativeScorerWithPriors.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/AbstractIterativeScorerWithPriors.java @@ -1,7 +1,7 @@ /* * Created on Jul 14, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -15,102 +15,94 @@ import com.google.common.graph.Network; /** - * An abstract class for iterative random-walk-based vertex scoring algorithms - * that have a - * fixed probability, for each vertex, of 'jumping' to that vertex at each - * step in the algorithm (rather than following a link out of that vertex). + * An abstract class for iterative random-walk-based vertex scoring algorithms that have a fixed + * probability, for each vertex, of 'jumping' to that vertex at each step in the algorithm (rather + * than following a link out of that vertex). * * @param the vertex type * @param the edge type * @param the score type */ -public abstract class AbstractIterativeScorerWithPriors extends - AbstractIterativeScorer implements VertexScorer -{ - /** - * The prior probability of each vertex being visited on a given - * 'jump' (non-link-following) step. - */ - protected Function vertex_priors; +public abstract class AbstractIterativeScorerWithPriors + extends AbstractIterativeScorer implements VertexScorer { + /** + * The prior probability of each vertex being visited on a given 'jump' (non-link-following) step. + */ + protected Function vertex_priors; - /** - * The probability of making a 'jump' at each step. - */ - protected double alpha; + /** The probability of making a 'jump' at each step. */ + protected double alpha; - /** - * Creates an instance for the specified graph, edge weights, vertex - * priors, and jump probability. - * @param g the graph whose vertices are to be assigned scores - * @param edge_weights the edge weights to use in the score assignment - * @param vertex_priors the prior probabilities of each vertex being 'jumped' to - * @param alpha the probability of making a 'jump' at each step - */ - public AbstractIterativeScorerWithPriors(Network g, - Function edge_weights, - Function vertex_priors, double alpha) - { - super(g, edge_weights); - this.vertex_priors = vertex_priors; - this.alpha = alpha; - initialize(); - } + /** + * Creates an instance for the specified graph, edge weights, vertex priors, and jump probability. + * + * @param g the graph whose vertices are to be assigned scores + * @param edge_weights the edge weights to use in the score assignment + * @param vertex_priors the prior probabilities of each vertex being 'jumped' to + * @param alpha the probability of making a 'jump' at each step + */ + public AbstractIterativeScorerWithPriors( + Network g, + Function edge_weights, + Function vertex_priors, + double alpha) { + super(g, edge_weights); + this.vertex_priors = vertex_priors; + this.alpha = alpha; + initialize(); + } - /** - * Creates an instance for the specified graph, vertex priors, and jump - * probability, with edge weights specified by the subclass. - * @param g the graph whose vertices are to be assigned scores - * @param vertex_priors the prior probabilities of each vertex being 'jumped' to - * @param alpha the probability of making a 'jump' at each step - */ - public AbstractIterativeScorerWithPriors(Network g, - Function vertex_priors, double alpha) - { - super(g); - this.vertex_priors = vertex_priors; - this.alpha = alpha; - initialize(); - } + /** + * Creates an instance for the specified graph, vertex priors, and jump probability, with edge + * weights specified by the subclass. + * + * @param g the graph whose vertices are to be assigned scores + * @param vertex_priors the prior probabilities of each vertex being 'jumped' to + * @param alpha the probability of making a 'jump' at each step + */ + public AbstractIterativeScorerWithPriors( + Network g, Function vertex_priors, double alpha) { + super(g); + this.vertex_priors = vertex_priors; + this.alpha = alpha; + initialize(); + } - /** - * Initializes the state of this instance. - */ - @Override - public void initialize() - { - super.initialize(); - // initialize output values to priors - // (output and current are swapped before each step(), so current will - // have priors when update()s start happening) - for (V v : graph.nodes()) - setOutputValue(v, getVertexPrior(v)); - } - - /** - * Returns the prior probability for v. - * @param v the vertex whose prior probability is being queried - * @return the prior probability for v - */ - protected S getVertexPrior(V v) - { - return vertex_priors.apply(v); - } + /** Initializes the state of this instance. */ + @Override + public void initialize() { + super.initialize(); + // initialize output values to priors + // (output and current are swapped before each step(), so current will + // have priors when update()s start happening) + for (V v : graph.nodes()) setOutputValue(v, getVertexPrior(v)); + } - /** - * Returns a Function which maps each vertex to its prior probability. - * @return a Function which maps each vertex to its prior probability - */ - public Function getVertexPriors() - { - return vertex_priors; - } + /** + * Returns the prior probability for v. + * + * @param v the vertex whose prior probability is being queried + * @return the prior probability for v + */ + protected S getVertexPrior(V v) { + return vertex_priors.apply(v); + } - /** - * Returns the probability of making a 'jump' (non-link-following step). - * @return the probability of making a 'jump' (non-link-following step) - */ - public double getAlpha() - { - return alpha; - } + /** + * Returns a Function which maps each vertex to its prior probability. + * + * @return a Function which maps each vertex to its prior probability + */ + public Function getVertexPriors() { + return vertex_priors; + } + + /** + * Returns the probability of making a 'jump' (non-link-following step). + * + * @return the probability of making a 'jump' (non-link-following step) + */ + public double getAlpha() { + return alpha; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/BarycenterScorer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/BarycenterScorer.java index a0912fce..421813a6 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/BarycenterScorer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/BarycenterScorer.java @@ -1,7 +1,7 @@ /* * Created on Jul 12, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -14,43 +14,38 @@ import com.google.common.base.Function; import com.google.common.graph.Graph; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.shortestpath.Distance; -/** - * Assigns scores to each vertex according to the sum of its distances to all other vertices. - */ -public class BarycenterScorer extends DistanceCentralityScorer -{ - /** - * Creates an instance with the specified graph and distance metric. - * @param graph the input graph - * @param distance the distance metric to use - */ - public BarycenterScorer(Network graph, Distance distance) - { - super(graph, distance, false); - } - - /** - * Creates an instance with the specified graph and edge weights. - * Will generate a Distance metric internally based on the edge weights. - * @param graph the input graph - * @param edge_weights the edge weights to use to calculate vertex/vertex distances - */ - public BarycenterScorer(Network graph, Function edge_weights) - { - super(graph, edge_weights, false); - } +/** Assigns scores to each vertex according to the sum of its distances to all other vertices. */ +public class BarycenterScorer extends DistanceCentralityScorer { + /** + * Creates an instance with the specified graph and distance metric. + * + * @param graph the input graph + * @param distance the distance metric to use + */ + public BarycenterScorer(Network graph, Distance distance) { + super(graph, distance, false); + } + + /** + * Creates an instance with the specified graph and edge weights. Will generate a Distance + * metric internally based on the edge weights. + * + * @param graph the input graph + * @param edge_weights the edge weights to use to calculate vertex/vertex distances + */ + public BarycenterScorer(Network graph, Function edge_weights) { + super(graph, edge_weights, false); + } - /** - * Creates an instance with the specified graph. - * Will generate a Distance metric internally assuming that the - * graph is unweighted. - * @param graph the input graph - */ - public BarycenterScorer(Graph graph) - { - super(graph, false); - } + /** + * Creates an instance with the specified graph. Will generate a Distance metric + * internally assuming that the graph is unweighted. + * + * @param graph the input graph + */ + public BarycenterScorer(Graph graph) { + super(graph, false); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/ClosenessCentrality.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/ClosenessCentrality.java index 7cdf19f0..27402967 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/ClosenessCentrality.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/ClosenessCentrality.java @@ -1,7 +1,7 @@ /* * Created on Jul 12, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -14,43 +14,41 @@ import com.google.common.base.Function; import com.google.common.graph.Graph; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.shortestpath.Distance; /** * Assigns scores to each vertex based on the mean distance to each other vertex. - * + * * @author Joshua O'Madadhain */ -public class ClosenessCentrality extends DistanceCentralityScorer -{ +public class ClosenessCentrality extends DistanceCentralityScorer { - /** - * Creates an instance using the specified vertex/vertex distance metric. - * @param graph the input - * @param distance the vertex/vertex distance metric. - */ - public ClosenessCentrality(Network graph, Distance distance) - { - super(graph, distance, true); - } + /** + * Creates an instance using the specified vertex/vertex distance metric. + * + * @param graph the input + * @param distance the vertex/vertex distance metric. + */ + public ClosenessCentrality(Network graph, Distance distance) { + super(graph, distance, true); + } - /** - * Creates an instance which measures distance using the specified edge weights. - * @param graph the input graph - * @param edge_weights the edge weights to be used to determine vertex/vertex distances - */ - public ClosenessCentrality(Network graph, Function edge_weights) - { - super(graph, edge_weights, true); - } + /** + * Creates an instance which measures distance using the specified edge weights. + * + * @param graph the input graph + * @param edge_weights the edge weights to be used to determine vertex/vertex distances + */ + public ClosenessCentrality(Network graph, Function edge_weights) { + super(graph, edge_weights, true); + } - /** - * Creates an instance which measures distance on the graph without edge weights. - * @param graph the graph whose vertices' centrality scores will be calculated - */ - public ClosenessCentrality(Graph graph) - { - super(graph, true); - } + /** + * Creates an instance which measures distance on the graph without edge weights. + * + * @param graph the graph whose vertices' centrality scores will be calculated + */ + public ClosenessCentrality(Graph graph) { + super(graph, true); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/DegreeScorer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/DegreeScorer.java index 42ff12f7..fde5bedc 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/DegreeScorer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/DegreeScorer.java @@ -1,7 +1,7 @@ /* * Created on Jul 6, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -11,45 +11,40 @@ */ package edu.uci.ics.jung.algorithms.scoring; -import java.util.Map; - import com.google.common.collect.Maps; import com.google.common.graph.Network; - +import java.util.Map; /** * Assigns a score to each vertex equal to its degree. * * @param the vertex type */ -public class DegreeScorer implements VertexScorer -{ - // TODO: Graph and Network degree are different, so we either need to - // provide separate constructors or separate classes - /** - * The graph for which scores are to be generated. - */ - protected Network graph; - - /** - * Creates an instance for the specified graph. - * @param graph the input graph - */ - public DegreeScorer(Network graph) - { - this.graph = graph; - } - - /** - * Returns the degree of the vertex. - * @return the degree of the vertex - */ - public Integer getVertexScore(N node) - { - return graph.degree(node); - } - - public Map vertexScores() { - return Maps.asMap(graph.nodes(), node -> graph.degree(node)); - } +public class DegreeScorer implements VertexScorer { + // TODO: Graph and Network degree are different, so we either need to + // provide separate constructors or separate classes + /** The graph for which scores are to be generated. */ + protected Network graph; + + /** + * Creates an instance for the specified graph. + * + * @param graph the input graph + */ + public DegreeScorer(Network graph) { + this.graph = graph; + } + + /** + * Returns the degree of the vertex. + * + * @return the degree of the vertex + */ + public Integer getVertexScore(N node) { + return graph.degree(node); + } + + public Map vertexScores() { + return Maps.asMap(graph.nodes(), node -> graph.degree(node)); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/DistanceCentralityScorer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/DistanceCentralityScorer.java index e2507c2c..795851c9 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/DistanceCentralityScorer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/DistanceCentralityScorer.java @@ -1,7 +1,7 @@ /* * Created on Jul 10, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -11,274 +11,247 @@ */ package edu.uci.ics.jung.algorithms.scoring; -import java.util.HashMap; -import java.util.Map; - import com.google.common.base.Function; import com.google.common.collect.Maps; import com.google.common.graph.Graph; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.shortestpath.DijkstraDistance; import edu.uci.ics.jung.algorithms.shortestpath.Distance; import edu.uci.ics.jung.algorithms.shortestpath.UnweightedShortestPath; +import java.util.HashMap; +import java.util.Map; /** - * Assigns scores to vertices based on their distances to each other vertex - * in the graph. - * - * This class optionally normalizes its results based on the value of its - * 'averaging' constructor parameter. If it is true, - * then the value returned for vertex v is 1 / (_average_ distance from v to all other vertices); - * this is sometimes called closeness centrality. - * If it is false, then the value returned is 1 / (_total_ distance from - * v to all other vertices); this is sometimes referred to as barycenter centrality. - * (If the average/total distance is 0, the value returned is {@code Double.POSITIVE_INFINITY}.) - * + * Assigns scores to vertices based on their distances to each other vertex in the graph. + * + *

This class optionally normalizes its results based on the value of its 'averaging' constructor + * parameter. If it is true, then the value returned for vertex v is 1 / (_average_ + * distance from v to all other vertices); this is sometimes called closeness centrality. If + * it is false, then the value returned is 1 / (_total_ distance from v to all other + * vertices); this is sometimes referred to as barycenter centrality. (If the average/total + * distance is 0, the value returned is {@code Double.POSITIVE_INFINITY}.) + * * @see BarycenterScorer * @see ClosenessCentrality */ -public class DistanceCentralityScorer implements VertexScorer -{ - /** - * The graph on which the vertex scores are to be calculated. - */ - protected Graph graph; - - /** - * The metric to use for specifying the distance between pairs of vertices. - */ - protected Distance distance; - - /** - * The cache for the output results. Null encodes "not yet calculated", - * < 0 encodes "no such distance exists". - */ - protected Map output; - - /** - * Specifies whether the values returned are the sum of the v-distances - * or the mean v-distance. - */ - protected boolean averaging; - - /** - * Specifies whether, for a vertex v with missing (null) distances, - * v's score should ignore the missing values or be set to 'null'. - * Defaults to 'true'. - */ - protected boolean ignore_missing; +public class DistanceCentralityScorer implements VertexScorer { + /** The graph on which the vertex scores are to be calculated. */ + protected Graph graph; - /** - * Specifies whether the values returned should ignore self-distances - * (distances from v to itself). - * Defaults to 'true'. - */ - protected boolean ignore_self_distances; + /** The metric to use for specifying the distance between pairs of vertices. */ + protected Distance distance; - // TODO: consider using a builder pattern rather than a bunch of parameters - - /** - * Creates an instance with the specified graph, distance metric, and - * averaging behavior. - * - * @param graph The graph on which the vertex scores are to be calculated. - * @param distance The metric to use for specifying the distance between - * pairs of vertices. - * @param averaging Specifies whether the values returned is the sum of all - * v-distances or the mean v-distance. - * @param ignore_missing Specifies whether scores for missing distances - * are to ignore missing distances or be set to null. - * @param ignore_self_distances Specifies whether distances from a vertex - * to itself should be included in its score. - */ - public DistanceCentralityScorer(Network graph, Distance distance, - boolean averaging, boolean ignore_missing, - boolean ignore_self_distances) - { - this.graph = graph.asGraph(); - this.distance = distance; - this.averaging = averaging; - this.ignore_missing = ignore_missing; - this.ignore_self_distances = ignore_self_distances; - this.output = new HashMap(); - } + /** + * The cache for the output results. Null encodes "not yet calculated", < 0 encodes "no such + * distance exists". + */ + protected Map output; - /** - * Creates an instance with the specified graph, distance metric, and - * averaging behavior. - * - * @param graph The graph on which the vertex scores are to be calculated. - * @param distance The metric to use for specifying the distance between - * pairs of vertices. - * @param averaging Specifies whether the values returned is the sum of all - * v-distances or the mean v-distance. - * @param ignore_missing Specifies whether scores for missing distances - * are to ignore missing distances or be set to null. - * @param ignore_self_distances Specifies whether distances from a vertex - * to itself should be included in its score. - */ - public DistanceCentralityScorer(Graph graph, Distance distance, - boolean averaging, boolean ignore_missing, - boolean ignore_self_distances) - { - this.graph = graph; - this.distance = distance; - this.averaging = averaging; - this.ignore_missing = ignore_missing; - this.ignore_self_distances = ignore_self_distances; - this.output = new HashMap(); - } + /** + * Specifies whether the values returned are the sum of the v-distances or the mean v-distance. + */ + protected boolean averaging; - /** - * Equivalent to this(graph, distance, averaging, true, true). - * - * @param graph The graph on which the vertex scores are to be calculated. - * @param distance The metric to use for specifying the distance between - * pairs of vertices. - * @param averaging Specifies whether the values returned is the sum of all - * v-distances or the mean v-distance. - */ - public DistanceCentralityScorer(Network graph, Distance distance, - boolean averaging) - { - this(graph, distance, averaging, true, true); - } - - /** - * Creates an instance with the specified graph and averaging behavior - * whose vertex distances are calculated based on the specified edge - * weights. - * - * @param graph The graph on which the vertex scores are to be - * calculated. - * @param edge_weights The edge weights to use for specifying the distance - * between pairs of vertices. - * @param averaging Specifies whether the values returned is the sum of - * all v-distances or the mean v-distance. - * @param ignore_missing Specifies whether scores for missing distances - * are to ignore missing distances or be set to null. - * @param ignore_self_distances Specifies whether distances from a vertex - * to itself should be included in its score. - */ - public DistanceCentralityScorer(Network graph, - Function edge_weights, boolean averaging, - boolean ignore_missing, boolean ignore_self_distances) - { - this(graph, new DijkstraDistance(graph, edge_weights), averaging, - ignore_missing, ignore_self_distances); - } - - /** - * Equivalent to this(graph, edge_weights, averaging, true, true). - * @param graph The graph on which the vertex scores are to be - * calculated. - * @param edge_weights The edge weights to use for specifying the distance - * between pairs of vertices. - * @param averaging Specifies whether the values returned is the sum of - * all v-distances or the mean v-distance. - */ - public DistanceCentralityScorer(Network graph, - Function edge_weights, boolean averaging) - { - this(graph, new DijkstraDistance(graph, edge_weights), averaging, - true, true); + /** + * Specifies whether, for a vertex v with missing (null) distances, v's + * score should ignore the missing values or be set to 'null'. Defaults to 'true'. + */ + protected boolean ignore_missing; + + /** + * Specifies whether the values returned should ignore self-distances (distances from v + * to itself). Defaults to 'true'. + */ + protected boolean ignore_self_distances; + + // TODO: consider using a builder pattern rather than a bunch of parameters + + /** + * Creates an instance with the specified graph, distance metric, and averaging behavior. + * + * @param graph The graph on which the vertex scores are to be calculated. + * @param distance The metric to use for specifying the distance between pairs of vertices. + * @param averaging Specifies whether the values returned is the sum of all v-distances or the + * mean v-distance. + * @param ignore_missing Specifies whether scores for missing distances are to ignore missing + * distances or be set to null. + * @param ignore_self_distances Specifies whether distances from a vertex to itself should be + * included in its score. + */ + public DistanceCentralityScorer( + Network graph, + Distance distance, + boolean averaging, + boolean ignore_missing, + boolean ignore_self_distances) { + this.graph = graph.asGraph(); + this.distance = distance; + this.averaging = averaging; + this.ignore_missing = ignore_missing; + this.ignore_self_distances = ignore_self_distances; + this.output = new HashMap(); + } + + /** + * Creates an instance with the specified graph, distance metric, and averaging behavior. + * + * @param graph The graph on which the vertex scores are to be calculated. + * @param distance The metric to use for specifying the distance between pairs of vertices. + * @param averaging Specifies whether the values returned is the sum of all v-distances or the + * mean v-distance. + * @param ignore_missing Specifies whether scores for missing distances are to ignore missing + * distances or be set to null. + * @param ignore_self_distances Specifies whether distances from a vertex to itself should be + * included in its score. + */ + public DistanceCentralityScorer( + Graph graph, + Distance distance, + boolean averaging, + boolean ignore_missing, + boolean ignore_self_distances) { + this.graph = graph; + this.distance = distance; + this.averaging = averaging; + this.ignore_missing = ignore_missing; + this.ignore_self_distances = ignore_self_distances; + this.output = new HashMap(); + } + + /** + * Equivalent to this(graph, distance, averaging, true, true). + * + * @param graph The graph on which the vertex scores are to be calculated. + * @param distance The metric to use for specifying the distance between pairs of vertices. + * @param averaging Specifies whether the values returned is the sum of all v-distances or the + * mean v-distance. + */ + public DistanceCentralityScorer(Network graph, Distance distance, boolean averaging) { + this(graph, distance, averaging, true, true); + } + + /** + * Creates an instance with the specified graph and averaging behavior whose vertex distances are + * calculated based on the specified edge weights. + * + * @param graph The graph on which the vertex scores are to be calculated. + * @param edge_weights The edge weights to use for specifying the distance between pairs of + * vertices. + * @param averaging Specifies whether the values returned is the sum of all v-distances or the + * mean v-distance. + * @param ignore_missing Specifies whether scores for missing distances are to ignore missing + * distances or be set to null. + * @param ignore_self_distances Specifies whether distances from a vertex to itself should be + * included in its score. + */ + public DistanceCentralityScorer( + Network graph, + Function edge_weights, + boolean averaging, + boolean ignore_missing, + boolean ignore_self_distances) { + this( + graph, + new DijkstraDistance(graph, edge_weights), + averaging, + ignore_missing, + ignore_self_distances); + } + + /** + * Equivalent to this(graph, edge_weights, averaging, true, true). + * + * @param graph The graph on which the vertex scores are to be calculated. + * @param edge_weights The edge weights to use for specifying the distance between pairs of + * vertices. + * @param averaging Specifies whether the values returned is the sum of all v-distances or the + * mean v-distance. + */ + public DistanceCentralityScorer( + Network graph, Function edge_weights, boolean averaging) { + this(graph, new DijkstraDistance(graph, edge_weights), averaging, true, true); + } + + /** + * Creates an instance with the specified graph and averaging behavior whose vertex distances are + * calculated on the unweighted graph. + * + * @param graph The graph on which the vertex scores are to be calculated. + * @param averaging Specifies whether the values returned is the sum of all v-distances or the + * mean v-distance. + * @param ignore_missing Specifies whether scores for missing distances are to ignore missing + * distances or be set to null. + * @param ignore_self_distances Specifies whether distances from a vertex to itself should be + * included in its score. + */ + public DistanceCentralityScorer( + Graph graph, boolean averaging, boolean ignore_missing, boolean ignore_self_distances) { + this( + graph, + new UnweightedShortestPath(graph), + averaging, + ignore_missing, + ignore_self_distances); + } + + /** + * Equivalent to this(graph, averaging, true, true). + * + * @param graph The graph on which the vertex scores are to be calculated. + * @param averaging Specifies whether the values returned is the sum of all v-distances or the + * mean v-distance. + */ + public DistanceCentralityScorer(Graph graph, boolean averaging) { + this(graph, new UnweightedShortestPath(graph), averaging, true, true); + } + + /** + * Calculates the score for the specified vertex. Returns {@code null} if there are missing + * distances and such are not ignored by this instance. + */ + public Double getVertexScore(V v) { + Double value = output.get(v); + if (value != null) { + if (value < 0) return null; + return value; } - - /** - * Creates an instance with the specified graph and averaging behavior - * whose vertex distances are calculated on the unweighted graph. - * - * @param graph The graph on which the vertex scores are to be - * calculated. - * @param averaging Specifies whether the values returned is the sum of - * all v-distances or the mean v-distance. - * @param ignore_missing Specifies whether scores for missing distances - * are to ignore missing distances or be set to null. - * @param ignore_self_distances Specifies whether distances from a vertex - * to itself should be included in its score. - */ - public DistanceCentralityScorer(Graph graph, boolean averaging, - boolean ignore_missing, boolean ignore_self_distances) - { - this(graph, new UnweightedShortestPath(graph), averaging, - ignore_missing, ignore_self_distances); + + Map v_distances = new HashMap(distance.getDistanceMap(v)); + if (ignore_self_distances) v_distances.remove(v); + + // if we don't ignore missing distances and there aren't enough + // distances, output null (shortcut) + if (!ignore_missing) { + int num_dests = graph.nodes().size() - (ignore_self_distances ? 1 : 0); + if (v_distances.size() != num_dests) { + output.put(v, -1.0); + return null; + } } - /** - * Equivalent to this(graph, averaging, true, true). - * @param graph The graph on which the vertex scores are to be - * calculated. - * @param averaging Specifies whether the values returned is the sum of - * all v-distances or the mean v-distance. - */ - public DistanceCentralityScorer(Graph graph, boolean averaging) - { - this(graph, new UnweightedShortestPath(graph), averaging, true, true); + Double sum = 0.0; + for (V w : graph.nodes()) { + if (w.equals(v) && ignore_self_distances) continue; + Number w_distance = v_distances.get(w); + if (w_distance == null) + if (ignore_missing) continue; + else { + output.put(v, -1.0); + return null; + } + else sum += w_distance.doubleValue(); } + value = sum; + if (averaging) value /= v_distances.size(); + + double score = value == 0 ? Double.POSITIVE_INFINITY : 1.0 / value; + output.put(v, score); - /** - * Calculates the score for the specified vertex. Returns {@code null} if - * there are missing distances and such are not ignored by this instance. - */ - public Double getVertexScore(V v) - { - Double value = output.get(v); - if (value != null) - { - if (value < 0) - return null; - return value; - } - - Map v_distances = new HashMap(distance.getDistanceMap(v)); - if (ignore_self_distances) - v_distances.remove(v); - - // if we don't ignore missing distances and there aren't enough - // distances, output null (shortcut) - if (!ignore_missing) - { - int num_dests = graph.nodes().size() - - (ignore_self_distances ? 1 : 0); - if (v_distances.size() != num_dests) - { - output.put(v, -1.0); - return null; - } - } - - Double sum = 0.0; - for (V w : graph.nodes()) - { - if (w.equals(v) && ignore_self_distances) - continue; - Number w_distance = v_distances.get(w); - if (w_distance == null) - if (ignore_missing) - continue; - else - { - output.put(v, -1.0); - return null; - } - else - sum += w_distance.doubleValue(); - } - value = sum; - if (averaging) - value /= v_distances.size(); - - double score = value == 0 ? - Double.POSITIVE_INFINITY : - 1.0 / value; - output.put(v, score); - - return score; - } + return score; + } - @Override - public Map vertexScores() { - return Maps.asMap(graph.nodes(), node -> getVertexScore(node)); - } + @Override + public Map vertexScores() { + return Maps.asMap(graph.nodes(), node -> getVertexScore(node)); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/EdgeScorer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/EdgeScorer.java index 4654080f..beef9c6f 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/EdgeScorer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/EdgeScorer.java @@ -1,7 +1,7 @@ /* * Created on Jul 6, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -19,13 +19,12 @@ * @param the edge type * @param the score type */ -public interface EdgeScorer -{ - /** - * @param e the edge whose score is requested - * @return the algorithm's score for this edge - */ - public S getEdgeScore(E e); - - public Map edgeScores(); +public interface EdgeScorer { + /** + * @param e the edge whose score is requested + * @return the algorithm's score for this edge + */ + public S getEdgeScore(E e); + + public Map edgeScores(); } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/EigenvectorCentrality.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/EigenvectorCentrality.java index 84d6d558..41816a87 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/EigenvectorCentrality.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/EigenvectorCentrality.java @@ -1,7 +1,7 @@ /* * Created on Jul 12, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -15,37 +15,32 @@ import com.google.common.graph.Network; /** - * Calculates eigenvector centrality for each vertex in the graph. - * The 'eigenvector centrality' for a vertex is defined as the fraction of - * time that a random walk(er) will spend at that vertex over an infinite - * time horizon. - * Assumes that the graph is strongly connected. + * Calculates eigenvector centrality for each vertex in the graph. The 'eigenvector centrality' for + * a vertex is defined as the fraction of time that a random walk(er) will spend at that vertex over + * an infinite time horizon. Assumes that the graph is strongly connected. */ -public class EigenvectorCentrality extends PageRank -{ - /** - * Creates an instance with the specified graph and edge weights. - * The outgoing edge weights for each edge must sum to 1. - * (See UniformDegreeWeight for one way to handle this for - * undirected graphs.) - * @param graph the graph for which the centrality is to be calculated - * @param edge_weights the edge weights - */ - public EigenvectorCentrality(Network graph, - Function edge_weights) - { - super(graph, edge_weights, 0); - acceptDisconnectedGraph(false); - } +public class EigenvectorCentrality extends PageRank { + /** + * Creates an instance with the specified graph and edge weights. The outgoing edge weights for + * each edge must sum to 1. (See UniformDegreeWeight for one way to handle this for + * undirected graphs.) + * + * @param graph the graph for which the centrality is to be calculated + * @param edge_weights the edge weights + */ + public EigenvectorCentrality(Network graph, Function edge_weights) { + super(graph, edge_weights, 0); + acceptDisconnectedGraph(false); + } - /** - * Creates an instance with the specified graph and default edge weights. - * (Default edge weights: UniformDegreeWeight.) - * @param graph the graph for which the centrality is to be calculated. - */ - public EigenvectorCentrality(Network graph) - { - super(graph, 0); - acceptDisconnectedGraph(false); - } + /** + * Creates an instance with the specified graph and default edge weights. (Default edge weights: + * UniformDegreeWeight.) + * + * @param graph the graph for which the centrality is to be calculated. + */ + public EigenvectorCentrality(Network graph) { + super(graph, 0); + acceptDisconnectedGraph(false); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/HITS.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/HITS.java index 63204880..6e82570a 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/HITS.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/HITS.java @@ -1,7 +1,7 @@ /* * Created on Jul 15, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -13,16 +13,15 @@ import com.google.common.base.Function; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.scoring.util.ScoringUtils; /** - * Assigns hub and authority scores to each vertex depending on the topology of - * the network. The essential idea is that a vertex is a hub to the extent - * that it links to authoritative vertices, and is an authority to the extent - * that it links to 'hub' vertices. - * + * Assigns hub and authority scores to each vertex depending on the topology of the network. The + * essential idea is that a vertex is a hub to the extent that it links to authoritative vertices, + * and is an authority to the extent that it links to 'hub' vertices. + * *

The classic HITS algorithm essentially proceeds as follows: + * *

  * assign equal initial hub and authority values to each vertex
  * repeat
@@ -32,116 +31,101 @@
  *   normalize hub and authority scores so that the sum of the squares of each = 1
  * until scores converge
  * 
- * - * HITS is somewhat different from random walk/eigenvector-based algorithms - * such as PageRank in that: + * + * HITS is somewhat different from random walk/eigenvector-based algorithms such as PageRank in + * that: + * *
    - *
  • there are two mutually recursive scores being calculated, rather than - * a single value - *
  • the edge weights are effectively all 1, i.e., they can't be interpreted - * as transition probabilities. This means that the more inlinks and outlinks - * that a vertex has, the better, since adding an inlink (or outlink) does - * not dilute the influence of the other inlinks (or outlinks) as in - * random walk-based algorithms. - *
  • the scores cannot be interpreted as posterior probabilities (due to the different - * normalization) + *
  • there are two mutually recursive scores being calculated, rather than a single value + *
  • the edge weights are effectively all 1, i.e., they can't be interpreted as transition + * probabilities. This means that the more inlinks and outlinks that a vertex has, the better, + * since adding an inlink (or outlink) does not dilute the influence of the other inlinks (or + * outlinks) as in random walk-based algorithms. + *
  • the scores cannot be interpreted as posterior probabilities (due to the different + * normalization) *
- * - * This implementation has the classic behavior by default. However, it has - * been generalized somewhat so that it can act in a more "PageRank-like" fashion: + * + * This implementation has the classic behavior by default. However, it has been generalized + * somewhat so that it can act in a more "PageRank-like" fashion: + * *
    - *
  • this implementation has an optional 'random jump probability' parameter analogous - * to the 'alpha' parameter used by PageRank. Varying this value between 0 and 1 - * allows the user to vary between the classic HITS behavior and one in which the - * scores are smoothed to a uniform distribution. - * The default value for this parameter is 0 (no random jumps possible). - *
  • the edge weights can be set to anything the user likes, and in - * particular they can be set up (e.g. using UniformDegreeWeight) - * so that the weights of the relevant edges incident to a vertex sum to 1. - *
  • The vertex score normalization has been factored into its own method - * so that it can be overridden by a subclass. Thus, for example, - * since the vertices' values are set to sum to 1 initially, if the weights of the - * relevant edges incident to a vertex sum to 1, then the vertices' values - * will continue to sum to 1 if the "sum-of-squares" normalization code - * is overridden to a no-op. (Other normalization methods may also be employed.) + *
  • this implementation has an optional 'random jump probability' parameter analogous to the + * 'alpha' parameter used by PageRank. Varying this value between 0 and 1 allows the user to + * vary between the classic HITS behavior and one in which the scores are smoothed to a + * uniform distribution. The default value for this parameter is 0 (no random jumps possible). + *
  • the edge weights can be set to anything the user likes, and in particular they can be set + * up (e.g. using UniformDegreeWeight) so that the weights of the relevant edges + * incident to a vertex sum to 1. + *
  • The vertex score normalization has been factored into its own method so that it can be + * overridden by a subclass. Thus, for example, since the vertices' values are set to sum to 1 + * initially, if the weights of the relevant edges incident to a vertex sum to 1, then the + * vertices' values will continue to sum to 1 if the "sum-of-squares" normalization code is + * overridden to a no-op. (Other normalization methods may also be employed.) *
- * + * * @param the vertex type * @param the edge type - * * @see "'Authoritative sources in a hyperlinked environment' by Jon Kleinberg, 1997" */ -public class HITS extends HITSWithPriors -{ +public class HITS extends HITSWithPriors { - /** - * Creates an instance for the specified graph, edge weights, and alpha - * (random jump probability) parameter. - * @param g the input graph - * @param edge_weights the weights to use for each edge - * @param alpha the probability of a hub giving some authority to all vertices, - * and of an authority increasing the score of all hubs (not just those connected - * via links) - */ - public HITS(Network g, Function edge_weights, double alpha) - { - super(g, edge_weights, ScoringUtils.getHITSUniformRootPrior(g.nodes()), alpha); - } + /** + * Creates an instance for the specified graph, edge weights, and alpha (random jump probability) + * parameter. + * + * @param g the input graph + * @param edge_weights the weights to use for each edge + * @param alpha the probability of a hub giving some authority to all vertices, and of an + * authority increasing the score of all hubs (not just those connected via links) + */ + public HITS(Network g, Function edge_weights, double alpha) { + super(g, edge_weights, ScoringUtils.getHITSUniformRootPrior(g.nodes()), alpha); + } - /** - * Creates an instance for the specified graph and alpha (random jump probability) - * parameter. The edge weights are all set to 1. - * @param g the input graph - * @param alpha the probability of a hub giving some authority to all vertices, - * and of an authority increasing the score of all hubs (not just those connected - * via links) - */ - public HITS(Network g, double alpha) - { - super(g, ScoringUtils.getHITSUniformRootPrior(g.nodes()), alpha); - } + /** + * Creates an instance for the specified graph and alpha (random jump probability) parameter. The + * edge weights are all set to 1. + * + * @param g the input graph + * @param alpha the probability of a hub giving some authority to all vertices, and of an + * authority increasing the score of all hubs (not just those connected via links) + */ + public HITS(Network g, double alpha) { + super(g, ScoringUtils.getHITSUniformRootPrior(g.nodes()), alpha); + } + + /** + * Creates an instance for the specified graph. The edge weights are all set to 1 and alpha is set + * to 0. + * + * @param g the input graph + */ + public HITS(Network g) { + this(g, 0.0); + } + + /** Maintains hub and authority score information for a vertex. */ + public static class Scores { + /** The hub score for a vertex. */ + public double hub; + + /** The authority score for a vertex. */ + public double authority; /** - * Creates an instance for the specified graph. The edge weights are all set to 1 - * and alpha is set to 0. - * @param g the input graph + * Creates an instance with the specified hub and authority score. + * + * @param hub the hub score + * @param authority the authority score */ - public HITS(Network g) - { - this(g, 0.0); + public Scores(double hub, double authority) { + this.hub = hub; + this.authority = authority; } - - /** - * Maintains hub and authority score information for a vertex. - */ - public static class Scores - { - /** - * The hub score for a vertex. - */ - public double hub; - - /** - * The authority score for a vertex. - */ - public double authority; - - /** - * Creates an instance with the specified hub and authority score. - * @param hub the hub score - * @param authority the authority score - */ - public Scores(double hub, double authority) - { - this.hub = hub; - this.authority = authority; - } - - @Override - public String toString() - { - return String.format("[h:%.4f,a:%.4f]", this.hub, this.authority); - } + @Override + public String toString() { + return String.format("[h:%.4f,a:%.4f]", this.hub, this.authority); } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/HITSWithPriors.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/HITSWithPriors.java index a4f814a1..59bdf7a1 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/HITSWithPriors.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/HITSWithPriors.java @@ -1,7 +1,7 @@ /* * Created on Jul 14, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -16,169 +16,150 @@ import com.google.common.graph.Network; /** - * A generalization of HITS that permits non-uniformly-distributed random jumps. - * The 'vertex_priors' (that is, prior probabilities for each vertex) may be - * thought of as the fraction of the total 'potential' (hub or authority score) - * that is assigned to that vertex out of the portion that is assigned according - * to random jumps. - * - * @see "Algorithms for Estimating Relative Importance in Graphs by Scott White and Padhraic Smyth, 2003" + * A generalization of HITS that permits non-uniformly-distributed random jumps. The 'vertex_priors' + * (that is, prior probabilities for each vertex) may be thought of as the fraction of the total + * 'potential' (hub or authority score) that is assigned to that vertex out of the portion that is + * assigned according to random jumps. + * + * @see "Algorithms for Estimating Relative Importance in Graphs by Scott White and Padhraic Smyth, + * 2003" */ -public class HITSWithPriors - extends AbstractIterativeScorerWithPriors -{ - /** - * The sum of the potential, at each step, associated with vertices with no outedges (authority) - * or no inedges (hub). - */ - protected HITS.Scores disappearing_potential; - - /** - * Creates an instance for the specified graph, edge weights, vertex prior probabilities, - * and random jump probability (alpha). - * @param g the input graph - * @param edge_weights the edge weights - * @param vertex_priors the prior probability for each vertex - * @param alpha the probability of a random jump at each step - */ - public HITSWithPriors(Network g, - Function edge_weights, - Function vertex_priors, double alpha) - { - super(g, edge_weights, vertex_priors, alpha); - disappearing_potential = new HITS.Scores(0,0); +public class HITSWithPriors extends AbstractIterativeScorerWithPriors { + /** + * The sum of the potential, at each step, associated with vertices with no outedges (authority) + * or no inedges (hub). + */ + protected HITS.Scores disappearing_potential; + + /** + * Creates an instance for the specified graph, edge weights, vertex prior probabilities, and + * random jump probability (alpha). + * + * @param g the input graph + * @param edge_weights the edge weights + * @param vertex_priors the prior probability for each vertex + * @param alpha the probability of a random jump at each step + */ + public HITSWithPriors( + Network g, + Function edge_weights, + Function vertex_priors, + double alpha) { + super(g, edge_weights, vertex_priors, alpha); + disappearing_potential = new HITS.Scores(0, 0); + } + + /** + * Creates an instance for the specified graph, vertex priors, and random jump probability + * (alpha). The edge weights default to 1.0. + * + * @param g the input graph + * @param vertex_priors the prior probability for each vertex + * @param alpha the probability of a random jump at each step + */ + public HITSWithPriors(Network g, Function vertex_priors, double alpha) { + super(g, Functions.constant(1.0), vertex_priors, alpha); + disappearing_potential = new HITS.Scores(0, 0); + } + + /** Updates the value for this vertex. */ + @Override + protected double update(V v) { + collectDisappearingPotential(v); + + double v_auth = 0; + for (V u : graph.predecessors(v)) { + for (E e : graph.edgesConnecting(u, v)) { + v_auth += (getCurrentValue(u).hub * getEdgeWeight(u, e).doubleValue()); + } } - /** - * Creates an instance for the specified graph, vertex priors, and random - * jump probability (alpha). The edge weights default to 1.0. - * @param g the input graph - * @param vertex_priors the prior probability for each vertex - * @param alpha the probability of a random jump at each step - */ - public HITSWithPriors(Network g, - Function vertex_priors, double alpha) - { - super(g, Functions.constant(1.0), vertex_priors, alpha); - disappearing_potential = new HITS.Scores(0,0); + double v_hub = 0; + for (V w : graph.successors(v)) { + for (E e : graph.edgesConnecting(v, w)) { + v_hub += (getCurrentValue(w).authority * getEdgeWeight(w, e).doubleValue()); + } } - /** - * Updates the value for this vertex. - */ - @Override - protected double update(V v) - { - collectDisappearingPotential(v); - - double v_auth = 0; - for (V u : graph.predecessors(v)) { - for (E e : graph.edgesConnecting(u, v)) { - v_auth += (getCurrentValue(u).hub * getEdgeWeight(u,e).doubleValue()); - } - } - - double v_hub = 0; - for (V w : graph.successors(v)) { - for (E e : graph.edgesConnecting(v, w)) { - v_hub += (getCurrentValue(w).authority * getEdgeWeight(w,e).doubleValue()); - } - } - - // modify total_input according to alpha - if (alpha > 0) - { - v_auth = v_auth * (1 - alpha) + getVertexPrior(v).authority * alpha; - v_hub = v_hub * (1 - alpha) + getVertexPrior(v).hub * alpha; - } - setOutputValue(v, new HITS.Scores(v_hub, v_auth)); - - return Math.max(Math.abs(getCurrentValue(v).hub - v_hub), - Math.abs(getCurrentValue(v).authority - v_auth)); + // modify total_input according to alpha + if (alpha > 0) { + v_auth = v_auth * (1 - alpha) + getVertexPrior(v).authority * alpha; + v_hub = v_hub * (1 - alpha) + getVertexPrior(v).hub * alpha; } + setOutputValue(v, new HITS.Scores(v_hub, v_auth)); + + return Math.max( + Math.abs(getCurrentValue(v).hub - v_hub), Math.abs(getCurrentValue(v).authority - v_auth)); + } - /** - * Code which is executed after each step. In this case, deals with the - * 'disappearing potential', normalizes the scores, and then calls - * super.afterStep(). - * @see #collectDisappearingPotential(Object) - */ - @Override - protected void afterStep() - { - if (disappearing_potential.hub > 0 || disappearing_potential.authority > 0) - { - for (V v : graph.nodes()) - { - double new_hub = getOutputValue(v).hub + - (1 - alpha) * (disappearing_potential.hub * getVertexPrior(v).hub); - double new_auth = getOutputValue(v).authority + - (1 - alpha) * (disappearing_potential.authority * getVertexPrior(v).authority); - setOutputValue(v, new HITS.Scores(new_hub, new_auth)); - } - disappearing_potential.hub = 0; - disappearing_potential.authority = 0; - } - - normalizeScores(); - - super.afterStep(); + /** + * Code which is executed after each step. In this case, deals with the 'disappearing potential', + * normalizes the scores, and then calls super.afterStep(). + * + * @see #collectDisappearingPotential(Object) + */ + @Override + protected void afterStep() { + if (disappearing_potential.hub > 0 || disappearing_potential.authority > 0) { + for (V v : graph.nodes()) { + double new_hub = + getOutputValue(v).hub + + (1 - alpha) * (disappearing_potential.hub * getVertexPrior(v).hub); + double new_auth = + getOutputValue(v).authority + + (1 - alpha) * (disappearing_potential.authority * getVertexPrior(v).authority); + setOutputValue(v, new HITS.Scores(new_hub, new_auth)); + } + disappearing_potential.hub = 0; + disappearing_potential.authority = 0; } - /** - * Normalizes scores so that sum of their squares = 1. - * This method may be overridden so as to yield different - * normalizations. - */ - protected void normalizeScores() { - double hub_ssum = 0; - double auth_ssum = 0; - for (V v : graph.nodes()) - { - double hub_val = getOutputValue(v).hub; - double auth_val = getOutputValue(v).authority; - hub_ssum += (hub_val * hub_val); - auth_ssum += (auth_val * auth_val); - } - - hub_ssum = Math.sqrt(hub_ssum); - auth_ssum = Math.sqrt(auth_ssum); - - for (V v : graph.nodes()) - { - HITS.Scores values = getOutputValue(v); - setOutputValue(v, new HITS.Scores( - values.hub / hub_ssum, - values.authority / auth_ssum)); - } - } - - /** - * Collects the "disappearing potential" associated with vertices that have either - * no incoming edges, no outgoing edges, or both. Vertices that have no incoming edges - * do not directly contribute to the hub scores of other vertices; similarly, vertices - * that have no outgoing edges do not directly contribute to the authority scores of - * other vertices. These values are collected at each step and then distributed across all vertices - * as a part of the normalization process. (This process is not required for, and does - * not affect, the 'sum-of-squares'-style normalization.) - */ - @Override - protected void collectDisappearingPotential(V v) - { - if (graph.outDegree(v) == 0) - { - if (isDisconnectedGraphOK()) - disappearing_potential.hub += getCurrentValue(v).authority; - else - throw new IllegalArgumentException("Outdegree of " + v + " must be > 0"); - } - if (graph.inDegree(v) == 0) - { - if (isDisconnectedGraphOK()) - disappearing_potential.authority += getCurrentValue(v).hub; - else - throw new IllegalArgumentException("Indegree of " + v + " must be > 0"); - } + normalizeScores(); + + super.afterStep(); + } + + /** + * Normalizes scores so that sum of their squares = 1. This method may be overridden so as to + * yield different normalizations. + */ + protected void normalizeScores() { + double hub_ssum = 0; + double auth_ssum = 0; + for (V v : graph.nodes()) { + double hub_val = getOutputValue(v).hub; + double auth_val = getOutputValue(v).authority; + hub_ssum += (hub_val * hub_val); + auth_ssum += (auth_val * auth_val); + } + + hub_ssum = Math.sqrt(hub_ssum); + auth_ssum = Math.sqrt(auth_ssum); + + for (V v : graph.nodes()) { + HITS.Scores values = getOutputValue(v); + setOutputValue(v, new HITS.Scores(values.hub / hub_ssum, values.authority / auth_ssum)); } + } + /** + * Collects the "disappearing potential" associated with vertices that have either no incoming + * edges, no outgoing edges, or both. Vertices that have no incoming edges do not directly + * contribute to the hub scores of other vertices; similarly, vertices that have no outgoing edges + * do not directly contribute to the authority scores of other vertices. These values are + * collected at each step and then distributed across all vertices as a part of the normalization + * process. (This process is not required for, and does not affect, the 'sum-of-squares'-style + * normalization.) + */ + @Override + protected void collectDisappearingPotential(V v) { + if (graph.outDegree(v) == 0) { + if (isDisconnectedGraphOK()) disappearing_potential.hub += getCurrentValue(v).authority; + else throw new IllegalArgumentException("Outdegree of " + v + " must be > 0"); + } + if (graph.inDegree(v) == 0) { + if (isDisconnectedGraphOK()) disappearing_potential.authority += getCurrentValue(v).hub; + else throw new IllegalArgumentException("Indegree of " + v + " must be > 0"); + } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/KStepMarkov.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/KStepMarkov.java index 8ad9209a..bb59d609 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/KStepMarkov.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/KStepMarkov.java @@ -1,147 +1,134 @@ /** - * Copyright (c) 2008, The JUNG Authors + * Copyright (c) 2008, The JUNG Authors * - * All rights reserved. + *

All rights reserved. * - * This software is open-source under the BSD license; see either - * "license.txt" or - * https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * Created on Aug 22, 2008 - * + *

This software is open-source under the BSD license; see either "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. Created on Aug 22, 2008 */ package edu.uci.ics.jung.algorithms.scoring; import com.google.common.base.Function; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.scoring.util.ScoringUtils; /** - * A special case of {@code PageRankWithPriors} in which the final scores - * represent a probability distribution over position assuming a random (Markovian) - * walk of exactly k steps, based on the initial distribution specified by the priors. - * - *

NOTE: The version of {@code KStepMarkov} in {@code algorithms.importance} - * (and in JUNG 1.x) is believed to be incorrect: rather than returning - * a score which represents a probability distribution over position assuming - * a k-step random walk, it returns a score which represents the sum over all steps - * of the probability for each step. If you want that behavior, set the + * A special case of {@code PageRankWithPriors} in which the final scores represent a probability + * distribution over position assuming a random (Markovian) walk of exactly k steps, based on the + * initial distribution specified by the priors. + * + *

NOTE: The version of {@code KStepMarkov} in {@code algorithms.importance} (and in JUNG + * 1.x) is believed to be incorrect: rather than returning a score which represents a probability + * distribution over position assuming a k-step random walk, it returns a score which represents the + * sum over all steps of the probability for each step. If you want that behavior, set the * 'cumulative' flag as follows before calling {@code evaluate()}: + * *

  *     KStepMarkov ksm = new KStepMarkov(...);
  *     ksm.setCumulative(true);
  *     ksm.evaluate();
  * 
- * + * * By default, the 'cumulative' flag is set to false. - * - * NOTE: THIS CLASS IS NOT YET COMPLETE. USE AT YOUR OWN RISK. (The original behavior - * is captured by the version still available in {@code algorithms.importance}.) - * - * @see "Algorithms for Estimating Relative Importance in Graphs by Scott White and Padhraic Smyth, 2003" + * + *

NOTE: THIS CLASS IS NOT YET COMPLETE. USE AT YOUR OWN RISK. (The original behavior is captured + * by the version still available in {@code algorithms.importance}.) + * + * @see "Algorithms for Estimating Relative Importance in Graphs by Scott White and Padhraic Smyth, + * 2003" * @see PageRank * @see PageRankWithPriors */ -public class KStepMarkov extends PageRankWithPriors -{ - private boolean cumulative; - - /** - * Creates an instance based on the specified graph, edge weights, vertex - * priors (initial scores), and number of steps to take. - * @param graph the input graph - * @param edge_weights the edge weights (transition probabilities) - * @param vertex_priors the initial probability distribution (score assignment) - * @param steps the number of times that {@code step()} will be called by {@code evaluate} - */ - public KStepMarkov(Network graph, Function edge_weights, - Function vertex_priors, int steps) - { - super(graph, edge_weights, vertex_priors, 0); - initialize(steps); - } - - /** - * Creates an instance based on the specified graph, vertex - * priors (initial scores), and number of steps to take. The edge - * weights (transition probabilities) are set to default values (a uniform - * distribution over all outgoing edges). - * @param graph the input graph - * @param vertex_priors the initial probability distribution (score assignment) - * @param steps the number of times that {@code step()} will be called by {@code evaluate} - */ - public KStepMarkov(Network graph, Function vertex_priors, int steps) - { - super(graph, vertex_priors, 0); - initialize(steps); - } - - /** - * Creates an instance based on the specified graph and number of steps to - * take. The edge weights (transition probabilities) and vertex initial scores - * (prior probabilities) are set to default values (a uniform - * distribution over all outgoing edges, and a uniform distribution over - * all vertices, respectively). - * @param graph the input graph - * @param steps the number of times that {@code step()} will be called by {@code evaluate} - */ - public KStepMarkov(Network graph, int steps) - { - super(graph, ScoringUtils.getUniformRootPrior(graph.nodes()), 0); - initialize(steps); - } - - private void initialize(int steps) - { - this.acceptDisconnectedGraph(false); - - if (steps <= 0) - throw new IllegalArgumentException("Number of steps must be > 0"); - - this.max_iterations = steps; - this.tolerance = -1.0; - - this.cumulative = false; - } - - /** - * Specifies whether this instance should assign a score to each vertex - * based on the sum over all steps of the probability for each step. - * See the class-level documentation for details. - * @param cumulative true if this instance should assign a cumulative score to each vertex - */ - public void setCumulative(boolean cumulative) - { - this.cumulative = cumulative; - } - - /** - * Updates the value for this vertex. Called by step(). - */ - @Override - public double update(V v) - { - if (!cumulative) - return super.update(v); - - collectDisappearingPotential(v); - - double v_input = 0; - for (V u : graph.predecessors(v)) { - for (E e : graph.edgesConnecting(u, v)) { - v_input += (getCurrentValue(u) * getEdgeWeight(u,e).doubleValue()); - } - } - - // modify total_input according to alpha - double new_value = alpha > 0 ? - v_input * (1 - alpha) + getVertexPrior(v) * alpha : - v_input; - setOutputValue(v, new_value + getCurrentValue(v)); - - // FIXME: DO WE NEED TO CHANGE HOW DISAPPEARING IS COUNTED? NORMALIZE? - - return Math.abs(getCurrentValue(v) - new_value); +public class KStepMarkov extends PageRankWithPriors { + private boolean cumulative; + + /** + * Creates an instance based on the specified graph, edge weights, vertex priors (initial scores), + * and number of steps to take. + * + * @param graph the input graph + * @param edge_weights the edge weights (transition probabilities) + * @param vertex_priors the initial probability distribution (score assignment) + * @param steps the number of times that {@code step()} will be called by {@code evaluate} + */ + public KStepMarkov( + Network graph, + Function edge_weights, + Function vertex_priors, + int steps) { + super(graph, edge_weights, vertex_priors, 0); + initialize(steps); + } + + /** + * Creates an instance based on the specified graph, vertex priors (initial scores), and number of + * steps to take. The edge weights (transition probabilities) are set to default values (a uniform + * distribution over all outgoing edges). + * + * @param graph the input graph + * @param vertex_priors the initial probability distribution (score assignment) + * @param steps the number of times that {@code step()} will be called by {@code evaluate} + */ + public KStepMarkov(Network graph, Function vertex_priors, int steps) { + super(graph, vertex_priors, 0); + initialize(steps); + } + + /** + * Creates an instance based on the specified graph and number of steps to take. The edge weights + * (transition probabilities) and vertex initial scores (prior probabilities) are set to default + * values (a uniform distribution over all outgoing edges, and a uniform distribution over all + * vertices, respectively). + * + * @param graph the input graph + * @param steps the number of times that {@code step()} will be called by {@code evaluate} + */ + public KStepMarkov(Network graph, int steps) { + super(graph, ScoringUtils.getUniformRootPrior(graph.nodes()), 0); + initialize(steps); + } + + private void initialize(int steps) { + this.acceptDisconnectedGraph(false); + + if (steps <= 0) throw new IllegalArgumentException("Number of steps must be > 0"); + + this.max_iterations = steps; + this.tolerance = -1.0; + + this.cumulative = false; + } + + /** + * Specifies whether this instance should assign a score to each vertex based on the sum over all + * steps of the probability for each step. See the class-level documentation for details. + * + * @param cumulative true if this instance should assign a cumulative score to each vertex + */ + public void setCumulative(boolean cumulative) { + this.cumulative = cumulative; + } + + /** Updates the value for this vertex. Called by step(). */ + @Override + public double update(V v) { + if (!cumulative) return super.update(v); + + collectDisappearingPotential(v); + + double v_input = 0; + for (V u : graph.predecessors(v)) { + for (E e : graph.edgesConnecting(u, v)) { + v_input += (getCurrentValue(u) * getEdgeWeight(u, e).doubleValue()); + } } + // modify total_input according to alpha + double new_value = alpha > 0 ? v_input * (1 - alpha) + getVertexPrior(v) * alpha : v_input; + setOutputValue(v, new_value + getCurrentValue(v)); + + // FIXME: DO WE NEED TO CHANGE HOW DISAPPEARING IS COUNTED? NORMALIZE? + + return Math.abs(getCurrentValue(v) - new_value); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/PageRank.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/PageRank.java index 169e159c..f2e9e096 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/PageRank.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/PageRank.java @@ -1,7 +1,7 @@ /* * Created on Jul 12, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -13,58 +13,55 @@ import com.google.common.base.Function; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.scoring.util.ScoringUtils; /** - * Assigns scores to each vertex according to the PageRank algorithm. - * - *

PageRank is an eigenvector-based algorithm. The score for a given vertex may be thought of - * as the fraction of time spent 'visiting' that vertex (measured over all time) - * in a random walk over the vertices (following outgoing edges from each vertex). - * PageRank modifies this random walk by adding to the model a probability (specified as 'alpha' - * in the constructor) of jumping to any vertex. If alpha is 0, this is equivalent to the - * eigenvector centrality algorithm; if alpha is 1, all vertices will receive the same score - * (1/|V|). Thus, alpha acts as a sort of score smoothing parameter. - * - *

The original algorithm assumed that, for a given vertex, the probability of following any - * outgoing edge was the same; this is the default if edge weights are not specified. - * This implementation generalizes the original by permitting - * the user to specify edge weights; in order to maintain the original semantics, however, - * the weights on the outgoing edges for a given vertex must represent transition probabilities; - * that is, they must sum to 1. - * + * Assigns scores to each vertex according to the PageRank algorithm. + * + *

PageRank is an eigenvector-based algorithm. The score for a given vertex may be thought of as + * the fraction of time spent 'visiting' that vertex (measured over all time) in a random walk over + * the vertices (following outgoing edges from each vertex). PageRank modifies this random walk by + * adding to the model a probability (specified as 'alpha' in the constructor) of jumping to any + * vertex. If alpha is 0, this is equivalent to the eigenvector centrality algorithm; if alpha is 1, + * all vertices will receive the same score (1/|V|). Thus, alpha acts as a sort of score smoothing + * parameter. + * + *

The original algorithm assumed that, for a given vertex, the probability of following any + * outgoing edge was the same; this is the default if edge weights are not specified. This + * implementation generalizes the original by permitting the user to specify edge weights; in order + * to maintain the original semantics, however, the weights on the outgoing edges for a given vertex + * must represent transition probabilities; that is, they must sum to 1. + * *

If a vertex has no outgoing edges, then the probability of taking a random jump from that - * vertex is (by default) effectively 1. If the user wishes to instead throw an exception when this happens, - * call acceptDisconnectedGraph(false) on this instance. - * - *

Typical values for alpha (according to the original paper) are in the range [0.1, 0.2] - * but may be any value between 0 and 1 inclusive. - * + * vertex is (by default) effectively 1. If the user wishes to instead throw an exception when this + * happens, call acceptDisconnectedGraph(false) on this instance. + * + *

Typical values for alpha (according to the original paper) are in the range [0.1, 0.2] but may + * be any value between 0 and 1 inclusive. + * * @see "The Anatomy of a Large-Scale Hypertextual Web Search Engine by L. Page and S. Brin, 1999" */ -public class PageRank extends PageRankWithPriors -{ +public class PageRank extends PageRankWithPriors { - /** - * Creates an instance for the specified graph, edge weights, and random jump probability. - * @param graph the input graph - * @param edge_weight the edge weights (transition probabilities) - * @param alpha the probability of taking a random jump to an arbitrary vertex - */ - public PageRank(Network graph, Function edge_weight, double alpha) - { - super(graph, edge_weight, ScoringUtils.getUniformRootPrior(graph.nodes()), alpha); - } + /** + * Creates an instance for the specified graph, edge weights, and random jump probability. + * + * @param graph the input graph + * @param edge_weight the edge weights (transition probabilities) + * @param alpha the probability of taking a random jump to an arbitrary vertex + */ + public PageRank(Network graph, Function edge_weight, double alpha) { + super(graph, edge_weight, ScoringUtils.getUniformRootPrior(graph.nodes()), alpha); + } - /** - * Creates an instance for the specified graph and random jump probability; the probability - * of following any outgoing edge from a given vertex is the same. - * @param graph the input graph - * @param alpha the probability of taking a random jump to an arbitrary vertex - */ - public PageRank(Network graph, double alpha) - { - super(graph, ScoringUtils.getUniformRootPrior(graph.nodes()), alpha); - } + /** + * Creates an instance for the specified graph and random jump probability; the probability of + * following any outgoing edge from a given vertex is the same. + * + * @param graph the input graph + * @param alpha the probability of taking a random jump to an arbitrary vertex + */ + public PageRank(Network graph, double alpha) { + super(graph, ScoringUtils.getUniformRootPrior(graph.nodes()), alpha); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/PageRankWithPriors.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/PageRankWithPriors.java index fa84ca7f..e3dc22ed 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/PageRankWithPriors.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/PageRankWithPriors.java @@ -1,7 +1,7 @@ /* * Created on Jul 6, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -13,120 +13,102 @@ import com.google.common.base.Function; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.scoring.util.UniformDegreeWeight; /** - * A generalization of PageRank that permits non-uniformly-distributed random jumps. - * The 'vertex_priors' (that is, prior probabilities for each vertex) may be - * thought of as the fraction of the total 'potential' that is assigned to that - * vertex at each step out of the portion that is assigned according - * to random jumps (this portion is specified by 'alpha'). - * - * @see "Algorithms for Estimating Relative Importance in Graphs by Scott White and Padhraic Smyth, 2003" + * A generalization of PageRank that permits non-uniformly-distributed random jumps. The + * 'vertex_priors' (that is, prior probabilities for each vertex) may be thought of as the fraction + * of the total 'potential' that is assigned to that vertex at each step out of the portion that is + * assigned according to random jumps (this portion is specified by 'alpha'). + * + * @see "Algorithms for Estimating Relative Importance in Graphs by Scott White and Padhraic Smyth, + * 2003" * @see PageRank */ -public class PageRankWithPriors - extends AbstractIterativeScorerWithPriors -{ - /** - * Maintains the amount of potential associated with vertices with no out-edges. - */ - protected double disappearing_potential = 0.0; - - /** - * Creates an instance with the specified graph, edge weights, vertex priors, and - * 'random jump' probability (alpha). - * @param graph the input graph - * @param edge_weights the edge weights, denoting transition probabilities from source to destination - * @param vertex_priors the prior probabilities for each vertex - * @param alpha the probability of executing a 'random jump' at each step - */ - public PageRankWithPriors(Network graph, - Function edge_weights, - Function vertex_priors, double alpha) - { - super(graph, edge_weights, vertex_priors, alpha); - } - - /** - * Creates an instance with the specified graph, vertex priors, and - * 'random jump' probability (alpha). The outgoing edge weights for each - * vertex will be equal and sum to 1. - * @param graph the input graph - * @param vertex_priors the prior probabilities for each vertex - * @param alpha the probability of executing a 'random jump' at each step - */ - public PageRankWithPriors(Network graph, - Function vertex_priors, double alpha) - { - super(graph, vertex_priors, alpha); - this.edge_weights = new UniformDegreeWeight(graph); - } - - /** - * Updates the value for this vertex. Called by step(). - */ - @Override - public double update(V v) - { - collectDisappearingPotential(v); - - double v_input = 0; - for (V u : graph.predecessors(v)) { - for (E e : graph.edgesConnecting(u, v)) { - v_input += (getCurrentValue(u) * getEdgeWeight(u, e).doubleValue()); - } - } - - // modify total_input according to alpha - double new_value = alpha > 0 ? - v_input * (1 - alpha) + getVertexPrior(v) * alpha : - v_input; - setOutputValue(v, new_value); - - return Math.abs(getCurrentValue(v) - new_value); +public class PageRankWithPriors extends AbstractIterativeScorerWithPriors { + /** Maintains the amount of potential associated with vertices with no out-edges. */ + protected double disappearing_potential = 0.0; + + /** + * Creates an instance with the specified graph, edge weights, vertex priors, and 'random jump' + * probability (alpha). + * + * @param graph the input graph + * @param edge_weights the edge weights, denoting transition probabilities from source to + * destination + * @param vertex_priors the prior probabilities for each vertex + * @param alpha the probability of executing a 'random jump' at each step + */ + public PageRankWithPriors( + Network graph, + Function edge_weights, + Function vertex_priors, + double alpha) { + super(graph, edge_weights, vertex_priors, alpha); + } + + /** + * Creates an instance with the specified graph, vertex priors, and 'random jump' probability + * (alpha). The outgoing edge weights for each vertex will be equal and sum to 1. + * + * @param graph the input graph + * @param vertex_priors the prior probabilities for each vertex + * @param alpha the probability of executing a 'random jump' at each step + */ + public PageRankWithPriors(Network graph, Function vertex_priors, double alpha) { + super(graph, vertex_priors, alpha); + this.edge_weights = new UniformDegreeWeight(graph); + } + + /** Updates the value for this vertex. Called by step(). */ + @Override + public double update(V v) { + collectDisappearingPotential(v); + + double v_input = 0; + for (V u : graph.predecessors(v)) { + for (E e : graph.edgesConnecting(u, v)) { + v_input += (getCurrentValue(u) * getEdgeWeight(u, e).doubleValue()); + } } - /** - * Cleans up after each step. In this case that involves allocating the disappearing - * potential (thus maintaining normalization of the scores) according to the vertex - * probability priors, and then calling - * super.afterStep. - */ - @Override - protected void afterStep() - { - // distribute disappearing potential according to priors - if (disappearing_potential > 0) - { - for (V v : graph.nodes()) - { - setOutputValue(v, getOutputValue(v) + - (1 - alpha) * (disappearing_potential * getVertexPrior(v))); - } - disappearing_potential = 0; - } - - super.afterStep(); + // modify total_input according to alpha + double new_value = alpha > 0 ? v_input * (1 - alpha) + getVertexPrior(v) * alpha : v_input; + setOutputValue(v, new_value); + + return Math.abs(getCurrentValue(v) - new_value); + } + + /** + * Cleans up after each step. In this case that involves allocating the disappearing potential + * (thus maintaining normalization of the scores) according to the vertex probability priors, and + * then calling super.afterStep. + */ + @Override + protected void afterStep() { + // distribute disappearing potential according to priors + if (disappearing_potential > 0) { + for (V v : graph.nodes()) { + setOutputValue( + v, getOutputValue(v) + (1 - alpha) * (disappearing_potential * getVertexPrior(v))); + } + disappearing_potential = 0; } - - /** - * Collects the "disappearing potential" associated with vertices that have - * no outgoing edges. Vertices that have no outgoing edges do not directly - * contribute to the scores of other vertices. These values are collected - * at each step and then distributed across all vertices - * as a part of the normalization process. - */ - @Override - protected void collectDisappearingPotential(V v) - { - if (graph.outDegree(v) == 0) - { - if (isDisconnectedGraphOK()) - disappearing_potential += getCurrentValue(v); - else - throw new IllegalArgumentException("Outdegree of " + v + " must be > 0"); - } + + super.afterStep(); + } + + /** + * Collects the "disappearing potential" associated with vertices that have no outgoing edges. + * Vertices that have no outgoing edges do not directly contribute to the scores of other + * vertices. These values are collected at each step and then distributed across all vertices as a + * part of the normalization process. + */ + @Override + protected void collectDisappearingPotential(V v) { + if (graph.outDegree(v) == 0) { + if (isDisconnectedGraphOK()) disappearing_potential += getCurrentValue(v); + else throw new IllegalArgumentException("Outdegree of " + v + " must be > 0"); } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/VertexScorer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/VertexScorer.java index fdd1011e..965efb96 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/VertexScorer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/VertexScorer.java @@ -1,7 +1,7 @@ /* * Created on Jul 6, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -19,13 +19,12 @@ * @param the vertex type * @param the score type */ -public interface VertexScorer -{ - /** - * @param v the vertex whose score is requested - * @return the algorithm's score for this vertex - */ - public S getVertexScore(V v); - - public Map vertexScores(); +public interface VertexScorer { + /** + * @param v the vertex whose score is requested + * @return the algorithm's score for this vertex + */ + public S getVertexScore(V v); + + public Map vertexScores(); } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/VoltageScorer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/VoltageScorer.java index a60c4794..825b8820 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/VoltageScorer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/VoltageScorer.java @@ -1,7 +1,7 @@ /* * Created on Jul 15, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -11,237 +11,217 @@ */ package edu.uci.ics.jung.algorithms.scoring; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.scoring.util.UniformDegreeWeight; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; /** - * Assigns scores to vertices according to their 'voltage' in an approximate - * solution to the Kirchoff equations. This is accomplished by tying "source" - * vertices to specified positive voltages, "sink" vertices to 0 V, and - * iteratively updating the voltage of each other vertex to the (weighted) - * average of the voltages of its neighbors. - * - *

The resultant voltages will all be in the range [0, max] - * where max is the largest voltage of any source vertex (in the - * absence of negative source voltages; see below). - * - *

A few notes about this algorithm's interpretation of the graph data: + * Assigns scores to vertices according to their 'voltage' in an approximate solution to the + * Kirchoff equations. This is accomplished by tying "source" vertices to specified positive + * voltages, "sink" vertices to 0 V, and iteratively updating the voltage of each other vertex to + * the (weighted) average of the voltages of its neighbors. + * + *

The resultant voltages will all be in the range [0, max] where max + * is the largest voltage of any source vertex (in the absence of negative source voltages; see + * below). + * + *

A few notes about this algorithm's interpretation of the graph data: + * *

    - *
  • Higher edge weights are interpreted as indicative of greater - * influence/effect than lower edge weights. - *
  • Negative edge weights (and negative "source" voltages) invalidate - * the interpretation of the resultant values as voltages. However, this - * algorithm will not reject graphs with negative edge weights or source voltages. - *
  • Parallel edges are equivalent to a single edge whose weight is the - * sum of the weights on the parallel edges. - *
  • Current flows along undirected edges in both directions, - * but only flows along directed edges in the direction of the edge. + *
  • Higher edge weights are interpreted as indicative of greater influence/effect than lower + * edge weights. + *
  • Negative edge weights (and negative "source" voltages) invalidate the interpretation of the + * resultant values as voltages. However, this algorithm will not reject graphs with negative + * edge weights or source voltages. + *
  • Parallel edges are equivalent to a single edge whose weight is the sum of the weights on + * the parallel edges. + *
  • Current flows along undirected edges in both directions, but only flows along directed + * edges in the direction of the edge. *
- * */ public class VoltageScorer extends AbstractIterativeScorer - implements VertexScorer -{ - protected Map source_voltages; - protected Set sinks; - - /** - * Creates an instance with the specified graph, edge weights, source voltages, - * and sinks. - * @param g the input graph - * @param edge_weights the edge weights, representing conductivity - * @param source_voltages the (fixed) voltage for each source - * @param sinks the vertices whose voltages are tied to 0 - */ - public VoltageScorer(Network g, Function edge_weights, - Map source_voltages, Set sinks) - { - super(g, edge_weights); - this.source_voltages = source_voltages; - this.sinks = sinks; - initialize(); - } + implements VertexScorer { + protected Map source_voltages; + protected Set sinks; + + /** + * Creates an instance with the specified graph, edge weights, source voltages, and sinks. + * + * @param g the input graph + * @param edge_weights the edge weights, representing conductivity + * @param source_voltages the (fixed) voltage for each source + * @param sinks the vertices whose voltages are tied to 0 + */ + public VoltageScorer( + Network g, + Function edge_weights, + Map source_voltages, + Set sinks) { + super(g, edge_weights); + this.source_voltages = source_voltages; + this.sinks = sinks; + initialize(); + } - /** - * Creates an instance with the specified graph, edge weights, source vertices - * (each of whose 'voltages' are tied to 1), and sinks. - * @param g the input graph - * @param edge_weights the edge weights, representing conductivity - * @param sources the vertices whose voltages are tied to 1 - * @param sinks the vertices whose voltages are tied to 0 - */ - public VoltageScorer(Network g, Function edge_weights, - Set sources, Set sinks) - { - super(g, edge_weights); - - Map unit_voltages = new HashMap(); - for(V v : sources) - unit_voltages.put(v, new Double(1.0)); - this.source_voltages = unit_voltages; - this.sinks = sinks; - initialize(); + /** + * Creates an instance with the specified graph, edge weights, source vertices (each of whose + * 'voltages' are tied to 1), and sinks. + * + * @param g the input graph + * @param edge_weights the edge weights, representing conductivity + * @param sources the vertices whose voltages are tied to 1 + * @param sinks the vertices whose voltages are tied to 0 + */ + public VoltageScorer( + Network g, + Function edge_weights, + Set sources, + Set sinks) { + super(g, edge_weights); + + Map unit_voltages = new HashMap(); + for (V v : sources) unit_voltages.put(v, new Double(1.0)); + this.source_voltages = unit_voltages; + this.sinks = sinks; + initialize(); + } + + /** + * Creates an instance with the specified graph, source vertices (each of whose 'voltages' are + * tied to 1), and sinks. The outgoing edges for each vertex are assigned weights that sum to 1. + * + * @param g the input graph + * @param sources the vertices whose voltages are tied to 1 + * @param sinks the vertices whose voltages are tied to 0 + */ + public VoltageScorer(Network g, Set sources, Set sinks) { + super(g); + + Map unit_voltages = new HashMap(); + for (V v : sources) unit_voltages.put(v, new Double(1.0)); + this.source_voltages = unit_voltages; + this.sinks = sinks; + initialize(); + } + + /** + * Creates an instance with the specified graph, source voltages, and sinks. The outgoing edges + * for each vertex are assigned weights that sum to 1. + * + * @param g the input graph + * @param source_voltages the (fixed) voltage for each source + * @param sinks the vertices whose voltages are tied to 0 + */ + public VoltageScorer(Network g, Map source_voltages, Set sinks) { + super(g); + this.source_voltages = source_voltages; + this.sinks = sinks; + this.edge_weights = new UniformDegreeWeight(g); + initialize(); + } + + /** + * Creates an instance with the specified graph, edge weights, source, and sink. The source vertex + * voltage is tied to 1. + * + * @param g the input graph + * @param edge_weights the edge weights, representing conductivity + * @param source the vertex whose voltage is tied to 1 + * @param sink the vertex whose voltage is tied to 0 + */ + public VoltageScorer( + Network g, Function edge_weights, V source, V sink) { + this(g, edge_weights, ImmutableMap.of(source, 1.0), ImmutableSet.of(sink)); + initialize(); + } + + /** + * Creates an instance with the specified graph, edge weights, source, and sink. The source vertex + * voltage is tied to 1. The outgoing edges for each vertex are assigned weights that sum to 1. + * + * @param g the input graph + * @param source the vertex whose voltage is tied to 1 + * @param sink the vertex whose voltage is tied to 0 + */ + public VoltageScorer(Network g, V source, V sink) { + this(g, ImmutableMap.of(source, 1.0), ImmutableSet.of(sink)); + initialize(); + } + + /** Initializes the state of this instance. */ + @Override + public void initialize() { + super.initialize(); + + // sanity check + Preconditions.checkArgument(!source_voltages.isEmpty(), "Source voltages must be non-empty"); + Preconditions.checkArgument(!sinks.isEmpty(), "Sinks must be non-empty"); + + Preconditions.checkArgument( + Sets.intersection(source_voltages.keySet(), sinks).isEmpty(), + "Sources and sinks must be disjoint"); + Preconditions.checkArgument( + graph.nodes().containsAll(source_voltages.keySet()), + "Sources must all be elements of the graph"); + Preconditions.checkArgument( + graph.nodes().containsAll(sinks), "Sinks must all be elements of the graph"); + + for (Map.Entry entry : source_voltages.entrySet()) { + V v = entry.getKey(); + if (sinks.contains(v)) + throw new IllegalArgumentException( + "Vertex " + v + " is incorrectly specified as both source and sink"); + double value = entry.getValue().doubleValue(); + if (value <= 0) + throw new IllegalArgumentException("Source vertex " + v + " has negative voltage"); } - /** - * Creates an instance with the specified graph, source vertices - * (each of whose 'voltages' are tied to 1), and sinks. - * The outgoing edges for each vertex are assigned - * weights that sum to 1. - * @param g the input graph - * @param sources the vertices whose voltages are tied to 1 - * @param sinks the vertices whose voltages are tied to 0 - */ - public VoltageScorer(Network g, Set sources, Set sinks) - { - super(g); - - Map unit_voltages = new HashMap(); - for(V v : sources) - unit_voltages.put(v, new Double(1.0)); - this.source_voltages = unit_voltages; - this.sinks = sinks; - initialize(); + // set up initial voltages + for (V v : graph.nodes()) { + if (source_voltages.containsKey(v)) setOutputValue(v, source_voltages.get(v).doubleValue()); + else setOutputValue(v, 0.0); } - - /** - * Creates an instance with the specified graph, source voltages, - * and sinks. The outgoing edges for each vertex are assigned - * weights that sum to 1. - * @param g the input graph - * @param source_voltages the (fixed) voltage for each source - * @param sinks the vertices whose voltages are tied to 0 - */ - public VoltageScorer(Network g, Map source_voltages, - Set sinks) - { - super(g); - this.source_voltages = source_voltages; - this.sinks = sinks; - this.edge_weights = new UniformDegreeWeight(g); - initialize(); + } + + /** @see edu.uci.ics.jung.algorithms.scoring.AbstractIterativeScorer#update(Object) */ + @Override + public double update(V v) { + // if it's a voltage source or sink, we're done + Number source_volts = source_voltages.get(v); + if (source_volts != null) { + setOutputValue(v, source_volts.doubleValue()); + return 0.0; } - - /** - * Creates an instance with the specified graph, edge weights, source, and - * sink. The source vertex voltage is tied to 1. - * @param g the input graph - * @param edge_weights the edge weights, representing conductivity - * @param source the vertex whose voltage is tied to 1 - * @param sink the vertex whose voltage is tied to 0 - */ - public VoltageScorer(Network g, Function edge_weights, - V source, V sink) - { - this(g, edge_weights, ImmutableMap.of(source, 1.0), ImmutableSet.of(sink)); - initialize(); + if (sinks.contains(v)) { + setOutputValue(v, 0.0); + return 0.0; } - /** - * Creates an instance with the specified graph, edge weights, source, and - * sink. The source vertex voltage is tied to 1. - * The outgoing edges for each vertex are assigned - * weights that sum to 1. - * @param g the input graph - * @param source the vertex whose voltage is tied to 1 - * @param sink the vertex whose voltage is tied to 0 - */ - public VoltageScorer(Network g, V source, V sink) - { - this(g, ImmutableMap.of(source, 1.0), ImmutableSet.of(sink)); - initialize(); + double voltage_sum = 0; + double weight_sum = 0; + for (V u : graph.predecessors(v)) { + for (E e : graph.edgesConnecting(u, v)) { + double weight = getEdgeWeight(u, e).doubleValue(); + voltage_sum += getCurrentValue(u).doubleValue() * weight; + weight_sum += weight; + } } - - /** - * Initializes the state of this instance. - */ - @Override - public void initialize() - { - super.initialize(); - - // sanity check - Preconditions.checkArgument(!source_voltages.isEmpty(), "Source voltages must be non-empty"); - Preconditions.checkArgument(!sinks.isEmpty(), "Sinks must be non-empty"); - - Preconditions.checkArgument(Sets.intersection(source_voltages.keySet(), sinks).isEmpty(), - "Sources and sinks must be disjoint"); - Preconditions.checkArgument(graph.nodes().containsAll(source_voltages.keySet()), - "Sources must all be elements of the graph"); - Preconditions.checkArgument(graph.nodes().containsAll(sinks), - "Sinks must all be elements of the graph"); - - for (Map.Entry entry : source_voltages.entrySet()) - { - V v = entry.getKey(); - if (sinks.contains(v)) - throw new IllegalArgumentException("Vertex " + v + " is incorrectly specified as both source and sink"); - double value = entry.getValue().doubleValue(); - if (value <= 0) - throw new IllegalArgumentException("Source vertex " + v + " has negative voltage"); - } - - // set up initial voltages - for (V v : graph.nodes()) - { - if (source_voltages.containsKey(v)) - setOutputValue(v, source_voltages.get(v).doubleValue()); - else - setOutputValue(v, 0.0); - } - } - - /** - * @see edu.uci.ics.jung.algorithms.scoring.AbstractIterativeScorer#update(Object) - */ - @Override - public double update(V v) - { - // if it's a voltage source or sink, we're done - Number source_volts = source_voltages.get(v); - if (source_volts != null) - { - setOutputValue(v, source_volts.doubleValue()); - return 0.0; - } - if (sinks.contains(v)) - { - setOutputValue(v, 0.0); - return 0.0; - } - - double voltage_sum = 0; - double weight_sum = 0; - for (V u : graph.predecessors(v)) { - for (E e : graph.edgesConnecting(u, v)) { - double weight = getEdgeWeight(u, e).doubleValue(); - voltage_sum += getCurrentValue(u).doubleValue() * weight; - weight_sum += weight; - } - } - - // if either is 0, new value is 0 - if (voltage_sum == 0 || weight_sum == 0) - { - setOutputValue(v, 0.0); - return getCurrentValue(v).doubleValue(); - } - - double outputValue = voltage_sum / weight_sum; - setOutputValue(v, outputValue); - return Math.abs(getCurrentValue(v).doubleValue() - outputValue); + // if either is 0, new value is 0 + if (voltage_sum == 0 || weight_sum == 0) { + setOutputValue(v, 0.0); + return getCurrentValue(v).doubleValue(); } + double outputValue = voltage_sum / weight_sum; + setOutputValue(v, outputValue); + return Math.abs(getCurrentValue(v).doubleValue() - outputValue); + } } - diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/WeightedNIPaths.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/WeightedNIPaths.java index 4ec4d19e..3d0e5a50 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/WeightedNIPaths.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/WeightedNIPaths.java @@ -1,14 +1,17 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.scoring; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.graph.MutableNetwork; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -18,22 +21,21 @@ import java.util.Map; import java.util.Set; -import com.google.common.base.Preconditions; -import com.google.common.base.Supplier; -import com.google.common.graph.MutableNetwork; - /** - * This algorithm measures the importance of nodes based upon both the number and length of disjoint paths that lead - * to a given node from each of the nodes in the root set. Specifically the formula for measuring the importance of a - * node is given by: I(t|R) = sum_i=1_|P(r,t)|_{alpha^|p_i|} where alpha is the path decay coefficient, p_i is path i - * and P(r,t) is a set of maximum-sized node-disjoint paths from r to t. + * This algorithm measures the importance of nodes based upon both the number and length of disjoint + * paths that lead to a given node from each of the nodes in the root set. Specifically the formula + * for measuring the importance of a node is given by: I(t|R) = sum_i=1_|P(r,t)|_{alpha^|p_i|} where + * alpha is the path decay coefficient, p_i is path i and P(r,t) is a set of maximum-sized + * node-disjoint paths from r to t. + * + *

This algorithm uses heuristic breadth-first search to try and find the maximum-sized set of + * node-disjoint paths between two nodes. As such, it is not guaranteed to give exact answers. + * *

- * This algorithm uses heuristic breadth-first search to try and find the maximum-sized set of node-disjoint paths - * between two nodes. As such, it is not guaranteed to give exact answers. - *

- * + * * @author Scott White - * @see "Algorithms for Estimating Relative Importance in Graphs by Scott White and Padhraic Smyth, 2003" + * @see "Algorithms for Estimating Relative Importance in Graphs by Scott White and Padhraic Smyth, + * 2003" */ // TODO: versions for Graph/ValueGraph // TODO: extend AbstractIterativeScorer and provide for iterating one step (depth) at a time? @@ -41,158 +43,162 @@ // TODO: this takes in a MutableNetwork and factories as a hack; there's got to be a better way; options include: // (1) create a delegate class that pretends that the extra node/edge are there // (2) refactor the internal logic so that we can emulate the presence of that node/edge -public class WeightedNIPaths implements VertexScorer { - private final MutableNetwork graph; - private final double alpha; - private final int maxDepth; - private final Set priors; - private final Map pathIndices = new HashMap(); - private final Map roots = new HashMap(); - private final Map> pathsSeenMap = new HashMap>(); - private final Map nodeScores = new LinkedHashMap<>(); - private final Supplier vertexFactory; - private final Supplier edgeFactory; - - - /** - * Constructs and initializes the algorithm. - * @param graph the graph whose nodes are being measured for their importance - * @param vertexFactory used to generate instances of V - * @param edgeFactory used to generate instances of E - * @param alpha the path decay coefficient (≥1); 2 is recommended - * @param maxDepth the maximal depth to search out from the root set - * @param priors the root set (starting vertices) - */ - public WeightedNIPaths(MutableNetwork graph, Supplier vertexFactory, - Supplier edgeFactory, double alpha, int maxDepth, Set priors) { - // TODO: is this actually restricted to only work on directed graphs? - Preconditions.checkArgument(graph.isDirected(), "Input graph must be directed"); - this.graph = graph; - this.vertexFactory = vertexFactory; - this.edgeFactory = edgeFactory; - this.alpha = alpha; - this.maxDepth = maxDepth; - this.priors = priors; - evaluate(); - } - - protected void incrementRankScore(N node, double rankValue) { - nodeScores.computeIfPresent(node, (v, value) -> value + rankValue); +public class WeightedNIPaths implements VertexScorer { + private final MutableNetwork graph; + private final double alpha; + private final int maxDepth; + private final Set priors; + private final Map pathIndices = new HashMap(); + private final Map roots = new HashMap(); + private final Map> pathsSeenMap = new HashMap>(); + private final Map nodeScores = new LinkedHashMap<>(); + private final Supplier vertexFactory; + private final Supplier edgeFactory; + + /** + * Constructs and initializes the algorithm. + * + * @param graph the graph whose nodes are being measured for their importance + * @param vertexFactory used to generate instances of V + * @param edgeFactory used to generate instances of E + * @param alpha the path decay coefficient (≥1); 2 is recommended + * @param maxDepth the maximal depth to search out from the root set + * @param priors the root set (starting vertices) + */ + public WeightedNIPaths( + MutableNetwork graph, + Supplier vertexFactory, + Supplier edgeFactory, + double alpha, + int maxDepth, + Set priors) { + // TODO: is this actually restricted to only work on directed graphs? + Preconditions.checkArgument(graph.isDirected(), "Input graph must be directed"); + this.graph = graph; + this.vertexFactory = vertexFactory; + this.edgeFactory = edgeFactory; + this.alpha = alpha; + this.maxDepth = maxDepth; + this.priors = priors; + evaluate(); + } + + protected void incrementRankScore(N node, double rankValue) { + nodeScores.computeIfPresent(node, (v, value) -> value + rankValue); + } + + protected void computeWeightedPathsFromSource(N root, int depth) { + + int pathIdx = 1; + + for (E e : graph.outEdges(root)) { + this.pathIndices.put(e, pathIdx); + this.roots.put(e, root); + newVertexEncountered(pathIdx, graph.incidentNodes(e).target(), root); + pathIdx++; } - protected void computeWeightedPathsFromSource(N root, int depth) { + List edges = new ArrayList(); + + N virtualNode = vertexFactory.get(); + graph.addNode(virtualNode); + E virtualSinkEdge = edgeFactory.get(); + + graph.addEdge(virtualNode, root, virtualSinkEdge); + edges.add(virtualSinkEdge); + + int currentDepth = 0; + while (currentDepth <= depth) { + double currentWeight = Math.pow(alpha, -1.0 * currentDepth); + for (E currentEdge : edges) { + incrementRankScore(graph.incidentNodes(currentEdge).target(), currentWeight); + } + + if ((currentDepth == depth) || (edges.size() == 0)) break; + + List newEdges = new ArrayList(); + + for (E currentSourceEdge : edges) { + Number sourcePathIndex = this.pathIndices.get(currentSourceEdge); + + // from the currentSourceEdge, get its opposite end + // then iterate over the out edges of that opposite end + N newDestVertex = graph.incidentNodes(currentSourceEdge).target(); + for (E currentDestEdge : graph.outEdges(newDestVertex)) { + N destEdgeRoot = this.roots.get(currentDestEdge); + N destEdgeDest = graph.incidentNodes(currentDestEdge).target(); + + if (currentSourceEdge == virtualSinkEdge) { + newEdges.add(currentDestEdge); + continue; + } + if (destEdgeRoot == root) { + continue; + } + if (destEdgeDest == graph.incidentNodes(currentSourceEdge).source()) { + continue; + } + Set pathsSeen = this.pathsSeenMap.get(destEdgeDest); + + if (pathsSeen == null) { + newVertexEncountered(sourcePathIndex.intValue(), destEdgeDest, root); + } else if (roots.get(destEdgeDest) != root) { + roots.put(destEdgeDest, root); + pathsSeen.clear(); + pathsSeen.add(sourcePathIndex); + } else if (!pathsSeen.contains(sourcePathIndex)) { + pathsSeen.add(sourcePathIndex); + } else { + continue; + } + + this.pathIndices.put(currentDestEdge, sourcePathIndex); + this.roots.put(currentDestEdge, root); + newEdges.add(currentDestEdge); + } + } - int pathIdx = 1; + edges = newEdges; + currentDepth++; + } - for (E e : graph.outEdges(root)) { - this.pathIndices.put(e, pathIdx); - this.roots.put(e, root); - newVertexEncountered(pathIdx, graph.incidentNodes(e).target(), root); - pathIdx++; - } + graph.removeNode(virtualNode); + } - List edges = new ArrayList(); - - N virtualNode = vertexFactory.get(); - graph.addNode(virtualNode); - E virtualSinkEdge = edgeFactory.get(); - - graph.addEdge(virtualNode, root, virtualSinkEdge); - edges.add(virtualSinkEdge); - - int currentDepth = 0; - while (currentDepth <= depth) { - double currentWeight = Math.pow(alpha, -1.0 * currentDepth); - for (E currentEdge : edges) { - incrementRankScore(graph.incidentNodes(currentEdge).target(), - currentWeight); - } - - if ((currentDepth == depth) || (edges.size() == 0)) break; - - List newEdges = new ArrayList(); - - for (E currentSourceEdge : edges) { - Number sourcePathIndex = this.pathIndices.get(currentSourceEdge); - - // from the currentSourceEdge, get its opposite end - // then iterate over the out edges of that opposite end - N newDestVertex = graph.incidentNodes(currentSourceEdge).target(); - for (E currentDestEdge : graph.outEdges(newDestVertex)) { - N destEdgeRoot = this.roots.get(currentDestEdge); - N destEdgeDest = graph.incidentNodes(currentDestEdge).target(); - - if (currentSourceEdge == virtualSinkEdge) { - newEdges.add(currentDestEdge); - continue; - } - if (destEdgeRoot == root) { - continue; - } - if (destEdgeDest == graph.incidentNodes(currentSourceEdge).source()) { - continue; - } - Set pathsSeen = this.pathsSeenMap.get(destEdgeDest); - - if (pathsSeen == null) { - newVertexEncountered(sourcePathIndex.intValue(), destEdgeDest, root); - } else if (roots.get(destEdgeDest) != root) { - roots.put(destEdgeDest,root); - pathsSeen.clear(); - pathsSeen.add(sourcePathIndex); - } else if (!pathsSeen.contains(sourcePathIndex)) { - pathsSeen.add(sourcePathIndex); - } else { - continue; - } - - this.pathIndices.put(currentDestEdge, sourcePathIndex); - this.roots.put(currentDestEdge, root); - newEdges.add(currentDestEdge); - } - } - - edges = newEdges; - currentDepth++; - } + private void newVertexEncountered(int sourcePathIndex, N dest, N root) { + Set pathsSeen = new HashSet(); + pathsSeen.add(sourcePathIndex); + this.pathsSeenMap.put(dest, pathsSeen); + roots.put(dest, root); + } - graph.removeNode(virtualNode); + private void evaluate() { + for (N node : graph.nodes()) { + nodeScores.put(node, 0.0); } - private void newVertexEncountered(int sourcePathIndex, N dest, N root) { - Set pathsSeen = new HashSet(); - pathsSeen.add(sourcePathIndex); - this.pathsSeenMap.put(dest, pathsSeen); - roots.put(dest, root); + for (N v : priors) { + computeWeightedPathsFromSource(v, maxDepth); } - private void evaluate() { - for (N node : graph.nodes()) { - nodeScores.put(node, 0.0); - } + double runningTotal = 0.0; + for (N node : graph.nodes()) { + runningTotal += nodeScores.get(node); + } - for (N v : priors) { - computeWeightedPathsFromSource(v, maxDepth); - } - - double runningTotal = 0.0; - for (N node : graph.nodes()) { - runningTotal += nodeScores.get(node); - } - - final double total = runningTotal; - for (N node : graph.nodes()) { - nodeScores.computeIfPresent(node, (n, value) -> value / total); - } + final double total = runningTotal; + for (N node : graph.nodes()) { + nodeScores.computeIfPresent(node, (n, value) -> value / total); } + } - @Override - public Double getVertexScore(N v) { - return nodeScores.get(v); - } + @Override + public Double getVertexScore(N v) { + return nodeScores.get(v); + } - @Override - public Map vertexScores() { - return Collections.unmodifiableMap(nodeScores); - } + @Override + public Map vertexScores() { + return Collections.unmodifiableMap(nodeScores); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/DelegateToEdgeTransformer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/DelegateToEdgeTransformer.java index b31db4c6..85ad2c65 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/DelegateToEdgeTransformer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/DelegateToEdgeTransformer.java @@ -1,7 +1,7 @@ /* * Created on Jul 11, 2008 * - * Copyright (c) 2008, The JUNG Authors + * Copyright (c) 2008, The JUNG Authors * * All rights reserved. * @@ -14,35 +14,26 @@ import com.google.common.base.Function; /** - * A {@code Transformer}. Mainly useful for technical reasons inside - * AbstractIterativeScorer; in essence it allows the edge weight instance - * variable to be of type VEPair,W even if the edge weight - * Transformer only operates on edges. + * A {@code Transformer}. Mainly useful for technical reasons inside AbstractIterativeScorer; in + * essence it allows the edge weight instance variable to be of type VEPair,W even if + * the edge weight Transformer only operates on edges. */ -public class DelegateToEdgeTransformer implements - Function,Number> -{ - /** - * The Function to which this instance delegates its function. - */ - protected Function delegate; - - /** - * Creates an instance with the specified delegate Function. - * @param delegate the Function to which this instance will delegate - */ - public DelegateToEdgeTransformer(Function delegate) - { - this.delegate = delegate; - } - - /** - * @see Function#apply(Object) - */ - public Number apply(VEPair arg0) - { - return delegate.apply(arg0.getE()); - } +public class DelegateToEdgeTransformer implements Function, Number> { + /** The Function to which this instance delegates its function. */ + protected Function delegate; + /** + * Creates an instance with the specified delegate Function. + * + * @param delegate the Function to which this instance will delegate + */ + public DelegateToEdgeTransformer(Function delegate) { + this.delegate = delegate; + } + + /** @see Function#apply(Object) */ + public Number apply(VEPair arg0) { + return delegate.apply(arg0.getE()); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/ScoringUtils.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/ScoringUtils.java index ed8c33e1..286ca671 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/ScoringUtils.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/ScoringUtils.java @@ -1,7 +1,7 @@ /* * Created on Jul 12, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -11,62 +11,54 @@ */ package edu.uci.ics.jung.algorithms.scoring.util; -import java.util.Collection; - import com.google.common.base.Function; - import edu.uci.ics.jung.algorithms.scoring.HITS; +import java.util.Collection; /** - * Methods for assigning values (to be interpreted as prior probabilities) to vertices in the context - * of random-walk-based scoring algorithms. + * Methods for assigning values (to be interpreted as prior probabilities) to vertices in the + * context of random-walk-based scoring algorithms. */ -public class ScoringUtils -{ - /** - * Assigns a probability of 1/roots.size() to each of the elements of roots. - * @param the vertex type - * @param roots the vertices to be assigned nonzero prior probabilities - * @return a Function assigning a uniform prior to each element in {@code roots} - */ - public static Function getUniformRootPrior(Collection roots) - { - final Collection inner_roots = roots; - Function distribution = new Function() - { - public Double apply(V input) - { - if (inner_roots.contains(input)) - return new Double(1.0 / inner_roots.size()); - else - return 0.0; - } +public class ScoringUtils { + /** + * Assigns a probability of 1/roots.size() to each of the elements of roots + * . + * + * @param the vertex type + * @param roots the vertices to be assigned nonzero prior probabilities + * @return a Function assigning a uniform prior to each element in {@code roots} + */ + public static Function getUniformRootPrior(Collection roots) { + final Collection inner_roots = roots; + Function distribution = + new Function() { + public Double apply(V input) { + if (inner_roots.contains(input)) return new Double(1.0 / inner_roots.size()); + else return 0.0; + } }; - - return distribution; - } - - /** - * Returns a Function that hub and authority values of 1/roots.size() to each - * element of roots. - * @param the vertex type - * @param roots the vertices to be assigned nonzero scores - * @return a Function that assigns uniform prior hub/authority probabilities to each root - */ - public static Function getHITSUniformRootPrior(Collection roots) - { - final Collection inner_roots = roots; - Function distribution = - new Function() - { - public HITS.Scores apply(V input) - { - if (inner_roots.contains(input)) - return new HITS.Scores(1.0 / inner_roots.size(), 1.0 / inner_roots.size()); - else - return new HITS.Scores(0.0, 0.0); - } + + return distribution; + } + + /** + * Returns a Function that hub and authority values of 1/roots.size() to each element + * of roots. + * + * @param the vertex type + * @param roots the vertices to be assigned nonzero scores + * @return a Function that assigns uniform prior hub/authority probabilities to each root + */ + public static Function getHITSUniformRootPrior(Collection roots) { + final Collection inner_roots = roots; + Function distribution = + new Function() { + public HITS.Scores apply(V input) { + if (inner_roots.contains(input)) + return new HITS.Scores(1.0 / inner_roots.size(), 1.0 / inner_roots.size()); + else return new HITS.Scores(0.0, 0.0); + } }; - return distribution; - } + return distribution; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/UniformDegreeWeight.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/UniformDegreeWeight.java index a31f28fd..bef8912c 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/UniformDegreeWeight.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/UniformDegreeWeight.java @@ -1,13 +1,10 @@ /** - * Copyright (c) 2008, The JUNG Authors + * Copyright (c) 2008, The JUNG Authors * - * All rights reserved. + *

All rights reserved. * - * This software is open-source under the BSD license; see either - * "license.txt" or - * https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * Created on Jul 14, 2008 - * + *

This software is open-source under the BSD license; see either "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. Created on Jul 14, 2008 */ package edu.uci.ics.jung.algorithms.scoring.util; @@ -15,33 +12,25 @@ import com.google.common.graph.Network; /** - * An edge weight function that assigns weights as uniform - * transition probabilities: + * An edge weight function that assigns weights as uniform transition probabilities: + * *

    - *
  • for undirected edges, returns 1/degree(v) (where 'v' is the - * vertex in the VEPair) - *
  • for directed edges, returns 1/outdegree(source(e)) (where 'e' - * is the edge in the VEPair) + *
  • for undirected edges, returns 1/degree(v) (where 'v' is the vertex in the VEPair) + *
  • for directed edges, returns 1/outdegree(source(e)) (where 'e' is the edge in the VEPair) */ -public class UniformDegreeWeight implements - Function, Double> -{ - private Network graph; - - /** - * @param graph the graph for which an instance is being created - */ - public UniformDegreeWeight(Network graph) - { - this.graph = graph; - } +public class UniformDegreeWeight implements Function, Double> { + private Network graph; + + /** @param graph the graph for which an instance is being created */ + public UniformDegreeWeight(Network graph) { + this.graph = graph; + } - public Double apply(VEPair ve_pair) - { - E e = ve_pair.getE(); - V v = ve_pair.getV(); - return graph.isDirected() - ? 1.0 / graph.outDegree(graph.incidentNodes(e).source()) - : 1.0 / graph.degree(v); - } + public Double apply(VEPair ve_pair) { + E e = ve_pair.getE(); + V v = ve_pair.getV(); + return graph.isDirected() + ? 1.0 / graph.outDegree(graph.incidentNodes(e).source()) + : 1.0 / graph.degree(v); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/VEPair.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/VEPair.java index b1f2ee26..2e9a6bbb 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/VEPair.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/VEPair.java @@ -1,7 +1,7 @@ /* * Created on Jul 8, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -12,45 +12,37 @@ package edu.uci.ics.jung.algorithms.scoring.util; /** - * Convenience class for associating a vertex and an edge. Used, for example, - * in contexts in which it is necessary to know the origin for an edge traversal - * (that is, the direction in which an (undirected) edge is being traversed). + * Convenience class for associating a vertex and an edge. Used, for example, in contexts in which + * it is necessary to know the origin for an edge traversal (that is, the direction in which an + * (undirected) edge is being traversed). * * @param the vertex type * @param the edge type */ -public class VEPair -{ - private V v; - private E e; - - /** - * Creates an instance with the specified vertex and edge - * @param v the vertex to add - * @param e the edge to add - */ - public VEPair(V v, E e) - { - if (v == null || e == null) - throw new IllegalArgumentException("elements must be non-null"); - - this.v = v; - this.e = e; - } - - /** - * @return the vertex of this pair - */ - public V getV() - { - return v; - } - - /** - * @return the edge of this pair - */ - public E getE() - { - return e; - } +public class VEPair { + private V v; + private E e; + + /** + * Creates an instance with the specified vertex and edge + * + * @param v the vertex to add + * @param e the edge to add + */ + public VEPair(V v, E e) { + if (v == null || e == null) throw new IllegalArgumentException("elements must be non-null"); + + this.v = v; + this.e = e; + } + + /** @return the vertex of this pair */ + public V getV() { + return v; + } + + /** @return the edge of this pair */ + public E getE() { + return e; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/VertexScoreTransformer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/VertexScoreTransformer.java index c6bc9952..182be29e 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/VertexScoreTransformer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/scoring/util/VertexScoreTransformer.java @@ -1,7 +1,7 @@ /* * Created on Jul 18, 2008 * - * Copyright (c) 2008, The JUNG Authors + * Copyright (c) 2008, The JUNG Authors * * All rights reserved. * @@ -12,35 +12,27 @@ package edu.uci.ics.jung.algorithms.scoring.util; import com.google.common.base.Function; - import edu.uci.ics.jung.algorithms.scoring.VertexScorer; -/** - * A Function convenience wrapper around VertexScorer. - */ -public class VertexScoreTransformer implements Function -{ - /** - * The VertexScorer instance that provides the values returned by transform. - */ - protected VertexScorer vs; - - /** - * Creates an instance based on the specified VertexScorer. - * @param vs the VertexScorer which will retrieve the score for each vertex - */ - public VertexScoreTransformer(VertexScorer vs) - { - this.vs = vs; - } +/** A Function convenience wrapper around VertexScorer. */ +public class VertexScoreTransformer implements Function { + /** The VertexScorer instance that provides the values returned by transform. */ + protected VertexScorer vs; - /** - * @param v the vertex whose score is being returned - * @return the score for this vertex. - */ - public S apply(V v) - { - return vs.getVertexScore(v); - } + /** + * Creates an instance based on the specified VertexScorer. + * + * @param vs the VertexScorer which will retrieve the score for each vertex + */ + public VertexScoreTransformer(VertexScorer vs) { + this.vs = vs; + } + /** + * @param v the vertex whose score is being returned + * @return the score for this vertex. + */ + public S apply(V v) { + return vs.getVertexScore(v); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/BFSDistanceLabeler.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/BFSDistanceLabeler.java index e2bbd35b..6acf20b5 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/BFSDistanceLabeler.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/BFSDistanceLabeler.java @@ -1,15 +1,16 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.shortestpath; - +import com.google.common.base.Preconditions; +import com.google.common.graph.Graph; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -18,157 +19,163 @@ import java.util.Map; import java.util.Set; -import com.google.common.base.Preconditions; -import com.google.common.graph.Graph; - /** - * Labels each node in the graph according to the BFS distance from the start node(s). If nodes are unreachable, then - * they are assigned a distance of -1. - * All nodes traversed at step k are marked as predecessors of their successors traversed at step k+1. - *

    - * Running time is: O(m) + * Labels each node in the graph according to the BFS distance from the start node(s). If nodes are + * unreachable, then they are assigned a distance of -1. All nodes traversed at step k are marked as + * predecessors of their successors traversed at step k+1. + * + *

    Running time is: O(m) + * * @author Scott White */ // TODO: update or replace public class BFSDistanceLabeler { - private Map distanceDecorator = new HashMap(); - private List mCurrentList; - private Set mUnvisitedVertices; - private List mVerticesInOrderVisited; - private Map> mPredecessorMap; - - /** - * Creates a new BFS labeler for the specified graph and root set - * The distances are stored in the corresponding Vertex objects and are of type MutableInteger - */ - public BFSDistanceLabeler() { - mPredecessorMap = new HashMap>(); - } - - /** - * Returns the list of vertices visited in order of traversal - * @return the list of vertices - */ - public List getVerticesInOrderVisited() { - return mVerticesInOrderVisited; + private Map distanceDecorator = new HashMap(); + private List mCurrentList; + private Set mUnvisitedVertices; + private List mVerticesInOrderVisited; + private Map> mPredecessorMap; + + /** + * Creates a new BFS labeler for the specified graph and root set The distances are stored in the + * corresponding Vertex objects and are of type MutableInteger + */ + public BFSDistanceLabeler() { + mPredecessorMap = new HashMap>(); + } + + /** + * Returns the list of vertices visited in order of traversal + * + * @return the list of vertices + */ + public List getVerticesInOrderVisited() { + return mVerticesInOrderVisited; + } + + /** + * Returns the set of all vertices that were not visited + * + * @return the list of unvisited vertices + */ + public Set getUnvisitedVertices() { + return mUnvisitedVertices; + } + + /** + * Given a vertex, returns the shortest distance from any node in the root set to v + * + * @param g the graph in which the distances are to be measured + * @param v the vertex whose distance is to be retrieved + * @return the shortest distance from any node in the root set to v + */ + public int getDistance(Graph g, N v) { + Preconditions.checkArgument( + g.nodes().contains(v), "Vertex %s is not contained in the graph %s", v, g); + if (!g.nodes().contains(v)) { + throw new IllegalArgumentException("Vertex is not contained in the graph."); } - /** - * Returns the set of all vertices that were not visited - * @return the list of unvisited vertices - */ - public Set getUnvisitedVertices() { - return mUnvisitedVertices; + return distanceDecorator.get(v); + } + + /** + * Returns set of predecessors of the given vertex + * + * @param v the vertex whose predecessors are to be retrieved + * @return the set of predecessors + */ + public Set getPredecessors(N v) { + return mPredecessorMap.get(v); + } + + protected void initialize(Graph g, Set rootSet) { + mVerticesInOrderVisited = new ArrayList(); + mUnvisitedVertices = new HashSet(); + for (N currentVertex : g.nodes()) { + mUnvisitedVertices.add(currentVertex); + mPredecessorMap.put(currentVertex, new HashSet()); } - /** - * Given a vertex, returns the shortest distance from any node in the root set to v - * @param g the graph in which the distances are to be measured - * @param v the vertex whose distance is to be retrieved - * @return the shortest distance from any node in the root set to v - */ - public int getDistance(Graph g, N v) { - Preconditions.checkArgument(g.nodes().contains(v), - "Vertex %s is not contained in the graph %s", v, g); - if (!g.nodes().contains(v)) { - throw new IllegalArgumentException("Vertex is not contained in the graph."); - } - - return distanceDecorator.get(v); - } - - /** - * Returns set of predecessors of the given vertex - * @param v the vertex whose predecessors are to be retrieved - * @return the set of predecessors - */ - public Set getPredecessors(N v) { - return mPredecessorMap.get(v); + mCurrentList = new ArrayList(); + for (N v : rootSet) { + distanceDecorator.put(v, new Integer(0)); + mCurrentList.add(v); + mUnvisitedVertices.remove(v); + mVerticesInOrderVisited.add(v); } - - protected void initialize(Graph g, Set rootSet) { - mVerticesInOrderVisited = new ArrayList(); - mUnvisitedVertices = new HashSet(); - for(N currentVertex : g.nodes()) { - mUnvisitedVertices.add(currentVertex); - mPredecessorMap.put(currentVertex,new HashSet()); - } - - mCurrentList = new ArrayList(); - for(N v : rootSet) { - distanceDecorator.put(v, new Integer(0)); - mCurrentList.add(v); - mUnvisitedVertices.remove(v); - mVerticesInOrderVisited.add(v); - } - } - - private void addPredecessor(N predecessor,N successor) { - HashSet predecessors = mPredecessorMap.get(successor); - predecessors.add(predecessor); - } - - /** - * Computes the distances of all the node from the starting root nodes. If there is more than one root node - * the minimum distance from each root node is used as the designated distance to a given node. Also keeps track - * of the predecessors of each node traversed as well as the order of nodes traversed. - * @param graph the graph to label - * @param rootSet the set of starting vertices to traverse from - */ - public void labelDistances(Graph graph, Set rootSet) { - - initialize(graph,rootSet); - - int distance = 1; - while (true) { - List newList = new ArrayList(); - for(N currentVertex : mCurrentList) { - if(graph.nodes().contains(currentVertex)) { - for(N next : graph.successors(currentVertex)) { - visitNewVertex(currentVertex,next, distance, newList); - } - } - } - if (newList.size() == 0) break; - mCurrentList = newList; - distance++; - } - - for(N v : mUnvisitedVertices) { - distanceDecorator.put(v,new Integer(-1)); + } + + private void addPredecessor(N predecessor, N successor) { + HashSet predecessors = mPredecessorMap.get(successor); + predecessors.add(predecessor); + } + + /** + * Computes the distances of all the node from the starting root nodes. If there is more than one + * root node the minimum distance from each root node is used as the designated distance to a + * given node. Also keeps track of the predecessors of each node traversed as well as the order of + * nodes traversed. + * + * @param graph the graph to label + * @param rootSet the set of starting vertices to traverse from + */ + public void labelDistances(Graph graph, Set rootSet) { + + initialize(graph, rootSet); + + int distance = 1; + while (true) { + List newList = new ArrayList(); + for (N currentVertex : mCurrentList) { + if (graph.nodes().contains(currentVertex)) { + for (N next : graph.successors(currentVertex)) { + visitNewVertex(currentVertex, next, distance, newList); + } } + } + if (newList.size() == 0) break; + mCurrentList = newList; + distance++; } - /** - * Computes the distances of all the node from the specified root node. Also keeps track - * of the predecessors of each node traversed as well as the order of nodes traversed. - * @param graph the graph to label - * @param root the single starting vertex to traverse from - */ - public void labelDistances(Graph graph, N root) { - labelDistances(graph, Collections.singleton(root)); + for (N v : mUnvisitedVertices) { + distanceDecorator.put(v, new Integer(-1)); } - - private void visitNewVertex(N predecessor, N neighbor, int distance, List newList) { - if (mUnvisitedVertices.contains(neighbor)) { - distanceDecorator.put(neighbor, new Integer(distance)); - newList.add(neighbor); - mVerticesInOrderVisited.add(neighbor); - mUnvisitedVertices.remove(neighbor); - } - int predecessorDistance = distanceDecorator.get(predecessor).intValue(); - int successorDistance = distanceDecorator.get(neighbor).intValue(); - if (predecessorDistance < successorDistance) { - addPredecessor(predecessor,neighbor); - } + } + + /** + * Computes the distances of all the node from the specified root node. Also keeps track of the + * predecessors of each node traversed as well as the order of nodes traversed. + * + * @param graph the graph to label + * @param root the single starting vertex to traverse from + */ + public void labelDistances(Graph graph, N root) { + labelDistances(graph, Collections.singleton(root)); + } + + private void visitNewVertex(N predecessor, N neighbor, int distance, List newList) { + if (mUnvisitedVertices.contains(neighbor)) { + distanceDecorator.put(neighbor, new Integer(distance)); + newList.add(neighbor); + mVerticesInOrderVisited.add(neighbor); + mUnvisitedVertices.remove(neighbor); } - - /** - * Must be called after {@code labelDistances} in order to contain valid data. - * @return a map from vertices to minimum distances from the original source(s) - */ - public Map getDistanceDecorator() { - return distanceDecorator; + int predecessorDistance = distanceDecorator.get(predecessor).intValue(); + int successorDistance = distanceDecorator.get(neighbor).intValue(); + if (predecessorDistance < successorDistance) { + addPredecessor(predecessor, neighbor); } + } + + /** + * Must be called after {@code labelDistances} in order to contain valid data. + * + * @return a map from vertices to minimum distances from the original source(s) + */ + public Map getDistanceDecorator() { + return distanceDecorator; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraDistance.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraDistance.java index 3ea0a1e1..3e9dd81e 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraDistance.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraDistance.java @@ -1,7 +1,7 @@ /* * Created on Jul 9, 2005 * - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,6 +11,12 @@ */ package edu.uci.ics.jung.algorithms.shortestpath; +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.base.Preconditions; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.util.BasicMapEntry; +import edu.uci.ics.jung.algorithms.util.MapBinaryHeap; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; @@ -19,507 +25,446 @@ import java.util.Map; import java.util.Set; -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.base.Preconditions; -import com.google.common.graph.Network; - -import edu.uci.ics.jung.algorithms.util.BasicMapEntry; -import edu.uci.ics.jung.algorithms.util.MapBinaryHeap; - /** - *

    Calculates distances in a specified graph, using - * Dijkstra's single-source-shortest-path algorithm. All edge weights - * in the graph must be nonnegative; if any edge with negative weight is - * found in the course of calculating distances, an - * IllegalArgumentException will be thrown. - * (Note: this exception will only be thrown when such an edge would be - * used to update a given tentative distance; - * the algorithm does not check for negative-weight edges "up front".) - * - *

    Distances and partial results are optionally cached (by this instance) - * for later reference. Thus, if the 10 closest vertices to a specified source - * vertex are known, calculating the 20 closest vertices does not require - * starting Dijkstra's algorithm over from scratch. - * - *

    Distances are stored as double-precision values. - * If a vertex is not reachable from the specified source vertex, no - * distance is stored. This is new behavior with version 1.4; - * the previous behavior was to store a value of - * Double.POSITIVE_INFINITY. This change gives the algorithm - * an approximate complexity of O(kD log k), where k is either the number of - * requested targets or the number of reachable vertices (whichever is smaller), - * and D is the average degree of a vertex. - * - *

    The elements in the maps returned by getDistanceMap - * are ordered (that is, returned - * by the iterator) by nondecreasing distance from source. - * - *

    Users are cautioned that distances calculated should be assumed to - * be invalidated by changes to the graph, and should invoke reset() - * when appropriate so that the distances can be recalculated. - * + * Calculates distances in a specified graph, using Dijkstra's single-source-shortest-path + * algorithm. All edge weights in the graph must be nonnegative; if any edge with negative weight is + * found in the course of calculating distances, an IllegalArgumentException will be + * thrown. (Note: this exception will only be thrown when such an edge would be used to update a + * given tentative distance; the algorithm does not check for negative-weight edges "up front".) + * + *

    Distances and partial results are optionally cached (by this instance) for later reference. + * Thus, if the 10 closest vertices to a specified source vertex are known, calculating the 20 + * closest vertices does not require starting Dijkstra's algorithm over from scratch. + * + *

    Distances are stored as double-precision values. If a vertex is not reachable from the + * specified source vertex, no distance is stored. This is new behavior with version 1.4; the + * previous behavior was to store a value of Double.POSITIVE_INFINITY. This change + * gives the algorithm an approximate complexity of O(kD log k), where k is either the number of + * requested targets or the number of reachable vertices (whichever is smaller), and D is the + * average degree of a vertex. + * + *

    The elements in the maps returned by getDistanceMap are ordered (that is, + * returned by the iterator) by nondecreasing distance from source. + * + *

    Users are cautioned that distances calculated should be assumed to be invalidated by changes + * to the graph, and should invoke reset() when appropriate so that the distances can + * be recalculated. + * * @author Joshua O'Madadhain * @author Tom Nelson converted to jung2 */ -public class DijkstraDistance implements Distance -{ - protected Network g; - protected Function nev; - protected Map sourceMap; // a map of source vertices to an instance of SourceData - protected boolean cached; - protected double max_distance; - protected int max_targets; - - /** - *

    Creates an instance of DijkstraShortestPath for - * the specified graph and the specified method of extracting weights - * from edges, which caches results locally if and only if - * cached is true. - * - * @param g the graph on which distances will be calculated - * @param nev the class responsible for returning weights for edges - * @param cached specifies whether the results are to be cached - */ - public DijkstraDistance(Network g, Function nev, boolean cached) { - this.g = g; - this.nev = nev; - this.sourceMap = new HashMap(); - this.cached = cached; - this.max_distance = Double.POSITIVE_INFINITY; - this.max_targets = Integer.MAX_VALUE; - } - - /** - *

    Creates an instance of DijkstraShortestPath for - * the specified graph and the specified method of extracting weights - * from edges, which caches results locally. - * - * @param g the graph on which distances will be calculated - * @param nev the class responsible for returning weights for edges - */ - public DijkstraDistance(Network g, Function nev) { - this(g, nev, true); - } - - /** - *

    Creates an instance of DijkstraShortestPath for - * the specified unweighted graph (that is, all weights 1) which - * caches results locally. - * - * @param g the graph on which distances will be calculated - */ - public DijkstraDistance(Network g) { - this(g, Functions.constant(1), true); +public class DijkstraDistance implements Distance { + protected Network g; + protected Function nev; + protected Map sourceMap; // a map of source vertices to an instance of SourceData + protected boolean cached; + protected double max_distance; + protected int max_targets; + + /** + * Creates an instance of DijkstraShortestPath for the specified graph and the + * specified method of extracting weights from edges, which caches results locally if and only if + * cached is true. + * + * @param g the graph on which distances will be calculated + * @param nev the class responsible for returning weights for edges + * @param cached specifies whether the results are to be cached + */ + public DijkstraDistance( + Network g, Function nev, boolean cached) { + this.g = g; + this.nev = nev; + this.sourceMap = new HashMap(); + this.cached = cached; + this.max_distance = Double.POSITIVE_INFINITY; + this.max_targets = Integer.MAX_VALUE; + } + + /** + * Creates an instance of DijkstraShortestPath for the specified graph and the + * specified method of extracting weights from edges, which caches results locally. + * + * @param g the graph on which distances will be calculated + * @param nev the class responsible for returning weights for edges + */ + public DijkstraDistance(Network g, Function nev) { + this(g, nev, true); + } + + /** + * Creates an instance of DijkstraShortestPath for the specified unweighted graph + * (that is, all weights 1) which caches results locally. + * + * @param g the graph on which distances will be calculated + */ + public DijkstraDistance(Network g) { + this(g, Functions.constant(1), true); + } + + /** + * Creates an instance of DijkstraShortestPath for the specified unweighted graph + * (that is, all weights 1) which caches results locally. + * + * @param g the graph on which distances will be calculated + * @param cached specifies whether the results are to be cached + */ + public DijkstraDistance(Network g, boolean cached) { + this(g, Functions.constant(1), cached); + } + + /** + * Implements Dijkstra's single-source shortest-path algorithm for weighted graphs. Uses a + * MapBinaryHeap as the priority queue, which gives this algorithm a time complexity of O(m + * lg n) (m = # of edges, n = # of vertices). This algorithm will terminate when any of the + * following have occurred (in order of priority): + * + *

      + *
    • the distance to the specified target (if any) has been found + *
    • no more vertices are reachable + *
    • the specified # of distances have been found, or the maximum distance desired has been + * exceeded + *
    • all distances have been found + *
    + * + * @param source the vertex from which distances are to be measured + * @param numDests the number of distances to measure + * @param targets the set of vertices to which distances are to be measured + * @return a mapping from vertex to the shortest distance from the source to each target + */ + protected LinkedHashMap singleSourceShortestPath( + V source, Collection targets, int numDests) { + SourceData sd = getSourceData(source); + + Set to_get = new HashSet(); + if (targets != null) { + to_get.addAll(targets); + Set existing_dists = sd.distances.keySet(); + for (V o : targets) { + if (existing_dists.contains(o)) to_get.remove(o); + } } - /** - *

    Creates an instance of DijkstraShortestPath for - * the specified unweighted graph (that is, all weights 1) which - * caches results locally. - * - * @param g the graph on which distances will be calculated - * @param cached specifies whether the results are to be cached - */ - public DijkstraDistance(Network g, boolean cached) { - this(g, Functions.constant(1), cached); + // if we've exceeded the max distance or max # of distances we're willing to calculate, or + // if we already have all the distances we need, + // terminate + if (sd.reached_max + || (targets != null && to_get.isEmpty()) + || (sd.distances.size() >= numDests)) { + return sd.distances; } - - /** - * Implements Dijkstra's single-source shortest-path algorithm for - * weighted graphs. Uses a MapBinaryHeap as the priority queue, - * which gives this algorithm a time complexity of O(m lg n) (m = # of edges, n = - * # of vertices). - * This algorithm will terminate when any of the following have occurred (in order - * of priority): - *

      - *
    • the distance to the specified target (if any) has been found - *
    • no more vertices are reachable - *
    • the specified # of distances have been found, or the maximum distance - * desired has been exceeded - *
    • all distances have been found - *
    - * - * @param source the vertex from which distances are to be measured - * @param numDests the number of distances to measure - * @param targets the set of vertices to which distances are to be measured - * @return a mapping from vertex to the shortest distance from the source to each target - */ - protected LinkedHashMap singleSourceShortestPath(V source, Collection targets, int numDests) - { - SourceData sd = getSourceData(source); - - Set to_get = new HashSet(); - if (targets != null) { - to_get.addAll(targets); - Set existing_dists = sd.distances.keySet(); - for(V o : targets) { - if (existing_dists.contains(o)) - to_get.remove(o); - } - } - - // if we've exceeded the max distance or max # of distances we're willing to calculate, or - // if we already have all the distances we need, - // terminate - if (sd.reached_max || - (targets != null && to_get.isEmpty()) || - (sd.distances.size() >= numDests)) - { - return sd.distances; - } - - while (!sd.unknownVertices.isEmpty() && (sd.distances.size() < numDests || !to_get.isEmpty())) - { - Map.Entry p = sd.getNextVertex(); - V v = p.getKey(); - double v_dist = p.getValue().doubleValue(); - to_get.remove(v); - if (v_dist > this.max_distance) - { - // we're done; put this vertex back in so that we're not including - // a distance beyond what we specified - sd.restoreVertex(v, v_dist); - sd.reached_max = true; - break; - } - sd.dist_reached = v_dist; - if (sd.distances.size() >= this.max_targets) - { - sd.reached_max = true; - break; - } - - for (V w : g.successors(v)) { - for (E e : g.edgesConnecting(v, w)) { - if (!sd.distances.containsKey(w)) - { - double edge_weight = nev.apply(e).doubleValue(); - Preconditions.checkArgument(edge_weight >= 0, - "encountered negative edge weight %s for edge %s", nev.apply(e), e); - double new_dist = v_dist + edge_weight; - if (!sd.estimatedDistances.containsKey(w)) - { - sd.createRecord(w, e, new_dist); - } - else - { - double w_dist = ((Double)sd.estimatedDistances.get(w)).doubleValue(); - if (new_dist < w_dist) // update tentative distance & path for w - sd.update(w, e, new_dist); - } - } - - } + while (!sd.unknownVertices.isEmpty() && (sd.distances.size() < numDests || !to_get.isEmpty())) { + Map.Entry p = sd.getNextVertex(); + V v = p.getKey(); + double v_dist = p.getValue().doubleValue(); + to_get.remove(v); + if (v_dist > this.max_distance) { + // we're done; put this vertex back in so that we're not including + // a distance beyond what we specified + sd.restoreVertex(v, v_dist); + sd.reached_max = true; + break; + } + sd.dist_reached = v_dist; + + if (sd.distances.size() >= this.max_targets) { + sd.reached_max = true; + break; + } + + for (V w : g.successors(v)) { + for (E e : g.edgesConnecting(v, w)) { + if (!sd.distances.containsKey(w)) { + double edge_weight = nev.apply(e).doubleValue(); + Preconditions.checkArgument( + edge_weight >= 0, + "encountered negative edge weight %s for edge %s", + nev.apply(e), + e); + double new_dist = v_dist + edge_weight; + if (!sd.estimatedDistances.containsKey(w)) { + sd.createRecord(w, e, new_dist); + } else { + double w_dist = ((Double) sd.estimatedDistances.get(w)).doubleValue(); + if (new_dist < w_dist) // update tentative distance & path for w + sd.update(w, e, new_dist); } + } } - return sd.distances; + } } + return sd.distances; + } - protected SourceData getSourceData(V source) - { - SourceData sd = sourceMap.get(source); - if (sd == null) - sd = new SourceData(source); - return sd; - } - - /** - * Returns the length of a shortest path from the source to the target vertex, - * or null if the target is not reachable from the source. - * If either vertex is not in the graph for which this instance - * was created, throws IllegalArgumentException. - * - * @param source the vertex from which the distance to {@code target} is to be measured - * @param target the vertex to which the distance from {@code source} is to be measured - * @return the distance between {@code source} and {@code target} - * - * @see #getDistanceMap(Object) - * @see #getDistanceMap(Object,int) - */ - public Number getDistance(V source, V target) - { - Preconditions.checkArgument( - g.nodes().contains(target), - "Specified target vertex %s is not part of graph %s", target, g); - Preconditions.checkArgument( - g.nodes().contains(source), - "Specified source vertex %s is not part of graph %s", source, g); - - Set targets = new HashSet(); - targets.add(target); - Map distanceMap = getDistanceMap(source, targets); - return distanceMap.get(target); - } - - - /** - * Returns a {@code Map} from each element {@code t} of {@code targets} to the - * shortest-path distance from {@code source} to {@code t}. - * @param source the vertex from which the distance to each target is to be measured - * @param targets the vertices to which the distance from the source is to be measured - * @return {@code Map} from each element of {@code targets} to its distance from {@code source} - */ - public Map getDistanceMap(V source, Collection targets) - { - Preconditions.checkArgument( - g.nodes().contains(source), - "Specified source vertex %s is not part of graph %s", source, g); - Preconditions.checkArgument( - targets.size() <= max_targets, - "size of target set %d exceeds maximum number of targets allowed: %d", - targets.size(), this.max_targets); - - Map distanceMap = - singleSourceShortestPath(source, targets, - Math.min(g.nodes().size(), max_targets)); - if (!cached) - reset(source); - - return distanceMap; - } - - /** - *

    Returns a LinkedHashMap which maps each vertex - * in the graph (including the source vertex) - * to its distance from the source vertex. - * The map's iterator will return the elements in order of - * increasing distance from source. - * - *

    The size of the map returned will be the number of - * vertices reachable from source. - * - * @see #getDistanceMap(Object,int) - * @see #getDistance(Object,Object) - * @param source the vertex from which distances are measured - * @return a mapping from each vertex in the graph to its distance from {@code source} - */ - public Map getDistanceMap(V source) - { - return getDistanceMap(source, Math.min(g.nodes().size(), max_targets)); - } - - - - /** - *

    Returns a LinkedHashMap which maps each of the closest - * numDist vertices to the source vertex - * in the graph (including the source vertex) - * to its distance from the source vertex. Throws - * an IllegalArgumentException if source - * is not in this instance's graph, or if numDests is - * either less than 1 or greater than the number of vertices in the - * graph. - * - *

    The size of the map returned will be the smaller of - * numDests and the number of vertices reachable from - * source. - * - * @see #getDistanceMap(Object) - * @see #getDistance(Object,Object) - * @param source the vertex from which distances are measured - * @param numDests the number of vertices for which to measure distances - * @return a mapping from the {@code numDests} vertices in the graph - * closest to {@code source}, to their distance from {@code source} - * - */ - public LinkedHashMap getDistanceMap(V source, int numDests) - { - Preconditions.checkArgument( - g.nodes().contains(source), - "Specified source vertex %s is not part of graph %s", source, g); - Preconditions.checkArgument( - numDests >= 1 && numDests <= g.nodes().size(), - "number of destinations must be in [1, %d]", g.nodes().size()); - - Preconditions.checkArgument( - numDests <= max_targets, - "size of target set %d exceeds maximum number of targets allowed: %d", - numDests, this.max_targets); - - LinkedHashMap distanceMap = - singleSourceShortestPath(source, null, numDests); - - if (!cached) - reset(source); - - return distanceMap; + protected SourceData getSourceData(V source) { + SourceData sd = sourceMap.get(source); + if (sd == null) sd = new SourceData(source); + return sd; + } + + /** + * Returns the length of a shortest path from the source to the target vertex, or null if the + * target is not reachable from the source. If either vertex is not in the graph for which this + * instance was created, throws IllegalArgumentException. + * + * @param source the vertex from which the distance to {@code target} is to be measured + * @param target the vertex to which the distance from {@code source} is to be measured + * @return the distance between {@code source} and {@code target} + * @see #getDistanceMap(Object) + * @see #getDistanceMap(Object,int) + */ + public Number getDistance(V source, V target) { + Preconditions.checkArgument( + g.nodes().contains(target), + "Specified target vertex %s is not part of graph %s", + target, + g); + Preconditions.checkArgument( + g.nodes().contains(source), + "Specified source vertex %s is not part of graph %s", + source, + g); + + Set targets = new HashSet(); + targets.add(target); + Map distanceMap = getDistanceMap(source, targets); + return distanceMap.get(target); + } + + /** + * Returns a {@code Map} from each element {@code t} of {@code targets} to the shortest-path + * distance from {@code source} to {@code t}. + * + * @param source the vertex from which the distance to each target is to be measured + * @param targets the vertices to which the distance from the source is to be measured + * @return {@code Map} from each element of {@code targets} to its distance from {@code source} + */ + public Map getDistanceMap(V source, Collection targets) { + Preconditions.checkArgument( + g.nodes().contains(source), + "Specified source vertex %s is not part of graph %s", + source, + g); + Preconditions.checkArgument( + targets.size() <= max_targets, + "size of target set %d exceeds maximum number of targets allowed: %d", + targets.size(), + this.max_targets); + + Map distanceMap = + singleSourceShortestPath(source, targets, Math.min(g.nodes().size(), max_targets)); + if (!cached) reset(source); + + return distanceMap; + } + + /** + * Returns a LinkedHashMap which maps each vertex in the graph (including the + * source vertex) to its distance from the source vertex. The map's iterator + * will return the elements in order of increasing distance from source. + * + *

    The size of the map returned will be the number of vertices reachable from source + * . + * + * @see #getDistanceMap(Object,int) + * @see #getDistance(Object,Object) + * @param source the vertex from which distances are measured + * @return a mapping from each vertex in the graph to its distance from {@code source} + */ + public Map getDistanceMap(V source) { + return getDistanceMap(source, Math.min(g.nodes().size(), max_targets)); + } + + /** + * Returns a LinkedHashMap which maps each of the closest numDist + * vertices to the source vertex in the graph (including the source + * vertex) to its distance from the source vertex. Throws an + * IllegalArgumentException if source is not in this instance's graph, or if + * numDests is either less than 1 or greater than the number of vertices in the + * graph. + * + *

    The size of the map returned will be the smaller of numDests and the number of + * vertices reachable from source. + * + * @see #getDistanceMap(Object) + * @see #getDistance(Object,Object) + * @param source the vertex from which distances are measured + * @param numDests the number of vertices for which to measure distances + * @return a mapping from the {@code numDests} vertices in the graph closest to {@code source}, to + * their distance from {@code source} + */ + public LinkedHashMap getDistanceMap(V source, int numDests) { + Preconditions.checkArgument( + g.nodes().contains(source), + "Specified source vertex %s is not part of graph %s", + source, + g); + Preconditions.checkArgument( + numDests >= 1 && numDests <= g.nodes().size(), + "number of destinations must be in [1, %d]", + g.nodes().size()); + + Preconditions.checkArgument( + numDests <= max_targets, + "size of target set %d exceeds maximum number of targets allowed: %d", + numDests, + this.max_targets); + + LinkedHashMap distanceMap = singleSourceShortestPath(source, null, numDests); + + if (!cached) reset(source); + + return distanceMap; + } + + /** + * Allows the user to specify the maximum distance that this instance will calculate. Any vertices + * past this distance will effectively be unreachable from the source, in the sense that the + * algorithm will not calculate the distance to any vertices which are farther away than this + * distance. A negative value for max_dist will ensure that no further distances are + * calculated. + * + *

    This can be useful for limiting the amount of time and space used by this algorithm if the + * graph is very large. + * + *

    Note: if this instance has already calculated distances greater than max_dist, + * and the results are cached, those results will still be valid and available; this limit applies + * only to subsequent distance calculations. + * + * @param max_dist the maximum distance that this instance will calculate + * @see #setMaxTargets(int) + */ + public void setMaxDistance(double max_dist) { + this.max_distance = max_dist; + for (V v : sourceMap.keySet()) { + SourceData sd = sourceMap.get(v); + sd.reached_max = + (this.max_distance <= sd.dist_reached) || (sd.distances.size() >= max_targets); } - - /** - * Allows the user to specify the maximum distance that this instance will calculate. - * Any vertices past this distance will effectively be unreachable from the source, in - * the sense that the algorithm will not calculate the distance to any vertices which - * are farther away than this distance. A negative value for max_dist - * will ensure that no further distances are calculated. - * - *

    This can be useful for limiting the amount of time and space used by this algorithm - * if the graph is very large. - * - *

    Note: if this instance has already calculated distances greater than max_dist, - * and the results are cached, those results will still be valid and available; this limit - * applies only to subsequent distance calculations. - * - * @param max_dist the maximum distance that this instance will calculate - * - * @see #setMaxTargets(int) - */ - public void setMaxDistance(double max_dist) - { - this.max_distance = max_dist; - for (V v : sourceMap.keySet()) - { - SourceData sd = sourceMap.get(v); - sd.reached_max = (this.max_distance <= sd.dist_reached) || (sd.distances.size() >= max_targets); - } + } + + /** + * Allows the user to specify the maximum number of target vertices per source vertex for which + * this instance will calculate distances. Once this threshold is reached, any further vertices + * will effectively be unreachable from the source, in the sense that the algorithm will not + * calculate the distance to any more vertices. A negative value for max_targets will + * ensure that no further distances are calculated. + * + *

    This can be useful for limiting the amount of time and space used by this algorithm if the + * graph is very large. + * + *

    Note: if this instance has already calculated distances to a greater number of targets than + * max_targets, and the results are cached, those results will still be valid and + * available; this limit applies only to subsequent distance calculations. + * + * @param max_targets the maximum number of targets for which this instance will calculate + * distances + * @see #setMaxDistance(double) + */ + public void setMaxTargets(int max_targets) { + this.max_targets = max_targets; + for (V v : sourceMap.keySet()) { + SourceData sd = sourceMap.get(v); + sd.reached_max = + (this.max_distance <= sd.dist_reached) || (sd.distances.size() >= max_targets); } - - /** - * Allows the user to specify the maximum number of target vertices per source vertex - * for which this instance will calculate distances. Once this threshold is reached, - * any further vertices will effectively be unreachable from the source, in - * the sense that the algorithm will not calculate the distance to any more vertices. - * A negative value for max_targets will ensure that no further distances are calculated. - * - *

    This can be useful for limiting the amount of time and space used by this algorithm - * if the graph is very large. - * - *

    Note: if this instance has already calculated distances to a greater number of - * targets than max_targets, and the results are cached, those results - * will still be valid and available; this limit applies only to subsequent distance - * calculations. - * - * @param max_targets the maximum number of targets for which this instance will calculate - * distances - * - * @see #setMaxDistance(double) - */ - public void setMaxTargets(int max_targets) - { - this.max_targets = max_targets; - for (V v : sourceMap.keySet()) - { - SourceData sd = sourceMap.get(v); - sd.reached_max = (this.max_distance <= sd.dist_reached) || (sd.distances.size() >= max_targets); - } + } + + /** + * Clears all stored distances for this instance. Should be called whenever the graph is modified + * (edge weights changed or edges added/removed). If the user knows that some currently calculated + * distances are unaffected by a change, reset(V) may be appropriate instead. + * + * @see #reset(Object) + */ + public void reset() { + sourceMap = new HashMap(); + } + + /** + * Specifies whether or not this instance of DijkstraShortestPath should cache its + * results (final and partial) for future reference. + * + * @param enable true if the results are to be cached, and false + * otherwise + */ + public void enableCaching(boolean enable) { + this.cached = enable; + } + + /** + * Clears all stored distances for the specified source vertex source. Should be + * called whenever the stored distances from this vertex are invalidated by changes to the graph. + * + * @param source the vertex for which stored distances should be cleared + * @see #reset() + */ + public void reset(V source) { + sourceMap.put(source, null); + } + + /** Compares according to distances, so that the BinaryHeap knows how to order the tree. */ + protected static class VertexComparator implements Comparator { + private Map distances; + + protected VertexComparator(Map distances) { + this.distances = distances; } - - /** - * Clears all stored distances for this instance. - * Should be called whenever the graph is modified (edge weights - * changed or edges added/removed). If the user knows that - * some currently calculated distances are unaffected by a - * change, reset(V) may be appropriate instead. - * - * @see #reset(Object) - */ - public void reset() - { - sourceMap = new HashMap(); + + public int compare(V o1, V o2) { + return ((Double) distances.get(o1)).compareTo((Double) distances.get(o2)); } - - /** - * Specifies whether or not this instance of DijkstraShortestPath - * should cache its results (final and partial) for future reference. - * - * @param enable true if the results are to be cached, and - * false otherwise - */ - public void enableCaching(boolean enable) - { - this.cached = enable; + } + + /** + * For a given source vertex, holds the estimated and final distances, tentative and final + * assignments of incoming edges on the shortest path from the source vertex, and a priority queue + * (ordered by estimated distance) of the vertices for which distances are unknown. + * + * @author Joshua O'Madadhain + */ + protected class SourceData { + protected LinkedHashMap distances; + protected Map estimatedDistances; + protected MapBinaryHeap unknownVertices; + protected boolean reached_max = false; + protected double dist_reached = 0; + + protected SourceData(V source) { + distances = new LinkedHashMap(); + estimatedDistances = new HashMap(); + unknownVertices = new MapBinaryHeap(new VertexComparator(estimatedDistances)); + + sourceMap.put(source, this); + + // initialize priority queue + estimatedDistances.put(source, new Double(0)); // distance from source to itself is 0 + unknownVertices.add(source); + reached_max = false; + dist_reached = 0; } - - /** - * Clears all stored distances for the specified source vertex - * source. Should be called whenever the stored distances - * from this vertex are invalidated by changes to the graph. - * - * @param source the vertex for which stored distances should be cleared - * - * @see #reset() - */ - public void reset(V source) - { - sourceMap.put(source, null); + + protected Map.Entry getNextVertex() { + V v = unknownVertices.remove(); + Double dist = (Double) estimatedDistances.remove(v); + distances.put(v, dist); + return new BasicMapEntry(v, dist); } - /** - * Compares according to distances, so that the BinaryHeap knows how to - * order the tree. - */ - protected static class VertexComparator implements Comparator - { - private Map distances; - - protected VertexComparator(Map distances) - { - this.distances = distances; - } + protected void update(V dest, E tentative_edge, double new_dist) { + estimatedDistances.put(dest, new_dist); + unknownVertices.update(dest); + } - public int compare(V o1, V o2) - { - return ((Double) distances.get(o1)).compareTo((Double) distances.get(o2)); - } + protected void createRecord(V w, E e, double new_dist) { + estimatedDistances.put(w, new_dist); + unknownVertices.add(w); } - - /** - * For a given source vertex, holds the estimated and final distances, - * tentative and final assignments of incoming edges on the shortest path from - * the source vertex, and a priority queue (ordered by estimated distance) - * of the vertices for which distances are unknown. - * - * @author Joshua O'Madadhain - */ - protected class SourceData - { - protected LinkedHashMap distances; - protected Map estimatedDistances; - protected MapBinaryHeap unknownVertices; - protected boolean reached_max = false; - protected double dist_reached = 0; - - protected SourceData(V source) - { - distances = new LinkedHashMap(); - estimatedDistances = new HashMap(); - unknownVertices = new MapBinaryHeap(new VertexComparator(estimatedDistances)); - - sourceMap.put(source, this); - - // initialize priority queue - estimatedDistances.put(source, new Double(0)); // distance from source to itself is 0 - unknownVertices.add(source); - reached_max = false; - dist_reached = 0; - } - - protected Map.Entry getNextVertex() - { - V v = unknownVertices.remove(); - Double dist = (Double)estimatedDistances.remove(v); - distances.put(v, dist); - return new BasicMapEntry(v, dist); - } - - protected void update(V dest, E tentative_edge, double new_dist) - { - estimatedDistances.put(dest, new_dist); - unknownVertices.update(dest); - } - - protected void createRecord(V w, E e, double new_dist) - { - estimatedDistances.put(w, new_dist); - unknownVertices.add(w); - } - - protected void restoreVertex(V v, double dist) - { - estimatedDistances.put(v, dist); - unknownVertices.add(v); - distances.remove(v); - } + + protected void restoreVertex(V v, double dist) { + estimatedDistances.put(v, dist); + unknownVertices.add(v); + distances.remove(v); } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraShortestPath.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraShortestPath.java index f8e64798..40c67923 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraShortestPath.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraShortestPath.java @@ -1,14 +1,17 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.shortestpath; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.graph.Network; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -17,280 +20,246 @@ import java.util.Map; import java.util.Set; -import com.google.common.base.Function; -import com.google.common.base.Preconditions; -import com.google.common.graph.Network; - /** - *

    Calculates distances and shortest paths using Dijkstra's - * single-source-shortest-path algorithm. This is a lightweight - * extension of DijkstraDistance that also stores - * path information, so that the shortest paths can be reconstructed. - * - *

    The elements in the maps returned by - * getIncomingEdgeMap are ordered (that is, returned - * by the iterator) by nondecreasing distance from source. - * + * Calculates distances and shortest paths using Dijkstra's single-source-shortest-path algorithm. + * This is a lightweight extension of DijkstraDistance that also stores path + * information, so that the shortest paths can be reconstructed. + * + *

    The elements in the maps returned by getIncomingEdgeMap are ordered (that is, + * returned by the iterator) by nondecreasing distance from source. + * * @author Joshua O'Madadhain * @author Tom Nelson converted to jung2 * @see DijkstraDistance */ -public class DijkstraShortestPath extends DijkstraDistance implements ShortestPath -{ - // TODO: refactor the heck out of this and of DijkstraDistance - - /** - *

    Creates an instance of DijkstraShortestPath for - * the specified graph and the specified method of extracting weights - * from edges, which caches results locally if and only if - * cached is true. - * - * @param g the graph on which distances will be calculated - * @param nev the class responsible for returning weights for edges - * @param cached specifies whether the results are to be cached - */ - public DijkstraShortestPath(Network g, Function nev, boolean cached) - { - super(g, nev, cached); - } - - /** - *

    Creates an instance of DijkstraShortestPath for - * the specified graph and the specified method of extracting weights - * from edges, which caches results locally. - * - * @param g the graph on which distances will be calculated - * @param nev the class responsible for returning weights for edges - */ - public DijkstraShortestPath(Network g, Function nev) - { - super(g, nev); - } - - /** - *

    Creates an instance of DijkstraShortestPath for - * the specified unweighted graph (that is, all weights 1) which - * caches results locally. - * - * @param g the graph on which distances will be calculated - */ - public DijkstraShortestPath(Network g) - { - super(g); - } +public class DijkstraShortestPath extends DijkstraDistance + implements ShortestPath { + // TODO: refactor the heck out of this and of DijkstraDistance - /** - *

    Creates an instance of DijkstraShortestPath for - * the specified unweighted graph (that is, all weights 1) which - * caches results locally. - * - * @param g the graph on which distances will be calculated - * @param cached specifies whether the results are to be cached - */ - public DijkstraShortestPath(Network g, boolean cached) - { - super(g, cached); - } - - @Override - protected SourceData getSourceData(V source) - { - SourceData sd = sourceMap.get(source); - if (sd == null) - sd = new SourcePathData(source); - return sd; + /** + * Creates an instance of DijkstraShortestPath for the specified graph and the + * specified method of extracting weights from edges, which caches results locally if and only if + * cached is true. + * + * @param g the graph on which distances will be calculated + * @param nev the class responsible for returning weights for edges + * @param cached specifies whether the results are to be cached + */ + public DijkstraShortestPath(Network g, Function nev, boolean cached) { + super(g, nev, cached); + } + + /** + * Creates an instance of DijkstraShortestPath for the specified graph and the + * specified method of extracting weights from edges, which caches results locally. + * + * @param g the graph on which distances will be calculated + * @param nev the class responsible for returning weights for edges + */ + public DijkstraShortestPath(Network g, Function nev) { + super(g, nev); + } + + /** + * Creates an instance of DijkstraShortestPath for the specified unweighted graph + * (that is, all weights 1) which caches results locally. + * + * @param g the graph on which distances will be calculated + */ + public DijkstraShortestPath(Network g) { + super(g); + } + + /** + * Creates an instance of DijkstraShortestPath for the specified unweighted graph + * (that is, all weights 1) which caches results locally. + * + * @param g the graph on which distances will be calculated + * @param cached specifies whether the results are to be cached + */ + public DijkstraShortestPath(Network g, boolean cached) { + super(g, cached); + } + + @Override + protected SourceData getSourceData(V source) { + SourceData sd = sourceMap.get(source); + if (sd == null) sd = new SourcePathData(source); + return sd; + } + + /** + * Returns the last edge on a shortest path from source to target, or + * null if target is not reachable from source. + * + *

    If either vertex is not in the graph for which this instance was created, throws + * IllegalArgumentException. + * + * @param source the vertex where the shortest path starts + * @param target the vertex where the shortest path ends + * @return the last edge on a shortest path from {@code source} to {@code target} or null if + * {@code target} is not reachable from {@code source} + */ + public E getIncomingEdge(V source, V target) { + Preconditions.checkArgument( + g.nodes().contains(target), + "Specified target vertex %s is not part of graph %s", + target, + g); + Preconditions.checkArgument( + g.nodes().contains(source), + "Specified source vertex %s is not part of graph %s", + source, + g); + + Set targets = new HashSet(); + targets.add(target); + singleSourceShortestPath(source, targets, g.nodes().size()); + @SuppressWarnings("unchecked") + Map incomingEdgeMap = ((SourcePathData) sourceMap.get(source)).incomingEdges; + E incomingEdge = incomingEdgeMap.get(target); + + if (!cached) reset(source); + + return incomingEdge; + } + + /** + * Returns a LinkedHashMap which maps each vertex in the graph (including the + * source vertex) to the last edge on the shortest path from the source + * vertex. The map's iterator will return the elements in order of increasing distance from + * source. + * + * @see DijkstraDistance#getDistanceMap(Object,int) + * @see DijkstraDistance#getDistance(Object,Object) + * @param source the vertex from which distances are measured + */ + public Map getIncomingEdgeMap(V source) { + return getIncomingEdgeMap(source, g.nodes().size()); + } + + /** + * Returns a List of the edges on the shortest path from source to + * target, in order of their occurrence on this path. If either vertex is not in the + * graph for which this instance was created, throws IllegalArgumentException. + * + * @param source the starting vertex for the path to generate + * @param target the ending vertex for the path to generate + * @return the edges on the shortest path from {@code source} to {@code target}, in order of their + * occurrence + */ + public List getPath(V source, V target) { + Preconditions.checkArgument( + g.nodes().contains(target), + "Specified target vertex %s is not part of graph %s", + target, + g); + Preconditions.checkArgument( + g.nodes().contains(source), + "Specified source vertex %s is not part of graph %s", + source, + g); + + LinkedList path = new LinkedList(); + + // collect path data; must use internal method rather than + // calling getIncomingEdge() because getIncomingEdge() may + // wipe out results if results are not cached + Set targets = new HashSet(); + targets.add(target); + singleSourceShortestPath(source, targets, g.nodes().size()); + @SuppressWarnings("unchecked") + Map incomingEdges = ((SourcePathData) sourceMap.get(source)).incomingEdges; + + if (incomingEdges.isEmpty() || incomingEdges.get(target) == null) return path; + V current = target; + while (!current.equals(source)) { + E incoming = incomingEdges.get(current); + path.addFirst(incoming); + current = g.incidentNodes(incoming).adjacentNode(current); } - - /** - *

    Returns the last edge on a shortest path from source - * to target, or null if target is not - * reachable from source. - * - *

    If either vertex is not in the graph for which this instance - * was created, throws IllegalArgumentException. - * - * @param source the vertex where the shortest path starts - * @param target the vertex where the shortest path ends - * @return the last edge on a shortest path from {@code source} to {@code target} - * or null if {@code target} is not reachable from {@code source} - */ - public E getIncomingEdge(V source, V target) - { - Preconditions.checkArgument( - g.nodes().contains(target), - "Specified target vertex %s is not part of graph %s", target, g); - Preconditions.checkArgument( - g.nodes().contains(source), - "Specified source vertex %s is not part of graph %s", source, g); + return path; + } + + /** + * Returns a LinkedHashMap which maps each of the closest numDests + * vertices to the source vertex in the graph (including the source + * vertex) to the incoming edge along the path from that vertex. Throws an + * IllegalArgumentException if source is not in this instance's graph, or if + * numDests is either less than 1 or greater than the number of vertices in the + * graph. + * + * @see #getIncomingEdgeMap(Object) + * @see #getPath(Object,Object) + * @param source the vertex from which distances are measured + * @param numDests the number of vertices for which to measure distances + * @return a map from each of the closest {@code numDests} vertices to the last edge on the + * shortest path to that vertex starting from {@code source} + */ + public LinkedHashMap getIncomingEdgeMap(V source, int numDests) { + Preconditions.checkArgument( + g.nodes().contains(source), + "Specified source vertex %s is not part of graph %s", + source, + g); + Preconditions.checkArgument( + numDests >= 1 && numDests <= g.nodes().size(), + "number of destinations must be in [1, %d]", + g.nodes().size()); + + singleSourceShortestPath(source, null, numDests); - Set targets = new HashSet(); - targets.add(target); - singleSourceShortestPath(source, targets, g.nodes().size()); - @SuppressWarnings("unchecked") - Map incomingEdgeMap = - ((SourcePathData)sourceMap.get(source)).incomingEdges; - E incomingEdge = incomingEdgeMap.get(target); - - if (!cached) - reset(source); - - return incomingEdge; - } + @SuppressWarnings("unchecked") + LinkedHashMap incomingEdgeMap = ((SourcePathData) sourceMap.get(source)).incomingEdges; - /** - *

    Returns a LinkedHashMap which maps each vertex - * in the graph (including the source vertex) - * to the last edge on the shortest path from the - * source vertex. - * The map's iterator will return the elements in order of - * increasing distance from source. - * - * @see DijkstraDistance#getDistanceMap(Object,int) - * @see DijkstraDistance#getDistance(Object,Object) - * @param source the vertex from which distances are measured - */ - public Map getIncomingEdgeMap(V source) - { - return getIncomingEdgeMap(source, g.nodes().size()); - } + if (!cached) reset(source); - /** - * Returns a List of the edges on the shortest path from - * source to target, in order of their - * occurrence on this path. - * If either vertex is not in the graph for which this instance - * was created, throws IllegalArgumentException. - * - * @param source the starting vertex for the path to generate - * @param target the ending vertex for the path to generate - * @return the edges on the shortest path from {@code source} to {@code target}, - * in order of their occurrence - */ - public List getPath(V source, V target) - { - Preconditions.checkArgument( - g.nodes().contains(target), - "Specified target vertex %s is not part of graph %s", target, g); - Preconditions.checkArgument( - g.nodes().contains(source), - "Specified source vertex %s is not part of graph %s", source, g); - - LinkedList path = new LinkedList(); + return incomingEdgeMap; + } - // collect path data; must use internal method rather than - // calling getIncomingEdge() because getIncomingEdge() may - // wipe out results if results are not cached - Set targets = new HashSet(); - targets.add(target); - singleSourceShortestPath(source, targets, g.nodes().size()); - @SuppressWarnings("unchecked") - Map incomingEdges = - ((SourcePathData)sourceMap.get(source)).incomingEdges; - - if (incomingEdges.isEmpty() || incomingEdges.get(target) == null) - return path; - V current = target; - while (!current.equals(source)) - { - E incoming = incomingEdges.get(current); - path.addFirst(incoming); - current = g.incidentNodes(incoming).adjacentNode(current); - } - return path; - } + /** + * For a given source vertex, holds the estimated and final distances, tentative and final + * assignments of incoming edges on the shortest path from the source vertex, and a priority queue + * (ordered by estimaed distance) of the vertices for which distances are unknown. + * + * @author Joshua O'Madadhain + */ + protected class SourcePathData extends SourceData { + protected Map tentativeIncomingEdges; + protected LinkedHashMap incomingEdges; - - /** - *

    Returns a LinkedHashMap which maps each of the closest - * numDests vertices to the source vertex - * in the graph (including the source vertex) - * to the incoming edge along the path from that vertex. Throws - * an IllegalArgumentException if source - * is not in this instance's graph, or if numDests is - * either less than 1 or greater than the number of vertices in the - * graph. - * - * @see #getIncomingEdgeMap(Object) - * @see #getPath(Object,Object) - * @param source the vertex from which distances are measured - * @param numDests the number of vertices for which to measure distances - * @return a map from each of the closest {@code numDests} vertices - * to the last edge on the shortest path to that vertex starting from {@code source} - */ - public LinkedHashMap getIncomingEdgeMap(V source, int numDests) - { - Preconditions.checkArgument( - g.nodes().contains(source), - "Specified source vertex %s is not part of graph %s", source, g); - Preconditions.checkArgument( - numDests >= 1 && numDests <= g.nodes().size(), - "number of destinations must be in [1, %d]", g.nodes().size()); + protected SourcePathData(V source) { + super(source); + incomingEdges = new LinkedHashMap(); + tentativeIncomingEdges = new HashMap(); + } - singleSourceShortestPath(source, null, numDests); - - @SuppressWarnings("unchecked") - LinkedHashMap incomingEdgeMap = - ((SourcePathData)sourceMap.get(source)).incomingEdges; - - if (!cached) - reset(source); - - return incomingEdgeMap; - } - - - /** - * For a given source vertex, holds the estimated and final distances, - * tentative and final assignments of incoming edges on the shortest path from - * the source vertex, and a priority queue (ordered by estimaed distance) - * of the vertices for which distances are unknown. - * - * @author Joshua O'Madadhain - */ - protected class SourcePathData extends SourceData - { - protected Map tentativeIncomingEdges; - protected LinkedHashMap incomingEdges; + @Override + public void update(V dest, E tentative_edge, double new_dist) { + super.update(dest, tentative_edge, new_dist); + tentativeIncomingEdges.put(dest, tentative_edge); + } - protected SourcePathData(V source) - { - super(source); - incomingEdges = new LinkedHashMap(); - tentativeIncomingEdges = new HashMap(); - } - - @Override - public void update(V dest, E tentative_edge, double new_dist) - { - super.update(dest, tentative_edge, new_dist); - tentativeIncomingEdges.put(dest, tentative_edge); - } - - @Override - public Map.Entry getNextVertex() - { - Map.Entry p = super.getNextVertex(); - V v = p.getKey(); - E incoming = tentativeIncomingEdges.remove(v); - incomingEdges.put(v, incoming); - return p; - } - - @Override - public void restoreVertex(V v, double dist) - { - super.restoreVertex(v, dist); - E incoming = incomingEdges.get(v); - tentativeIncomingEdges.put(v, incoming); - } - - @Override - public void createRecord(V w, E e, double new_dist) - { - super.createRecord(w, e, new_dist); - tentativeIncomingEdges.put(w, e); - } - + @Override + public Map.Entry getNextVertex() { + Map.Entry p = super.getNextVertex(); + V v = p.getKey(); + E incoming = tentativeIncomingEdges.remove(v); + incomingEdges.put(v, incoming); + return p; } + @Override + public void restoreVertex(V v, double dist) { + super.restoreVertex(v, dist); + E incoming = incomingEdges.get(v); + tentativeIncomingEdges.put(v, incoming); + } + + @Override + public void createRecord(V w, E e, double new_dist) { + super.createRecord(w, e, new_dist); + tentativeIncomingEdges.put(w, e); + } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/Distance.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/Distance.java index e0fc2fe4..b0434443 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/Distance.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/Distance.java @@ -1,7 +1,7 @@ /* * Created on Apr 2, 2004 * - * Copyright (c) 2004, The JUNG Authors + * Copyright (c) 2004, The JUNG Authors * * All rights reserved. * @@ -13,34 +13,29 @@ import java.util.Map; - /** - * An interface for classes which calculate the distance between - * one vertex and another. - * + * An interface for classes which calculate the distance between one vertex and another. + * * @author Joshua O'Madadhain */ -public interface Distance -{ - /** - * Returns the distance from the source vertex to the - * target vertex. If target is not reachable from - * source, returns null. - * - * @param source the vertex from which distance is to be measured - * @param target the vertex to which distance is to be measured - * @return the distance from {@code source} to {@code target} - */ - Number getDistance(V source, V target); +public interface Distance { + /** + * Returns the distance from the source vertex to the target vertex. If + * target is not reachable from source, returns null. + * + * @param source the vertex from which distance is to be measured + * @param target the vertex to which distance is to be measured + * @return the distance from {@code source} to {@code target} + */ + Number getDistance(V source, V target); - /** - * Returns a Map which maps each vertex in the graph (including - * the source vertex) to its distance (represented as a Number) - * from source. If any vertex is not reachable from - * source, no distance is stored for that vertex. - * - * @param source the vertex from which distances are to be measured - * @return a {@code Map} of the distances from {@code source} to other vertices in the graph - */ - Map getDistanceMap(V source); + /** + * Returns a Map which maps each vertex in the graph (including the source + * vertex) to its distance (represented as a Number) from source. If any + * vertex is not reachable from source, no distance is stored for that vertex. + * + * @param source the vertex from which distances are to be measured + * @return a {@code Map} of the distances from {@code source} to other vertices in the graph + */ + Map getDistanceMap(V source); } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/MinimumSpanningTree.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/MinimumSpanningTree.java index 2ecca8ae..5f2cf849 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/MinimumSpanningTree.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/MinimumSpanningTree.java @@ -1,10 +1,5 @@ package edu.uci.ics.jung.algorithms.shortestpath; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - import com.google.common.base.Function; import com.google.common.graph.MutableNetwork; import com.google.common.graph.MutableValueGraph; @@ -12,133 +7,137 @@ import com.google.common.graph.NetworkBuilder; import com.google.common.graph.ValueGraph; import com.google.common.graph.ValueGraphBuilder; - import edu.uci.ics.jung.algorithms.util.MapBinaryHeap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; /** * Creates a minimum spanning tree of a specified graph using a variation of Prim's algorithm. - * - *

    The input graph is treated as though it were undirected, and the generated spanning tree - * is undirected. - * + * + *

    The input graph is treated as though it were undirected, and the generated spanning tree is + * undirected. + * * @author Tom Nelson - tomnelson@dev.java.net * @author Joshua O'Madadhain - * * @param the vertex type * @param the edge type */ -public class MinimumSpanningTree { - // TODO: consider providing a separate mechanism for extracting a spanning tree from an unweighted graph. - - /** - * Extracts a minimum spanning forest from {@code graph} based on the specified edge weights. - * (If {@code graph} is connected, then the graph returned will be a tree.) - * - *

    Uses Prim's algorithm with a binary heap, for a run time of O(|E| log |V|). - * - * @param graph the graph from which to extract the minimum spanning forest - * @param edgeWeights a mapping from edges to weights - */ - public static Network extractFrom(Network graph, Function edgeWeights) { - Set remainingNodes = new HashSet<>(graph.nodes()); - Map> nodeData = new HashMap<>(); - // initialize node data - for (N node : remainingNodes) { - nodeData.put(node, new NodeData<>()); - } - MapBinaryHeap heap = new MapBinaryHeap<>( - (a, b) -> Double.compare(nodeData.get(a).cost, nodeData.get(b).cost)); - heap.addAll(remainingNodes); +public class MinimumSpanningTree { + // TODO: consider providing a separate mechanism for extracting a spanning tree from an unweighted graph. + + /** + * Extracts a minimum spanning forest from {@code graph} based on the specified edge weights. (If + * {@code graph} is connected, then the graph returned will be a tree.) + * + *

    Uses Prim's algorithm with a binary heap, for a run time of O(|E| log |V|). + * + * @param graph the graph from which to extract the minimum spanning forest + * @param edgeWeights a mapping from edges to weights + */ + public static Network extractFrom( + Network graph, Function edgeWeights) { + Set remainingNodes = new HashSet<>(graph.nodes()); + Map> nodeData = new HashMap<>(); + // initialize node data + for (N node : remainingNodes) { + nodeData.put(node, new NodeData<>()); + } + MapBinaryHeap heap = + new MapBinaryHeap<>((a, b) -> Double.compare(nodeData.get(a).cost, nodeData.get(b).cost)); + heap.addAll(remainingNodes); + + // TODO: it seems unfortunate that this is a directed graph, but our libraries + // (e.g. TreeLayout) assume that it is one. Consider other options: + // * let the user specify whether to create a directed or undirected graph + // * let TreeLayout (etc.) handle undirected graphs (given a root set) + MutableNetwork tree = + NetworkBuilder.directed().build(); // no self-loops or parallel edges - // TODO: it seems unfortunate that this is a directed graph, but our libraries - // (e.g. TreeLayout) assume that it is one. Consider other options: - // * let the user specify whether to create a directed or undirected graph - // * let TreeLayout (etc.) handle undirected graphs (given a root set) - MutableNetwork tree = NetworkBuilder.directed().build(); // no self-loops or parallel edges + while (!remainingNodes.isEmpty()) { + N node = heap.poll(); // remove the node with the minimum incident edge cost + remainingNodes.remove(node); + E edge = nodeData.get(node).connection; + if (edge == null) { + tree.addNode(node); + } else { + tree.addEdge(node, graph.incidentNodes(edge).adjacentNode(node), edge); + } + for (N adjacentNode : graph.adjacentNodes(node)) { + if (!remainingNodes.contains(adjacentNode)) { + continue; + } + NodeData adjacentNodeData = nodeData.get(adjacentNode); + for (E connectingEdge : graph.edgesConnecting(node, adjacentNode)) { + double connectingEdgeWeight = edgeWeights.apply(connectingEdge); + if (connectingEdgeWeight < adjacentNodeData.cost) { + adjacentNodeData.update(connectingEdgeWeight, connectingEdge); + heap.update(adjacentNode); + } + } + } + } + return tree; + } + + /** + * Extracts a minimum spanning forest from {@code graph} using its edge values (interpreted as + * doubles). If {@code graph} is connected, then the graph returned will be a tree; otherwise it + * will be a forest of trees. + * + *

    Uses Prim's algorithm with a binary heap, for a run time of O(|E| log |V|). + * + * @param graph the graph from which to extract the minimum spanning forest + */ + public static ValueGraph extractFrom(ValueGraph graph) { + Set remainingNodes = new HashSet<>(graph.nodes()); + Map> nodeData = new HashMap<>(); + // initialize node data + for (N node : remainingNodes) { + nodeData.put(node, new NodeData<>()); + } + MapBinaryHeap heap = + new MapBinaryHeap<>((a, b) -> Double.compare(nodeData.get(a).cost, nodeData.get(b).cost)); + heap.addAll(remainingNodes); - while (!remainingNodes.isEmpty()) { - N node = heap.poll(); // remove the node with the minimum incident edge cost - remainingNodes.remove(node); - E edge = nodeData.get(node).connection; - if (edge == null) { - tree.addNode(node); - } else { - tree.addEdge(node, graph.incidentNodes(edge).adjacentNode(node), edge); - } - for (N adjacentNode : graph.adjacentNodes(node)) { - if (!remainingNodes.contains(adjacentNode)) { - continue; - } - NodeData adjacentNodeData = nodeData.get(adjacentNode); - for (E connectingEdge : graph.edgesConnecting(node, adjacentNode)) { - double connectingEdgeWeight = edgeWeights.apply(connectingEdge); - if (connectingEdgeWeight < adjacentNodeData.cost) { - adjacentNodeData.update(connectingEdgeWeight, connectingEdge); - heap.update(adjacentNode); - } - } - } - } - return tree; - } + MutableValueGraph tree = + ValueGraphBuilder.directed().build(); // no self-loops or parallel edges + + while (!remainingNodes.isEmpty()) { + N node = heap.peek(); // get the node with the minimum incident edge cost + remainingNodes.remove(node); + N connectedNode = nodeData.get(node).connection; + if (connectedNode == null) { + tree.addNode(node); + } else { + tree.putEdgeValue(node, connectedNode, graph.edgeValue(node, connectedNode)); + } + for (N adjacentNode : graph.adjacentNodes(node)) { + if (!remainingNodes.contains(adjacentNode)) { + continue; + } + NodeData adjacentNodeData = nodeData.get(adjacentNode); + double connectingEdgeWeight = graph.edgeValue(node, adjacentNode).doubleValue(); + if (connectingEdgeWeight < adjacentNodeData.cost) { + adjacentNodeData.update(connectingEdgeWeight, node); + heap.update(adjacentNode); + } + } + } + return tree; + } - /** - * Extracts a minimum spanning forest from {@code graph} using its edge values (interpreted as - * doubles). - * If {@code graph} is connected, then the graph returned will be a tree; otherwise it will be - * a forest of trees. - * - *

    Uses Prim's algorithm with a binary heap, for a run time of O(|E| log |V|). - * - * @param graph the graph from which to extract the minimum spanning forest - */ - public static ValueGraph extractFrom(ValueGraph graph) { - Set remainingNodes = new HashSet<>(graph.nodes()); - Map> nodeData = new HashMap<>(); - // initialize node data - for (N node : remainingNodes) { - nodeData.put(node, new NodeData<>()); - } - MapBinaryHeap heap = new MapBinaryHeap<>( - (a, b) -> Double.compare(nodeData.get(a).cost, nodeData.get(b).cost)); - heap.addAll(remainingNodes); + // TODO: make this an AutoValue? + private static class NodeData { + private double cost = Double.POSITIVE_INFINITY; + private T connection = null; - MutableValueGraph tree = ValueGraphBuilder.directed().build(); // no self-loops or parallel edges - - while (!remainingNodes.isEmpty()) { - N node = heap.peek(); // get the node with the minimum incident edge cost - remainingNodes.remove(node); - N connectedNode = nodeData.get(node).connection; - if (connectedNode == null) { - tree.addNode(node); - } else { - tree.putEdgeValue(node, connectedNode, graph.edgeValue(node, connectedNode)); - } - for (N adjacentNode : graph.adjacentNodes(node)) { - if (!remainingNodes.contains(adjacentNode)) { - continue; - } - NodeData adjacentNodeData = nodeData.get(adjacentNode); - double connectingEdgeWeight = graph.edgeValue(node, adjacentNode).doubleValue(); - if (connectingEdgeWeight < adjacentNodeData.cost) { - adjacentNodeData.update(connectingEdgeWeight, node); - heap.update(adjacentNode); - } - } - } - return tree; - } + private NodeData() {} - // TODO: make this an AutoValue? - private static class NodeData { - private double cost = Double.POSITIVE_INFINITY; - private T connection = null; - - private NodeData() {} - - private void update(double cost, T connection) { - this.cost = cost; - this.connection = connection; - } + private void update(double cost, T connection) { + this.cost = cost; + this.connection = connection; } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/ShortestPath.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/ShortestPath.java index 104ace5f..fcc88a64 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/ShortestPath.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/ShortestPath.java @@ -1,31 +1,27 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -* -* Created on Feb 12, 2004 -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + * + * Created on Feb 12, 2004 + */ package edu.uci.ics.jung.algorithms.shortestpath; import java.util.Map; - -/** - * An interface for algorithms that calculate shortest paths. - */ -public interface ShortestPath -{ - /** - * Returns a map from vertices to the last edge on the shortest path to that vertex - * starting from {@code source}. - * - * @param source the starting point for the shortest paths - * @return a map from vertices to the last edge on the shortest path to that vertex - * starting from {@code source} - */ - Map getIncomingEdgeMap(V source); +/** An interface for algorithms that calculate shortest paths. */ +public interface ShortestPath { + /** + * Returns a map from vertices to the last edge on the shortest path to that vertex starting from + * {@code source}. + * + * @param source the starting point for the shortest paths + * @return a map from vertices to the last edge on the shortest path to that vertex starting from + * {@code source} + */ + Map getIncomingEdgeMap(V source); } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/ShortestPathUtils.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/ShortestPathUtils.java index af12fe56..ba97c78b 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/ShortestPathUtils.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/ShortestPathUtils.java @@ -1,7 +1,7 @@ /* * Created on Jul 10, 2005 * - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,46 +11,39 @@ */ package edu.uci.ics.jung.algorithms.shortestpath; +import com.google.common.graph.Network; import java.util.LinkedList; import java.util.List; import java.util.Map; -import com.google.common.graph.Network; +/** Utilities relating to the shortest paths in a graph. */ +public class ShortestPathUtils { + /** + * Returns a List of the edges on the shortest path from source to + * target, in order of their occurrence on this path. + * + * @param graph the graph for which the shortest path is defined + * @param sp holder of the shortest path information + * @param source the vertex from which the shortest path is measured + * @param target the vertex to which the shortest path is measured + * @param the vertex type + * @param the edge type + * @return the edges on the shortest path from {@code source} to {@code target}, in the order + * traversed + */ + public static List getPath( + Network graph, ShortestPath sp, V source, V target) { + LinkedList path = new LinkedList(); -/** - * Utilities relating to the shortest paths in a graph. - */ -public class ShortestPathUtils -{ - /** - * Returns a List of the edges on the shortest path from - * source to target, in order of their - * occurrence on this path. - * - * @param graph the graph for which the shortest path is defined - * @param sp holder of the shortest path information - * @param source the vertex from which the shortest path is measured - * @param target the vertex to which the shortest path is measured - * @param the vertex type - * @param the edge type - * @return the edges on the shortest path from {@code source} to {@code target}, - * in the order traversed - */ - public static List getPath(Network graph, ShortestPath sp, V source, V target) - { - LinkedList path = new LinkedList(); - - Map incomingEdges = sp.getIncomingEdgeMap(source); - - if (incomingEdges.isEmpty() || incomingEdges.get(target) == null) - return path; - V current = target; - while (!current.equals(source)) - { - E incoming = incomingEdges.get(current); - path.addFirst(incoming); - current = graph.incidentNodes(incoming).adjacentNode(current); - } - return path; + Map incomingEdges = sp.getIncomingEdgeMap(source); + + if (incomingEdges.isEmpty() || incomingEdges.get(target) == null) return path; + V current = target; + while (!current.equals(source)) { + E incoming = incomingEdges.get(current); + path.addFirst(incoming); + current = graph.incidentNodes(incoming).adjacentNode(current); } + return path; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/UnweightedShortestPath.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/UnweightedShortestPath.java index 3c3743f1..cd76b11e 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/UnweightedShortestPath.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/UnweightedShortestPath.java @@ -1,147 +1,123 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.shortestpath; +import com.google.common.graph.Graph; import java.util.HashMap; import java.util.Map; -import com.google.common.graph.Graph; - /** * Computes the shortest path distances for graphs whose edges are not weighted (using BFS). - * + * * @author Scott White */ // TODO: refactor to make this (much!) more efficient -public class UnweightedShortestPath implements Distance -{ - private Map> mDistanceMap; - private Map> mPredecessorMap; - private Graph mGraph; - private Map distances = new HashMap(); +public class UnweightedShortestPath implements Distance { + private Map> mDistanceMap; + private Map> mPredecessorMap; + private Graph mGraph; + private Map distances = new HashMap(); - /** - * Constructs and initializes algorithm - * @param g the graph - */ - public UnweightedShortestPath(Graph g) - { - mDistanceMap = new HashMap<>(); - mPredecessorMap = new HashMap<>(); - mGraph = g; - } + /** + * Constructs and initializes algorithm + * + * @param g the graph + */ + public UnweightedShortestPath(Graph g) { + mDistanceMap = new HashMap<>(); + mPredecessorMap = new HashMap<>(); + mGraph = g; + } - /** - * @see edu.uci.ics.jung.algorithms.shortestpath.Distance#getDistance(Object, Object) - */ - public Integer getDistance(N source, N target) - { - Map sourceSPMap = getDistanceMap(source); - return sourceSPMap.get(target); - } + /** @see edu.uci.ics.jung.algorithms.shortestpath.Distance#getDistance(Object, Object) */ + public Integer getDistance(N source, N target) { + Map sourceSPMap = getDistanceMap(source); + return sourceSPMap.get(target); + } - /** - * @see edu.uci.ics.jung.algorithms.shortestpath.Distance#getDistanceMap(Object) - */ - public Map getDistanceMap(N source) - { - Map sourceSPMap = mDistanceMap.get(source); - if (sourceSPMap == null) - { - computeShortestPathsFromSource(source); - sourceSPMap = mDistanceMap.get(source); - } - return sourceSPMap; - } + /** @see edu.uci.ics.jung.algorithms.shortestpath.Distance#getDistanceMap(Object) */ + public Map getDistanceMap(N source) { + Map sourceSPMap = mDistanceMap.get(source); + if (sourceSPMap == null) { + computeShortestPathsFromSource(source); + sourceSPMap = mDistanceMap.get(source); + } + return sourceSPMap; + } - /** - * @see edu.uci.ics.jung.algorithms.shortestpath.ShortestPath#getIncomingEdgeMap(Object) - */ - public Map getIncomingEdgeMap(N source) - { - Map sourceIEMap = mPredecessorMap.get(source); - if (sourceIEMap == null) - { - computeShortestPathsFromSource(source); - sourceIEMap = mPredecessorMap.get(source); - } - return sourceIEMap; - } + /** @see edu.uci.ics.jung.algorithms.shortestpath.ShortestPath#getIncomingEdgeMap(Object) */ + public Map getIncomingEdgeMap(N source) { + Map sourceIEMap = mPredecessorMap.get(source); + if (sourceIEMap == null) { + computeShortestPathsFromSource(source); + sourceIEMap = mPredecessorMap.get(source); + } + return sourceIEMap; + } + /** + * Computes the shortest path distances from a given node to all other nodes. + * + * @param source the source node + */ + private void computeShortestPathsFromSource(N source) { + BFSDistanceLabeler labeler = new BFSDistanceLabeler(); + labeler.labelDistances(mGraph, source); + distances = labeler.getDistanceDecorator(); + Map currentSourceSPMap = new HashMap(); + Map currentSourcePredMap = new HashMap(); - /** - * Computes the shortest path distances from a given node to all other nodes. - * @param source the source node - */ - private void computeShortestPathsFromSource(N source) - { - BFSDistanceLabeler labeler = new BFSDistanceLabeler(); - labeler.labelDistances(mGraph, source); - distances = labeler.getDistanceDecorator(); - Map currentSourceSPMap = new HashMap(); - Map currentSourcePredMap = new HashMap(); + for (N vertex : mGraph.nodes()) { - for(N vertex : mGraph.nodes()) { - - Integer distanceVal = distances.get(vertex); - // BFSDistanceLabeler uses -1 to indicate unreachable vertices; - // don't bother to store unreachable vertices - if (distanceVal != null && distanceVal.intValue() >= 0) - { - currentSourceSPMap.put(vertex, distanceVal); - int minDistance = distanceVal.intValue(); - for (N predecessor : mGraph.predecessors(vertex)) { - if (predecessor.equals(vertex)) - continue; + Integer distanceVal = distances.get(vertex); + // BFSDistanceLabeler uses -1 to indicate unreachable vertices; + // don't bother to store unreachable vertices + if (distanceVal != null && distanceVal.intValue() >= 0) { + currentSourceSPMap.put(vertex, distanceVal); + int minDistance = distanceVal.intValue(); + for (N predecessor : mGraph.predecessors(vertex)) { + if (predecessor.equals(vertex)) continue; - Integer predDistance = distances.get(predecessor); - if (predDistance < minDistance && predDistance >= 0) - { - minDistance = predDistance.intValue(); - currentSourcePredMap.put(vertex, predecessor); - } - - } - } - } - mDistanceMap.put(source, currentSourceSPMap); - mPredecessorMap.put(source, currentSourcePredMap); - } - - /** - * Clears all stored distances for this instance. - * Should be called whenever the graph is modified (edge weights - * changed or edges added/removed). If the user knows that - * some currently calculated distances are unaffected by a - * change, reset(V) may be appropriate instead. - * - * @see #reset(Object) - */ - public void reset() - { - mDistanceMap.clear(); - mPredecessorMap.clear(); - } - - /** - * Clears all stored distances for the specified source vertex - * source. Should be called whenever the stored distances - * from this vertex are invalidated by changes to the graph. - * - * @see #reset() - * - * @param v the vertex for which distances should be cleared - */ - public void reset(N v) - { - mDistanceMap.remove(v); - mPredecessorMap.remove(v); + Integer predDistance = distances.get(predecessor); + if (predDistance < minDistance && predDistance >= 0) { + minDistance = predDistance.intValue(); + currentSourcePredMap.put(vertex, predecessor); + } + } + } } + mDistanceMap.put(source, currentSourceSPMap); + mPredecessorMap.put(source, currentSourcePredMap); + } + + /** + * Clears all stored distances for this instance. Should be called whenever the graph is modified + * (edge weights changed or edges added/removed). If the user knows that some currently calculated + * distances are unaffected by a change, reset(V) may be appropriate instead. + * + * @see #reset(Object) + */ + public void reset() { + mDistanceMap.clear(); + mPredecessorMap.clear(); + } + + /** + * Clears all stored distances for the specified source vertex source. Should be + * called whenever the stored distances from this vertex are invalidated by changes to the graph. + * + * @see #reset() + * @param v the vertex for which distances should be cleared + */ + public void reset(N v) { + mDistanceMap.remove(v); + mPredecessorMap.remove(v); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/transformation/FoldingTransformer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/transformation/FoldingTransformer.java index 26d1809f..730bb62f 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/transformation/FoldingTransformer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/transformation/FoldingTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, The JUNG Authors + * Copyright (c) 2003, The JUNG Authors * * All rights reserved. * @@ -12,31 +12,27 @@ */ package edu.uci.ics.jung.algorithms.transformation; -import java.util.LinkedHashSet; -import java.util.Set; - import com.google.common.base.Preconditions; import com.google.common.graph.Graph; import com.google.common.graph.GraphBuilder; import com.google.common.graph.MutableGraph; import com.google.common.graph.MutableValueGraph; import com.google.common.graph.ValueGraphBuilder; +import java.util.LinkedHashSet; +import java.util.Set; /** - * Methods for creating a "folded" graph based on an input graph.graph or a - * hypergraph. - * - *

    A "folded" graph is derived from a k-partite graph by identifying - * a partition of vertices which will become the vertices of the new graph, copying - * these vertices into the new graph, and then connecting those vertices whose - * original analogues were connected indirectly through elements - * of other partitions. - * - *

    A "folded" graph is derived from a hypergraph by creating vertices based on - * either the vertices or the hyperedges of the original graph, and connecting - * vertices in the new graph if their corresponding vertices/hyperedges share a - * connection with a common hyperedge/vertex. - * + * Methods for creating a "folded" graph based on an input graph.graph or a hypergraph. + * + *

    A "folded" graph is derived from a k-partite graph by identifying a partition of vertices + * which will become the vertices of the new graph, copying these vertices into the new graph, and + * then connecting those vertices whose original analogues were connected indirectly through + * elements of other partitions. + * + *

    A "folded" graph is derived from a hypergraph by creating vertices based on either the + * vertices or the hyperedges of the original graph, and connecting vertices in the new graph if + * their corresponding vertices/hyperedges share a connection with a common hyperedge/vertex. + * * @author Danyel Fisher * @author Joshua O'Madadhain */ @@ -44,98 +40,89 @@ // see JUNG 2.1 source for this file for concepts: // * nodes -> nodes, replace hyperedges by k-cliques on incident nodes // * hyperedges -> nodes, (a,b) exists in new graph if a and b share a node -public class FoldingTransformer -{ - - /** - * Converts g into a unipartite graph whose vertex set is the - * vertices of g's partition p. For vertices - * a and b in this partition, the resultant - * graph will include the edge (a,b) if the original graph - * contains edges (a,c) and (c,b) for at least - * one vertex c. - * - *

    The vertices of the new graph are the same as the vertices of the - * appropriate partition in the old graph. - * - *

    This function will not create self-loops. - * - * @param vertex type - * @param input edge type - * @param g input graph - * @param nodes input node set - */ - // TODO: consider providing ValueGraph/Network versions of this - // TODO: consider renaming this - public static MutableGraph foldToGraph(Graph g, Set nodes) - { - Preconditions.checkArgument(g.nodes().containsAll(nodes), - "Input graph must contain all specified nodes"); - MutableGraph newGraph = GraphBuilder.from(g).expectedNodeCount(nodes.size()).build(); +public class FoldingTransformer { + + /** + * Converts g into a unipartite graph whose vertex set is the vertices of g + * 's partition p. For vertices a and b in this + * partition, the resultant graph will include the edge (a,b) if the original graph + * contains edges (a,c) and (c,b) for at least one vertex c + * . + * + *

    The vertices of the new graph are the same as the vertices of the appropriate partition in + * the old graph. + * + *

    This function will not create self-loops. + * + * @param vertex type + * @param input edge type + * @param g input graph + * @param nodes input node set + */ + // TODO: consider providing ValueGraph/Network versions of this + // TODO: consider renaming this + public static MutableGraph foldToGraph(Graph g, Set nodes) { + Preconditions.checkArgument( + g.nodes().containsAll(nodes), "Input graph must contain all specified nodes"); + MutableGraph newGraph = GraphBuilder.from(g).expectedNodeCount(nodes.size()).build(); - for (N node : nodes) { - for (N s : g.successors(node)) { - for (N t : g.successors(s)) { - if (!nodes.contains(t) || t.equals(node)) { - continue; - } - newGraph.putEdge(node, t); - } - } + for (N node : nodes) { + for (N s : g.successors(node)) { + for (N t : g.successors(s)) { + if (!nodes.contains(t) || t.equals(node)) { + continue; + } + newGraph.putEdge(node, t); } - return newGraph; + } } + return newGraph; + } - /** - * Converts g into a unipartite graph whose vertex set is the - * vertices of g's partition p. For vertices - * a and b in this partition, the resultant - * graph will include the edge (a,b) if the original graph - * contains edges (a,c) and (c,b) for at least - * one vertex c. - * - *

    The vertices of the new graph are the same as the vertices of the - * appropriate partition in the input graph. The edge values are the sets of - * nodes that connected the edge's endpoints in the input graph. - * - *

    This function will not create self-loops. - * - * @param vertex type - * @param input edge type - * @param g input graph - * @param nodes input node set - */ - // TODO: consider providing ValueGraph/Network versions of this - // TODO: consider renaming this - public static MutableValueGraph> foldToValueGraph(Graph g, Set nodes) - { - Preconditions.checkArgument(g.nodes().containsAll(nodes), - "Input graph must contain all specified nodes"); - ValueGraphBuilder builder = g.isDirected() - ? ValueGraphBuilder.directed() - : ValueGraphBuilder.undirected(); - MutableValueGraph> newGraph = builder - .expectedNodeCount(nodes.size()) - .nodeOrder(g.nodeOrder()) - .build(); + /** + * Converts g into a unipartite graph whose vertex set is the vertices of g + * 's partition p. For vertices a and b in this + * partition, the resultant graph will include the edge (a,b) if the original graph + * contains edges (a,c) and (c,b) for at least one vertex c + * . + * + *

    The vertices of the new graph are the same as the vertices of the appropriate partition in + * the input graph. The edge values are the sets of nodes that connected the edge's endpoints in + * the input graph. + * + *

    This function will not create self-loops. + * + * @param vertex type + * @param input edge type + * @param g input graph + * @param nodes input node set + */ + // TODO: consider providing ValueGraph/Network versions of this + // TODO: consider renaming this + public static MutableValueGraph> foldToValueGraph(Graph g, Set nodes) { + Preconditions.checkArgument( + g.nodes().containsAll(nodes), "Input graph must contain all specified nodes"); + ValueGraphBuilder builder = + g.isDirected() ? ValueGraphBuilder.directed() : ValueGraphBuilder.undirected(); + MutableValueGraph> newGraph = + builder.expectedNodeCount(nodes.size()).nodeOrder(g.nodeOrder()).build(); - for (N node : nodes) { - for (N s : g.successors(node)) { - for (N t : g.successors(s)) { - if (!nodes.contains(t) || t.equals(node)) { - continue; - } - // TODO: consider having the type of Set depend on - // the input graph's node order - Set intermediateNodes = - newGraph.edgeValueOrDefault(node, t, new LinkedHashSet()); - if (intermediateNodes.isEmpty()) { - newGraph.putEdgeValue(node, t, intermediateNodes); - } - intermediateNodes.add(s); - } - } + for (N node : nodes) { + for (N s : g.successors(node)) { + for (N t : g.successors(s)) { + if (!nodes.contains(t) || t.equals(node)) { + continue; + } + // TODO: consider having the type of Set depend on + // the input graph's node order + Set intermediateNodes = newGraph.edgeValueOrDefault(node, t, new LinkedHashSet()); + if (intermediateNodes.isEmpty()) { + newGraph.putEdgeValue(node, t, intermediateNodes); + } + intermediateNodes.add(s); } - return newGraph; + } } -} \ No newline at end of file + return newGraph; + } +} diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/transformation/VertexPartitionCollapser.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/transformation/VertexPartitionCollapser.java index a3f0ce55..0e678a28 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/transformation/VertexPartitionCollapser.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/transformation/VertexPartitionCollapser.java @@ -1,74 +1,69 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.transformation; -import java.util.Map; -import java.util.Set; - import com.google.common.graph.EndpointPair; import com.google.common.graph.Graph; import com.google.common.graph.MutableValueGraph; import com.google.common.graph.ValueGraph; import com.google.common.graph.ValueGraphBuilder; - import edu.uci.ics.jung.algorithms.blockmodel.VertexPartition; +import java.util.Map; +import java.util.Set; /** - * This class transforms a graph with a known vertex partitioning into a graph whose - * vertices correspond to the input graph's partitions - * + * This class transforms a graph with a known vertex partitioning into a graph whose vertices + * correspond to the input graph's partitions + * *

    Concept based on Danyel Fisher's GraphCollapser in JUNG 1.x. */ // TODO: add tests -public class VertexPartitionCollapser -{ - /** - * Creates a new graph whose vertices correspond to the partitions of the supplied graph. - * Two nodes u and v in the collapsed graph will be connected if there is an edge between - * any of the nodes in u and any of the nodes in v, and u and v are distinct. The value - * of the edge represents the number of such edges. - * @param partitioning a vertex partition of a graph - * @return the collapsed graph - */ - public static ValueGraph, Integer> collapseVertexPartitions( - VertexPartition partitioning) - { - Graph original = partitioning.getGraph(); - ValueGraphBuilder builder = original.isDirected() - ? ValueGraphBuilder.directed() - : ValueGraphBuilder.undirected(); - MutableValueGraph, Integer> collapsed = builder.build(); - - // create vertices in new graph corresponding to equivalence sets in the original graph - for (Set set : partitioning.getVertexPartitions()) - { - collapsed.addNode(set); - } +public class VertexPartitionCollapser { + /** + * Creates a new graph whose vertices correspond to the partitions of the supplied graph. Two + * nodes u and v in the collapsed graph will be connected if there is an edge between any of the + * nodes in u and any of the nodes in v, and u and v are distinct. The value of the edge + * represents the number of such edges. + * + * @param partitioning a vertex partition of a graph + * @return the collapsed graph + */ + public static ValueGraph, Integer> collapseVertexPartitions( + VertexPartition partitioning) { + Graph original = partitioning.getGraph(); + ValueGraphBuilder builder = + original.isDirected() ? ValueGraphBuilder.directed() : ValueGraphBuilder.undirected(); + MutableValueGraph, Integer> collapsed = builder.build(); + + // create vertices in new graph corresponding to equivalence sets in the original graph + for (Set set : partitioning.getVertexPartitions()) { + collapsed.addNode(set); + } - // for each pair of endpoints in the original graph, connect the corresponding nodes - // (representing partitions) in the collapsed graph if the partitions are different - Map> nodeToPartition = partitioning.getVertexToPartitionMap(); - for (EndpointPair endpoints : original.edges()) { - V nodeU = endpoints.nodeU(); - V nodeV = endpoints.nodeV(); - Set partitionU = nodeToPartition.get(nodeU); - Set partitionV = nodeToPartition.get(nodeV); - if (nodeU.equals(nodeV) || partitionU.equals(partitionV)) { - // we only connect partitions if the partitions are different; - // check the nodes first as an optimization - continue; - } + // for each pair of endpoints in the original graph, connect the corresponding nodes + // (representing partitions) in the collapsed graph if the partitions are different + Map> nodeToPartition = partitioning.getVertexToPartitionMap(); + for (EndpointPair endpoints : original.edges()) { + V nodeU = endpoints.nodeU(); + V nodeV = endpoints.nodeV(); + Set partitionU = nodeToPartition.get(nodeU); + Set partitionV = nodeToPartition.get(nodeV); + if (nodeU.equals(nodeV) || partitionU.equals(partitionV)) { + // we only connect partitions if the partitions are different; + // check the nodes first as an optimization + continue; + } - int edgeCount = collapsed.edgeValueOrDefault(partitionU, partitionV, 0); - collapsed.putEdgeValue(partitionU, partitionV, edgeCount + 1); - } - return collapsed; + int edgeCount = collapsed.edgeValueOrDefault(partitionU, partitionV, 0); + collapsed.putEdgeValue(partitionU, partitionV, edgeCount + 1); } + return collapsed; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/BasicMapEntry.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/BasicMapEntry.java index c59ed9b4..1b972c3a 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/BasicMapEntry.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/BasicMapEntry.java @@ -9,73 +9,64 @@ * @param the key type * @param the value type */ -public class BasicMapEntry implements Map.Entry { - final K key; - V value; - - /** - * @param k the key - * @param v the value - */ - public BasicMapEntry(K k, V v) { - value = v; - key = k; - } +public class BasicMapEntry implements Map.Entry { + final K key; + V value; - public K getKey() { - return key; - } + /** + * @param k the key + * @param v the value + */ + public BasicMapEntry(K k, V v) { + value = v; + key = k; + } - public V getValue() { - return value; - } + public K getKey() { + return key; + } - public V setValue(V newValue) { + public V getValue() { + return value; + } + + public V setValue(V newValue) { V oldValue = value; - value = newValue; - return oldValue; - } + value = newValue; + return oldValue; + } - @Override - public boolean equals(Object o) { - if (!(o instanceof Map.Entry)) - return false; - @SuppressWarnings("rawtypes") - Map.Entry e = (Map.Entry)o; - Object k1 = getKey(); - Object k2 = e.getKey(); - if (k1 == k2 || (k1 != null && k1.equals(k2))) { - Object v1 = getValue(); - Object v2 = e.getValue(); - if (v1 == v2 || (v1 != null && v1.equals(v2))) - return true; - } - return false; + @Override + public boolean equals(Object o) { + if (!(o instanceof Map.Entry)) return false; + @SuppressWarnings("rawtypes") + Map.Entry e = (Map.Entry) o; + Object k1 = getKey(); + Object k2 = e.getKey(); + if (k1 == k2 || (k1 != null && k1.equals(k2))) { + Object v1 = getValue(); + Object v2 = e.getValue(); + if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; } + return false; + } - @Override - public int hashCode() { - return (key==null ? 0 : key.hashCode()) ^ - (value==null ? 0 : value.hashCode()); - } + @Override + public int hashCode() { + return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); + } - @Override - public String toString() { - return getKey() + "=" + getValue(); - } + @Override + public String toString() { + return getKey() + "=" + getValue(); + } - /** - * This method is invoked whenever the value in an entry is - * overwritten by an invocation of put(k,v) for a key k that's already - * in the HashMap. - */ - void recordAccess(HashMap m) { - } + /** + * This method is invoked whenever the value in an entry is overwritten by an invocation of + * put(k,v) for a key k that's already in the HashMap. + */ + void recordAccess(HashMap m) {} - /** - * This method is invoked whenever the entry is - * removed from the table. - */ - void recordRemoval(HashMap m) { - } + /** This method is invoked whenever the entry is removed from the table. */ + void recordRemoval(HashMap m) {} } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/DiscreteDistribution.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/DiscreteDistribution.java index d0ed62ac..14c2bc06 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/DiscreteDistribution.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/DiscreteDistribution.java @@ -1,219 +1,191 @@ /* - * Copyright (c) 2003, The JUNG Authors + * Copyright (c) 2003, The JUNG Authors * * All rights reserved. * * This software is open-source under the BSD license; see either * "license.txt" or * https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * * Created on Feb 18, 2004 */ package edu.uci.ics.jung.algorithms.util; +import com.google.common.base.Preconditions; import java.util.Collection; import java.util.Iterator; -import com.google.common.base.Preconditions; - /** - * A utility class for calculating properties of discrete distributions. - * Generally, these distributions are represented as arrays of - * double values, which are assumed to be normalized - * such that the entries in a single array sum to 1. - * + * A utility class for calculating properties of discrete distributions. Generally, these + * distributions are represented as arrays of double values, which are assumed to be + * normalized such that the entries in a single array sum to 1. + * * @author Joshua O'Madadhain */ -public class DiscreteDistribution -{ - - /** - * Returns the Kullback-Leibler divergence between the - * two specified distributions, which must have the same - * number of elements. This is defined as - * the sum over all i of - * dist[i] * Math.log(dist[i] / reference[i]). - * Note that this value is not symmetric; see - * symmetricKL for a symmetric variant. - * @see #symmetricKL(double[], double[]) - * @param dist the distribution whose divergence from {@code reference} is being measured - * @param reference the reference distribution - * @return sum_i of {@code dist[i] * Math.log(dist[i] / reference[i])} - */ - public static double KullbackLeibler(double[] dist, double[] reference) - { - double distance = 0; - - Preconditions.checkArgument(dist.length == reference.length, - "input arrays must be of the same length"); - - for (int i = 0; i < dist.length; i++) - { - if (dist[i] > 0 && reference[i] > 0) - distance += dist[i] * Math.log(dist[i] / reference[i]); - } - return distance; - } - - /** - * @param dist the distribution whose divergence from {@code reference} is being measured - * @param reference the reference distribution - * @return KullbackLeibler(dist, reference) + KullbackLeibler(reference, dist) - * @see #KullbackLeibler(double[], double[]) - */ - public static double symmetricKL(double[] dist, double[] reference) - { - return KullbackLeibler(dist, reference) - + KullbackLeibler(reference, dist); +public class DiscreteDistribution { + + /** + * Returns the Kullback-Leibler divergence between the two specified distributions, which must + * have the same number of elements. This is defined as the sum over all i of + * dist[i] * Math.log(dist[i] / reference[i]). Note that this value is not symmetric; see + * symmetricKL for a symmetric variant. + * + * @see #symmetricKL(double[], double[]) + * @param dist the distribution whose divergence from {@code reference} is being measured + * @param reference the reference distribution + * @return sum_i of {@code dist[i] * Math.log(dist[i] / reference[i])} + */ + public static double KullbackLeibler(double[] dist, double[] reference) { + double distance = 0; + + Preconditions.checkArgument( + dist.length == reference.length, "input arrays must be of the same length"); + + for (int i = 0; i < dist.length; i++) { + if (dist[i] > 0 && reference[i] > 0) distance += dist[i] * Math.log(dist[i] / reference[i]); } - - /** - * Returns the squared difference between the - * two specified distributions, which must have the same - * number of elements. This is defined as - * the sum over all i of the square of - * (dist[i] - reference[i]). - * @param dist the distribution whose distance from {@code reference} is being measured - * @param reference the reference distribution - * @return sum_i {@code (dist[i] - reference[i])^2} - */ - public static double squaredError(double[] dist, double[] reference) - { - double error = 0; - - Preconditions.checkArgument(dist.length == reference.length, - "input arrays must be of the same length"); - - for (int i = 0; i < dist.length; i++) - { - double difference = dist[i] - reference[i]; - error += difference * difference; - } - return error; + return distance; + } + + /** + * @param dist the distribution whose divergence from {@code reference} is being measured + * @param reference the reference distribution + * @return KullbackLeibler(dist, reference) + KullbackLeibler(reference, dist) + * @see #KullbackLeibler(double[], double[]) + */ + public static double symmetricKL(double[] dist, double[] reference) { + return KullbackLeibler(dist, reference) + KullbackLeibler(reference, dist); + } + + /** + * Returns the squared difference between the two specified distributions, which must have the + * same number of elements. This is defined as the sum over all i of the square of + * (dist[i] - reference[i]). + * + * @param dist the distribution whose distance from {@code reference} is being measured + * @param reference the reference distribution + * @return sum_i {@code (dist[i] - reference[i])^2} + */ + public static double squaredError(double[] dist, double[] reference) { + double error = 0; + + Preconditions.checkArgument( + dist.length == reference.length, "input arrays must be of the same length"); + + for (int i = 0; i < dist.length; i++) { + double difference = dist[i] - reference[i]; + error += difference * difference; } - - /** - * Returns the cosine distance between the two - * specified distributions, which must have the same number - * of elements. The distributions are treated as vectors - * in dist.length-dimensional space. - * Given the following definitions - *

      - *
    • v = the sum over all i of dist[i] * dist[i] - *
    • w = the sum over all i of reference[i] * reference[i] - *
    • vw = the sum over all i of dist[i] * reference[i] - *
    - * the value returned is defined as vw / (Math.sqrt(v) * Math.sqrt(w)). - * @param dist the distribution whose distance from {@code reference} is being measured - * @param reference the reference distribution - * @return the cosine distance between {@code dist} and {@code reference}, considered as vectors - */ - public static double cosine(double[] dist, double[] reference) - { - double v_prod = 0; // dot product x*x - double w_prod = 0; // dot product y*y - double vw_prod = 0; // dot product x*y - - Preconditions.checkArgument(dist.length == reference.length, - "input arrays must be of the same length"); - - for (int i = 0; i < dist.length; i++) - { - vw_prod += dist[i] * reference[i]; - v_prod += dist[i] * dist[i]; - w_prod += reference[i] * reference[i]; - } - // cosine distance between v and w - return vw_prod / (Math.sqrt(v_prod) * Math.sqrt(w_prod)); - } - - /** - * Returns the entropy of this distribution. - * High entropy indicates that the distribution is - * close to uniform; low entropy indicates that the - * distribution is close to a Dirac delta (i.e., if - * the probability mass is concentrated at a single - * point, this method returns 0). Entropy is defined as - * the sum over all i of - * -(dist[i] * Math.log(dist[i])) - * - * @param dist the distribution whose entropy is being measured - * @return sum_i {@code -(dist[i] * Math.log(dist[i]))} - */ - public static double entropy(double[] dist) - { - double total = 0; - - for (int i = 0; i < dist.length; i++) - { - if (dist[i] > 0) - total += dist[i] * Math.log(dist[i]); - } - return -total; - } - - /** - * Normalizes, with Lagrangian smoothing, the specified double - * array, so that the values sum to 1 (i.e., can be treated as probabilities). - * The effect of the Lagrangian smoothing is to ensure that all entries - * are nonzero; effectively, a value of alpha is added to each - * entry in the original array prior to normalization. - * @param counts the array to be converted into a probability distribution - * @param alpha the value to add to each entry prior to normalization - */ - public static void normalize(double[] counts, double alpha) - { - double total_count = 0; - - for (int i = 0; i < counts.length; i++) - total_count += counts[i]; - - for (int i = 0; i < counts.length; i++) - counts[i] = (counts[i] + alpha) - / (total_count + counts.length * alpha); - } - - /** - * Returns the mean of the specified Collection of - * distributions, which are assumed to be normalized arrays of - * double values. - * @see #mean(double[][]) - * @param distributions the distributions whose mean is to be calculated - * @return the mean of the distributions - */ - public static double[] mean(Collection distributions) - { - if (distributions.isEmpty()) - throw new IllegalArgumentException("Distribution collection must be non-empty"); - Iterator iter = distributions.iterator(); - double[] first = iter.next(); - double[][] d_array = new double[distributions.size()][first.length]; - d_array[0] = first; - for (int i = 1; i < d_array.length; i++) - d_array[i] = iter.next(); - - return mean(d_array); + return error; + } + + /** + * Returns the cosine distance between the two specified distributions, which must have the same + * number of elements. The distributions are treated as vectors in dist.length + * -dimensional space. Given the following definitions + * + *
      + *
    • v = the sum over all i of dist[i] * dist[i] + *
    • w = the sum over all i of reference[i] * reference[i] + * + *
    • vw = the sum over all i of dist[i] * reference[i] + *
    + * + * the value returned is defined as vw / (Math.sqrt(v) * Math.sqrt(w)). + * + * @param dist the distribution whose distance from {@code reference} is being measured + * @param reference the reference distribution + * @return the cosine distance between {@code dist} and {@code reference}, considered as vectors + */ + public static double cosine(double[] dist, double[] reference) { + double v_prod = 0; // dot product x*x + double w_prod = 0; // dot product y*y + double vw_prod = 0; // dot product x*y + + Preconditions.checkArgument( + dist.length == reference.length, "input arrays must be of the same length"); + + for (int i = 0; i < dist.length; i++) { + vw_prod += dist[i] * reference[i]; + v_prod += dist[i] * dist[i]; + w_prod += reference[i] * reference[i]; } - - /** - * Returns the mean of the specified array of distributions, - * represented as normalized arrays of double values. - * Will throw an "index out of bounds" exception if the - * distribution arrays are not all of the same length. - * @param distributions the distributions whose mean is to be calculated - * @return the mean of the distributions - */ - public static double[] mean(double[][] distributions) - { - double[] d_mean = new double[distributions[0].length]; - for (int j = 0; j < d_mean.length; j++) - d_mean[j] = 0; - - for (int i = 0; i < distributions.length; i++) - for (int j = 0; j < d_mean.length; j++) - d_mean[j] += distributions[i][j] / distributions.length; - - return d_mean; + // cosine distance between v and w + return vw_prod / (Math.sqrt(v_prod) * Math.sqrt(w_prod)); + } + + /** + * Returns the entropy of this distribution. High entropy indicates that the distribution is close + * to uniform; low entropy indicates that the distribution is close to a Dirac delta (i.e., if the + * probability mass is concentrated at a single point, this method returns 0). Entropy is defined + * as the sum over all i of -(dist[i] * Math.log(dist[i])) + * + * @param dist the distribution whose entropy is being measured + * @return sum_i {@code -(dist[i] * Math.log(dist[i]))} + */ + public static double entropy(double[] dist) { + double total = 0; + + for (int i = 0; i < dist.length; i++) { + if (dist[i] > 0) total += dist[i] * Math.log(dist[i]); } - -} \ No newline at end of file + return -total; + } + + /** + * Normalizes, with Lagrangian smoothing, the specified double array, so that the + * values sum to 1 (i.e., can be treated as probabilities). The effect of the Lagrangian smoothing + * is to ensure that all entries are nonzero; effectively, a value of alpha is added + * to each entry in the original array prior to normalization. + * + * @param counts the array to be converted into a probability distribution + * @param alpha the value to add to each entry prior to normalization + */ + public static void normalize(double[] counts, double alpha) { + double total_count = 0; + + for (int i = 0; i < counts.length; i++) total_count += counts[i]; + + for (int i = 0; i < counts.length; i++) + counts[i] = (counts[i] + alpha) / (total_count + counts.length * alpha); + } + + /** + * Returns the mean of the specified Collection of distributions, which are assumed + * to be normalized arrays of double values. + * + * @see #mean(double[][]) + * @param distributions the distributions whose mean is to be calculated + * @return the mean of the distributions + */ + public static double[] mean(Collection distributions) { + if (distributions.isEmpty()) + throw new IllegalArgumentException("Distribution collection must be non-empty"); + Iterator iter = distributions.iterator(); + double[] first = iter.next(); + double[][] d_array = new double[distributions.size()][first.length]; + d_array[0] = first; + for (int i = 1; i < d_array.length; i++) d_array[i] = iter.next(); + + return mean(d_array); + } + + /** + * Returns the mean of the specified array of distributions, represented as normalized arrays of + * double values. Will throw an "index out of bounds" exception if the distribution + * arrays are not all of the same length. + * + * @param distributions the distributions whose mean is to be calculated + * @return the mean of the distributions + */ + public static double[] mean(double[][] distributions) { + double[] d_mean = new double[distributions[0].length]; + for (int j = 0; j < d_mean.length; j++) d_mean[j] = 0; + + for (int i = 0; i < distributions.length; i++) + for (int j = 0; j < d_mean.length; j++) + d_mean[j] += distributions[i][j] / distributions.length; + + return d_mean; + } +} diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/Indexer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/Indexer.java index c41ed453..53c56a91 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/Indexer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/Indexer.java @@ -1,56 +1,53 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.util; -import java.util.Collection; - import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import java.util.Collection; /** - * A class providing static methods useful for improving the - * performance of graph algorithms. - * - * @author Tom Nelson + * A class providing static methods useful for improving the performance of graph algorithms. * + * @author Tom Nelson */ public class Indexer { - - /** - * Returns a BiMap mapping each element of the collection to its - * index as encountered while iterating over the collection. The purpose - * of the index operation is to supply an O(1) replacement operation for the - * O(n) indexOf(element) method of a List - * @param the type of the collection elements - * @param collection the collection whose indices are to be generated - * @return a bidirectional map from collection elements to 0-based indices - */ - public static BiMap create(Collection collection) { - return create(collection, 0); - } - /** - * Returns a BiMap mapping each element of the collection to its - * index as encountered while iterating over the collection. The purpose - * of the index operation is to supply an O(1) replacement operation for the - * O(n) indexOf(element) method of a List - * @param the type of the collection elements - * @param collection the collection whose indices are to be generated - * @param start start index - * @return a bidirectional map from collection elements to start-based indices - */ - public static BiMap create(Collection collection, int start) { - BiMap map = HashBiMap.create(); - int i=start; - for(T t : collection) { - map.put(t,i++); - } - return map; - } + + /** + * Returns a BiMap mapping each element of the collection to its index as encountered + * while iterating over the collection. The purpose of the index operation is to supply an O(1) + * replacement operation for the O(n) indexOf(element) method of a List + * + * @param the type of the collection elements + * @param collection the collection whose indices are to be generated + * @return a bidirectional map from collection elements to 0-based indices + */ + public static BiMap create(Collection collection) { + return create(collection, 0); + } + /** + * Returns a BiMap mapping each element of the collection to its index as encountered + * while iterating over the collection. The purpose of the index operation is to supply an O(1) + * replacement operation for the O(n) indexOf(element) method of a List + * + * @param the type of the collection elements + * @param collection the collection whose indices are to be generated + * @param start start index + * @return a bidirectional map from collection elements to start-based indices + */ + public static BiMap create(Collection collection, int start) { + BiMap map = HashBiMap.create(); + int i = start; + for (T t : collection) { + map.put(t, i++); + } + return map; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/IterativeContext.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/IterativeContext.java index de719627..010280e4 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/IterativeContext.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/IterativeContext.java @@ -1,28 +1,19 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ -package edu.uci.ics.jung.algorithms.util; - - -/** - * An interface for algorithms that proceed iteratively. + * Copyright (c) 2003, The JUNG Authors * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. */ -public interface IterativeContext -{ - /** - * Advances one step. - */ - void step(); +package edu.uci.ics.jung.algorithms.util; + +/** An interface for algorithms that proceed iteratively. */ +public interface IterativeContext { + /** Advances one step. */ + void step(); - /** - * @return {@code true} if this iterative process is finished, and {@code false} otherwise. - */ - boolean done(); + /** @return {@code true} if this iterative process is finished, and {@code false} otherwise. */ + boolean done(); } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/IterativeProcess.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/IterativeProcess.java index 67749e70..7ac32d5c 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/IterativeProcess.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/IterativeProcess.java @@ -1,26 +1,26 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.util; - - /** * Provides basic infrastructure for iterative algorithms. Services provided include: + * *
      - *
    • storage of current and max iteration count
    • - *
    • framework for initialization, iterative evaluation, and finalization
    • - *
    • test for convergence
    • - *
    • etc.
    • + *
    • storage of current and max iteration count + *
    • framework for initialization, iterative evaluation, and finalization + *
    • test for convergence + *
    • etc. *
    - *

    - * Algorithms that subclass this class are typically used in the following way:
    + * + *

    Algorithms that subclass this class are typically used in the following way:
    + * *

      * FooAlgorithm foo = new FooAlgorithm(...)
      * foo.setMaximumIterations(100); //set up conditions
    @@ -28,147 +28,106 @@
      * foo.evaluate(); //key method which initiates iterative process
      * foo.getSomeResult();
      * 
    - * + * * @author Scott White (originally written by Didier Besset) */ public abstract class IterativeProcess implements IterativeContext { - /** - * Number of iterations performed. - */ - private int iterations; - /** - * Maximum allowed number of iterations. - */ - private int maximumIterations = 50; - /** - * Desired precision. - */ - private double desiredPrecision = Double.MIN_VALUE; - /** - * Achieved precision. - */ - private double precision; - - - /** - * Generic constructor. - */ - public IterativeProcess() { - } - - /** - * Performs the iterative process. - * Note: this method does not return anything because Java does not - * allow mixing double, int, or objects - */ - public void evaluate() { - iterations = 0; - initializeIterations(); - while (iterations++ < maximumIterations) { - step(); - precision = getPrecision(); - if (hasConverged()) - break; - } - finalizeIterations(); - } - - /** - * Evaluate the result of the current iteration. - */ - abstract public void step(); - - /** - * Perform eventual clean-up operations - * (must be implement by subclass when needed). - */ - protected void finalizeIterations() { - } - - /** - * @return the desired precision. - */ - public double getDesiredPrecision() { - return desiredPrecision; - } - - /** - * @return the number of iterations performed. - */ - public int getIterations() { - return iterations; - } - - /** - * @return the maximum allowed number of iterations. - */ - public int getMaximumIterations() { - return maximumIterations; - } - - /** - * @return the attained precision. - */ - public double getPrecision() { - return precision; - } - - /** - * @param precision the precision to set - */ - public void setPrecision(double precision) { - this.precision = precision; - } - - /** - * - * Check to see if the result has been attained. - * @return boolean - */ - public boolean hasConverged() { - return precision < desiredPrecision; - } - - public boolean done() { - return hasConverged(); - } - - /** - * Initializes internal parameters to start the iterative process. - */ - protected void initializeIterations() { - } - - /** - * - */ - public void reset() { - } - - /** - * @return double - * @param epsilon double - * @param x double - */ - public double relativePrecision(double epsilon, double x) { - return x > desiredPrecision ? epsilon / x: epsilon; - } - - /** - * @param prec the desired precision. - */ - public void setDesiredPrecision(double prec) throws IllegalArgumentException { - if (prec <= 0) - throw new IllegalArgumentException("Non-positive precision: " + prec); - desiredPrecision = prec; - } - - /** - * @param maxIter the maximum allowed number of iterations - */ - public void setMaximumIterations(int maxIter) throws IllegalArgumentException { - if (maxIter < 1) - throw new IllegalArgumentException("Non-positive maximum iteration: " + maxIter); - maximumIterations = maxIter; + /** Number of iterations performed. */ + private int iterations; + /** Maximum allowed number of iterations. */ + private int maximumIterations = 50; + /** Desired precision. */ + private double desiredPrecision = Double.MIN_VALUE; + /** Achieved precision. */ + private double precision; + + /** Generic constructor. */ + public IterativeProcess() {} + + /** + * Performs the iterative process. Note: this method does not return anything because Java does + * not allow mixing double, int, or objects + */ + public void evaluate() { + iterations = 0; + initializeIterations(); + while (iterations++ < maximumIterations) { + step(); + precision = getPrecision(); + if (hasConverged()) break; } -} \ No newline at end of file + finalizeIterations(); + } + + /** Evaluate the result of the current iteration. */ + public abstract void step(); + + /** Perform eventual clean-up operations (must be implement by subclass when needed). */ + protected void finalizeIterations() {} + + /** @return the desired precision. */ + public double getDesiredPrecision() { + return desiredPrecision; + } + + /** @return the number of iterations performed. */ + public int getIterations() { + return iterations; + } + + /** @return the maximum allowed number of iterations. */ + public int getMaximumIterations() { + return maximumIterations; + } + + /** @return the attained precision. */ + public double getPrecision() { + return precision; + } + + /** @param precision the precision to set */ + public void setPrecision(double precision) { + this.precision = precision; + } + + /** + * Check to see if the result has been attained. + * + * @return boolean + */ + public boolean hasConverged() { + return precision < desiredPrecision; + } + + public boolean done() { + return hasConverged(); + } + + /** Initializes internal parameters to start the iterative process. */ + protected void initializeIterations() {} + + /** */ + public void reset() {} + + /** + * @return double + * @param epsilon double + * @param x double + */ + public double relativePrecision(double epsilon, double x) { + return x > desiredPrecision ? epsilon / x : epsilon; + } + + /** @param prec the desired precision. */ + public void setDesiredPrecision(double prec) throws IllegalArgumentException { + if (prec <= 0) throw new IllegalArgumentException("Non-positive precision: " + prec); + desiredPrecision = prec; + } + + /** @param maxIter the maximum allowed number of iterations */ + public void setMaximumIterations(int maxIter) throws IllegalArgumentException { + if (maxIter < 1) + throw new IllegalArgumentException("Non-positive maximum iteration: " + maxIter); + maximumIterations = maxIter; + } +} diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/KMeansClusterer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/KMeansClusterer.java index bfc7e52b..d4679fc1 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/KMeansClusterer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/KMeansClusterer.java @@ -23,235 +23,199 @@ import java.util.Random; import java.util.Set; - - /** - * Groups items into a specified number of clusters, based on their proximity in - * d-dimensional space, using the k-means algorithm. Calls to - * cluster will terminate when either of the two following - * conditions is true: + * Groups items into a specified number of clusters, based on their proximity in d-dimensional + * space, using the k-means algorithm. Calls to cluster will terminate when either of + * the two following conditions is true: + * *
      - *
    • the number of iterations is > max_iterations - *
    • none of the centroids has moved as much as convergence_threshold - * since the previous iteration + *
    • the number of iterations is > max_iterations + *
    • none of the centroids has moved as much as convergence_threshold since the + * previous iteration *
    - * + * * @author Joshua O'Madadhain */ -public class KMeansClusterer -{ - protected int max_iterations; - protected double convergence_threshold; - protected Random rand; - - /** - * Creates an instance which will terminate when either the maximum number of - * iterations has been reached, or all changes are smaller than the convergence threshold. - * @param max_iterations the maximum number of iterations to employ - * @param convergence_threshold the smallest change we want to track - */ - public KMeansClusterer(int max_iterations, double convergence_threshold) - { - this.max_iterations = max_iterations; - this.convergence_threshold = convergence_threshold; - this.rand = new Random(); +public class KMeansClusterer { + protected int max_iterations; + protected double convergence_threshold; + protected Random rand; + + /** + * Creates an instance which will terminate when either the maximum number of iterations has been + * reached, or all changes are smaller than the convergence threshold. + * + * @param max_iterations the maximum number of iterations to employ + * @param convergence_threshold the smallest change we want to track + */ + public KMeansClusterer(int max_iterations, double convergence_threshold) { + this.max_iterations = max_iterations; + this.convergence_threshold = convergence_threshold; + this.rand = new Random(); + } + + /** Creates an instance with max iterations of 100 and convergence threshold of 0.001. */ + public KMeansClusterer() { + this(100, 0.001); + } + + /** @return the maximum number of iterations */ + public int getMaxIterations() { + return max_iterations; + } + + /** @param max_iterations the maximum number of iterations */ + public void setMaxIterations(int max_iterations) { + if (max_iterations < 0) throw new IllegalArgumentException("max iterations must be >= 0"); + + this.max_iterations = max_iterations; + } + + /** @return the convergence threshold */ + public double getConvergenceThreshold() { + return convergence_threshold; + } + + /** @param convergence_threshold the convergence threshold */ + public void setConvergenceThreshold(double convergence_threshold) { + if (convergence_threshold <= 0) + throw new IllegalArgumentException("convergence threshold " + "must be > 0"); + + this.convergence_threshold = convergence_threshold; + } + + /** + * Returns a Collection of clusters, where each cluster is represented as a Map + * of Objects to locations in d-dimensional space. + * + * @param object_locations a map of the items to cluster, to double arrays that + * specify their locations in d-dimensional space. + * @param num_clusters the number of clusters to create + * @return a clustering of the input objects in d-dimensional space + * @throws NotEnoughClustersException if {@code num_clusters} is larger than the number of + * distinct points in object_locations + */ + @SuppressWarnings("unchecked") + public Collection> cluster(Map object_locations, int num_clusters) { + if (object_locations == null || object_locations.isEmpty()) + throw new IllegalArgumentException("'objects' must be non-empty"); + + if (num_clusters < 2 || num_clusters > object_locations.size()) + throw new IllegalArgumentException( + "number of clusters " + + "must be >= 2 and <= number of objects (" + + object_locations.size() + + ")"); + + Set centroids = new HashSet(); + + Object[] obj_array = object_locations.keySet().toArray(); + Set tried = new HashSet(); + + // create the specified number of clusters + while (centroids.size() < num_clusters && tried.size() < object_locations.size()) { + T o = (T) obj_array[(int) (rand.nextDouble() * obj_array.length)]; + tried.add(o); + double[] mean_value = object_locations.get(o); + boolean duplicate = false; + for (double[] cur : centroids) { + if (Arrays.equals(mean_value, cur)) duplicate = true; + } + if (!duplicate) centroids.add(mean_value); } - /** - * Creates an instance with max iterations of 100 and convergence threshold - * of 0.001. - */ - public KMeansClusterer() - { - this(100, 0.001); - } - - /** - * @return the maximum number of iterations - */ - public int getMaxIterations() - { - return max_iterations; - } - - /** - * @param max_iterations the maximum number of iterations - */ - public void setMaxIterations(int max_iterations) - { - if (max_iterations < 0) - throw new IllegalArgumentException("max iterations must be >= 0"); - - this.max_iterations = max_iterations; - } - - /** - * @return the convergence threshold - */ - public double getConvergenceThreshold() - { - return convergence_threshold; - } - - /** - * @param convergence_threshold the convergence threshold - */ - public void setConvergenceThreshold(double convergence_threshold) - { - if (convergence_threshold <= 0) - throw new IllegalArgumentException("convergence threshold " + - "must be > 0"); - - this.convergence_threshold = convergence_threshold; - } - - /** - * Returns a Collection of clusters, where each cluster is - * represented as a Map of Objects to locations - * in d-dimensional space. - * @param object_locations a map of the items to cluster, to - * double arrays that specify their locations in d-dimensional space. - * @param num_clusters the number of clusters to create - * @return a clustering of the input objects in d-dimensional space - * @throws NotEnoughClustersException if {@code num_clusters} is larger than the number of - * distinct points in object_locations - */ - @SuppressWarnings("unchecked") - public Collection> cluster(Map object_locations, int num_clusters) - { - if (object_locations == null || object_locations.isEmpty()) - throw new IllegalArgumentException("'objects' must be non-empty"); - - if (num_clusters < 2 || num_clusters > object_locations.size()) - throw new IllegalArgumentException("number of clusters " + - "must be >= 2 and <= number of objects (" + - object_locations.size() + ")"); - - - Set centroids = new HashSet(); - - Object[] obj_array = object_locations.keySet().toArray(); - Set tried = new HashSet(); - - // create the specified number of clusters - while (centroids.size() < num_clusters && tried.size() < object_locations.size()) - { - T o = (T)obj_array[(int)(rand.nextDouble() * obj_array.length)]; - tried.add(o); - double[] mean_value = object_locations.get(o); - boolean duplicate = false; - for (double[] cur : centroids) - { - if (Arrays.equals(mean_value, cur)) - duplicate = true; - } - if (!duplicate) - centroids.add(mean_value); - } - - if (tried.size() >= object_locations.size()) - throw new NotEnoughClustersException(); - - // put items in their initial clusters - Map> clusterMap = assignToClusters(object_locations, centroids); - - // keep reconstituting clusters until either - // (a) membership is stable, or - // (b) number of iterations passes max_iterations, or - // (c) max movement of any centroid is <= convergence_threshold - int iterations = 0; - double max_movement = Double.POSITIVE_INFINITY; - while (iterations++ < max_iterations && max_movement > convergence_threshold) - { - max_movement = 0; - Set new_centroids = new HashSet(); - // calculate new mean for each cluster - for (Map.Entry> entry : clusterMap.entrySet()) - { - double[] centroid = entry.getKey(); - Map elements = entry.getValue(); - ArrayList locations = new ArrayList(elements.values()); - - double[] mean = DiscreteDistribution.mean(locations); - max_movement = Math.max(max_movement, - Math.sqrt(DiscreteDistribution.squaredError(centroid, mean))); - new_centroids.add(mean); - } - - // TODO: check membership of clusters: have they changed? - - // regenerate cluster membership based on means - clusterMap = assignToClusters(object_locations, new_centroids); - } - return clusterMap.values(); + if (tried.size() >= object_locations.size()) throw new NotEnoughClustersException(); + + // put items in their initial clusters + Map> clusterMap = assignToClusters(object_locations, centroids); + + // keep reconstituting clusters until either + // (a) membership is stable, or + // (b) number of iterations passes max_iterations, or + // (c) max movement of any centroid is <= convergence_threshold + int iterations = 0; + double max_movement = Double.POSITIVE_INFINITY; + while (iterations++ < max_iterations && max_movement > convergence_threshold) { + max_movement = 0; + Set new_centroids = new HashSet(); + // calculate new mean for each cluster + for (Map.Entry> entry : clusterMap.entrySet()) { + double[] centroid = entry.getKey(); + Map elements = entry.getValue(); + ArrayList locations = new ArrayList(elements.values()); + + double[] mean = DiscreteDistribution.mean(locations); + max_movement = + Math.max(max_movement, Math.sqrt(DiscreteDistribution.squaredError(centroid, mean))); + new_centroids.add(mean); + } + + // TODO: check membership of clusters: have they changed? + + // regenerate cluster membership based on means + clusterMap = assignToClusters(object_locations, new_centroids); } - - /** - * Assigns each object to the cluster whose centroid is closest to the - * object. - * @param object_locations a map of objects to locations - * @param centroids the centroids of the clusters to be formed - * @return a map of objects to assigned clusters - */ - protected Map> assignToClusters(Map object_locations, Set centroids) - { - Map> clusterMap = new HashMap>(); - for (double[] centroid : centroids) - clusterMap.put(centroid, new HashMap()); - - for (Map.Entry object_location : object_locations.entrySet()) - { - T object = object_location.getKey(); - double[] location = object_location.getValue(); - - // find the cluster with the closest centroid - Iterator c_iter = centroids.iterator(); - double[] closest = c_iter.next(); - double distance = DiscreteDistribution.squaredError(location, closest); - - while (c_iter.hasNext()) - { - double[] centroid = c_iter.next(); - double dist_cur = DiscreteDistribution.squaredError(location, centroid); - if (dist_cur < distance) - { - distance = dist_cur; - closest = centroid; - } - } - clusterMap.get(closest).put(object, location); + return clusterMap.values(); + } + + /** + * Assigns each object to the cluster whose centroid is closest to the object. + * + * @param object_locations a map of objects to locations + * @param centroids the centroids of the clusters to be formed + * @return a map of objects to assigned clusters + */ + protected Map> assignToClusters( + Map object_locations, Set centroids) { + Map> clusterMap = new HashMap>(); + for (double[] centroid : centroids) clusterMap.put(centroid, new HashMap()); + + for (Map.Entry object_location : object_locations.entrySet()) { + T object = object_location.getKey(); + double[] location = object_location.getValue(); + + // find the cluster with the closest centroid + Iterator c_iter = centroids.iterator(); + double[] closest = c_iter.next(); + double distance = DiscreteDistribution.squaredError(location, closest); + + while (c_iter.hasNext()) { + double[] centroid = c_iter.next(); + double dist_cur = DiscreteDistribution.squaredError(location, centroid); + if (dist_cur < distance) { + distance = dist_cur; + closest = centroid; } - - return clusterMap; - } - - /** - * Sets the seed used by the internal random number generator. - * Enables consistent outputs. - * @param random_seed the random seed to use - */ - public void setSeed(int random_seed) - { - this.rand = new Random(random_seed); + } + clusterMap.get(closest).put(object, location); } - /** - * An exception that indicates that the specified data points cannot be - * clustered into the number of clusters requested by the user. - * This will happen if and only if there are fewer distinct points than - * requested clusters. (If there are fewer total data points than - * requested clusters, IllegalArgumentException will be thrown.) - * - * @author Joshua O'Madadhain - */ - @SuppressWarnings("serial") - public static class NotEnoughClustersException extends RuntimeException - { - @Override - public String getMessage() - { - return "Not enough distinct points in the input data set to form " + - "the requested number of clusters"; - } + return clusterMap; + } + + /** + * Sets the seed used by the internal random number generator. Enables consistent outputs. + * + * @param random_seed the random seed to use + */ + public void setSeed(int random_seed) { + this.rand = new Random(random_seed); + } + + /** + * An exception that indicates that the specified data points cannot be clustered into the number + * of clusters requested by the user. This will happen if and only if there are fewer distinct + * points than requested clusters. (If there are fewer total data points than requested clusters, + * IllegalArgumentException will be thrown.) + * + * @author Joshua O'Madadhain + */ + @SuppressWarnings("serial") + public static class NotEnoughClustersException extends RuntimeException { + @Override + public String getMessage() { + return "Not enough distinct points in the input data set to form " + + "the requested number of clusters"; } + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/MapBinaryHeap.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/MapBinaryHeap.java index 6995986e..680a01ca 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/MapBinaryHeap.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/MapBinaryHeap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, The JUNG Authors + * Copyright (c) 2003, The JUNG Authors * * All rights reserved. * @@ -8,11 +8,12 @@ * https://github.com/jrtom/jung/blob/master/LICENSE for a description. */ /* - * + * * Created on Oct 29, 2003 */ package edu.uci.ics.jung.algorithms.util; +import com.google.common.collect.Iterators; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; @@ -24,358 +25,288 @@ import java.util.NoSuchElementException; import java.util.Queue; -import com.google.common.collect.Iterators; - /** - * An array-based binary heap implementation of a priority queue, - * which also provides - * efficient update() and contains operations. - * It contains extra infrastructure (a hash table) to keep track of the - * position of each element in the array; thus, if the key value of an element - * changes, it may be "resubmitted" to the heap via update - * so that the heap can reposition it efficiently, as necessary. - * + * An array-based binary heap implementation of a priority queue, which also provides efficient + * update() and contains operations. It contains extra infrastructure (a + * hash table) to keep track of the position of each element in the array; thus, if the key value of + * an element changes, it may be "resubmitted" to the heap via update so that the heap + * can reposition it efficiently, as necessary. + * * @author Joshua O'Madadhain */ -public class MapBinaryHeap - extends AbstractCollection - implements Queue -{ - private List heap = new ArrayList(); // holds the heap as an implicit binary tree - private Map object_indices = new HashMap(); // maps each object in the heap to its index in the heap - private Comparator comp; - private final static int TOP = 0; // the index of the top of the heap - - /** - * Creates a MapBinaryHeap whose heap ordering - * is based on the ordering of the elements specified by comp. - * @param comp the comparator to use to order elements in the heap - */ - public MapBinaryHeap(Comparator comp) - { - initialize(comp); - } - - /** - * Creates a MapBinaryHeap whose heap ordering - * will be based on the natural ordering of the elements, - * which must be Comparable. - */ - public MapBinaryHeap() - { - initialize(new ComparableComparator()); - } +public class MapBinaryHeap extends AbstractCollection implements Queue { + private List heap = new ArrayList(); // holds the heap as an implicit binary tree + private Map object_indices = + new HashMap(); // maps each object in the heap to its index in the heap + private Comparator comp; + private static final int TOP = 0; // the index of the top of the heap - /** - * Creates a MapBinaryHeap based on the specified - * collection whose heap ordering - * will be based on the natural ordering of the elements, - * which must be Comparable. - * @param c the collection of {@code Comparable} elements to add to the heap - */ - public MapBinaryHeap(Collection c) - { - this(); - addAll(c); - } - - /** - * Creates a MapBinaryHeap based on the specified collection - * whose heap ordering - * is based on the ordering of the elements specified by c. - * @param c the collection of elements to add to the heap - * @param comp the comparator to use for items in {@code c} - */ - public MapBinaryHeap(Collection c, Comparator comp) - { - this(comp); - addAll(c); - } - - private void initialize(Comparator comp) - { - this.comp = comp; - clear(); - } - - /** - * @see Collection#clear() - */ - @Override - public void clear() - { - object_indices.clear(); - heap.clear(); - } - - /** - * Inserts o into this collection. - */ - @Override - public boolean add(T o) - { - int lastIndex = heap.size(); - heap.add(o); - percolateUp(lastIndex, o); - return true; - } - - /** - * Returns true if this collection contains no elements, and - * false otherwise. - */ - @Override - public boolean isEmpty() - { - return heap.isEmpty(); - } - - /** - * Returns the element at the top of the heap; does not - * alter the heap. - */ - public T peek() - { - if (heap.size() > 0) - return heap.get(TOP); - else - return null; - } - - /** - * @return the size of this heap - */ - @Override - public int size() - { - return heap.size(); - } - - /** - * Informs the heap that this object's internal key value has been - * updated, and that its place in the heap may need to be shifted - * (up or down). - * @param o the object whose key value has been updated - */ - public void update(T o) - { - // Since we don't know whether the key value increased or - // decreased, we just percolate up followed by percolating down; - // one of the two will have no effect. - - int cur = object_indices.get(o); // current index - int new_idx = percolateUp(cur, o); - percolateDown(new_idx); - } + /** + * Creates a MapBinaryHeap whose heap ordering is based on the ordering of the + * elements specified by comp. + * + * @param comp the comparator to use to order elements in the heap + */ + public MapBinaryHeap(Comparator comp) { + initialize(comp); + } - @Override - public boolean contains(Object o) - { - return object_indices.containsKey(o); - } - - /** - * Moves the element at position cur closer to - * the bottom of the heap, or returns if no further motion is - * necessary. Calls itself recursively if further motion is - * possible. - */ - private void percolateDown(int cur) - { - int left = lChild(cur); - int right = rChild(cur); - int smallest; - - if ((left < heap.size()) && - (comp.compare(heap.get(left), heap.get(cur)) < 0)) { - smallest = left; - } else { - smallest = cur; - } - - if ((right < heap.size()) && - (comp.compare(heap.get(right), heap.get(smallest)) < 0)) { - smallest = right; - } - - if (cur != smallest) - { - swap(cur, smallest); - percolateDown(smallest); - } - } + /** + * Creates a MapBinaryHeap whose heap ordering will be based on the natural + * ordering of the elements, which must be Comparable. + */ + public MapBinaryHeap() { + initialize(new ComparableComparator()); + } - /** - * Moves the element o at position cur - * as high as it can go in the heap. Returns the new position of the - * element in the heap. - */ - private int percolateUp(int cur, T o) - { - int i = cur; - - while ((i > TOP) && (comp.compare(heap.get(parent(i)), o) > 0)) - { - T parentElt = heap.get(parent(i)); - heap.set(i, parentElt); - object_indices.put(parentElt, i); // reset index to i (new location) - i = parent(i); - } - - // place object in heap at appropriate place - object_indices.put(o, i); - heap.set(i, o); - - return i; - } - - /** - * Returns the index of the left child of the element at - * index i of the heap. - * @param i - * @return the index of the left child of the element at - * index i of the heap - */ - private int lChild(int i) - { - return (i<<1) + 1; - } - - /** - * Returns the index of the right child of the element at - * index i of the heap. - * @param i - * @return the index of the right child of the element at - * index i of the heap - */ - private int rChild(int i) - { - return (i<<1) + 2; - } - - /** - * Returns the index of the parent of the element at - * index i of the heap. - * @param i - * @return the index of the parent of the element at index i of the heap - */ - private int parent(int i) - { - return (i-1)>>1; - } - - /** - * Swaps the positions of the elements at indices i - * and j of the heap. - * @param i - * @param j - */ - private void swap(int i, int j) - { - T iElt = heap.get(i); - T jElt = heap.get(j); - - heap.set(i, jElt); - object_indices.put(jElt, i); - - heap.set(j, iElt); - object_indices.put(iElt, j); - } - - /** - * Comparator used if none is specified in the constructor. - * @author Joshua O'Madadhain - */ - private class ComparableComparator implements Comparator - { - /** - * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) - */ - @SuppressWarnings("unchecked") - public int compare(T arg0, T arg1) - { - if (!(arg0 instanceof Comparable) || !(arg1 instanceof Comparable)) - throw new IllegalArgumentException("Arguments must be Comparable"); - - return ((Comparable)arg0).compareTo(arg1); - } + /** + * Creates a MapBinaryHeap based on the specified collection whose heap ordering will + * be based on the natural ordering of the elements, which must be Comparable. + * + * @param c the collection of {@code Comparable} elements to add to the heap + */ + public MapBinaryHeap(Collection c) { + this(); + addAll(c); + } + + /** + * Creates a MapBinaryHeap based on the specified collection whose heap ordering is + * based on the ordering of the elements specified by c. + * + * @param c the collection of elements to add to the heap + * @param comp the comparator to use for items in {@code c} + */ + public MapBinaryHeap(Collection c, Comparator comp) { + this(comp); + addAll(c); + } + + private void initialize(Comparator comp) { + this.comp = comp; + clear(); + } + + /** @see Collection#clear() */ + @Override + public void clear() { + object_indices.clear(); + heap.clear(); + } + + /** Inserts o into this collection. */ + @Override + public boolean add(T o) { + int lastIndex = heap.size(); + heap.add(o); + percolateUp(lastIndex, o); + return true; + } + + /** + * Returns true if this collection contains no elements, and false + * otherwise. + */ + @Override + public boolean isEmpty() { + return heap.isEmpty(); + } + + /** Returns the element at the top of the heap; does not alter the heap. */ + public T peek() { + if (heap.size() > 0) return heap.get(TOP); + else return null; + } + + /** @return the size of this heap */ + @Override + public int size() { + return heap.size(); + } + + /** + * Informs the heap that this object's internal key value has been updated, and that its place in + * the heap may need to be shifted (up or down). + * + * @param o the object whose key value has been updated + */ + public void update(T o) { + // Since we don't know whether the key value increased or + // decreased, we just percolate up followed by percolating down; + // one of the two will have no effect. + + int cur = object_indices.get(o); // current index + int new_idx = percolateUp(cur, o); + percolateDown(new_idx); + } + + @Override + public boolean contains(Object o) { + return object_indices.containsKey(o); + } + + /** + * Moves the element at position cur closer to the bottom of the heap, or returns if + * no further motion is necessary. Calls itself recursively if further motion is possible. + */ + private void percolateDown(int cur) { + int left = lChild(cur); + int right = rChild(cur); + int smallest; + + if ((left < heap.size()) && (comp.compare(heap.get(left), heap.get(cur)) < 0)) { + smallest = left; + } else { + smallest = cur; } - /** - * Returns an Iterator that does not support modification - * of the heap. - */ - @Override - public Iterator iterator() - { - return Iterators.unmodifiableIterator(heap.iterator()); + if ((right < heap.size()) && (comp.compare(heap.get(right), heap.get(smallest)) < 0)) { + smallest = right; } - /** - * This data structure does not support the removal of arbitrary elements. - */ - @Override - public boolean remove(Object o) - { - throw new UnsupportedOperationException(); + if (cur != smallest) { + swap(cur, smallest); + percolateDown(smallest); } + } + + /** + * Moves the element o at position cur as high as it can go in the heap. + * Returns the new position of the element in the heap. + */ + private int percolateUp(int cur, T o) { + int i = cur; - /** - * This data structure does not support the removal of arbitrary elements. - */ - @Override - public boolean removeAll(Collection c) - { - throw new UnsupportedOperationException(); + while ((i > TOP) && (comp.compare(heap.get(parent(i)), o) > 0)) { + T parentElt = heap.get(parent(i)); + heap.set(i, parentElt); + object_indices.put(parentElt, i); // reset index to i (new location) + i = parent(i); } - /** - * This data structure does not support the removal of arbitrary elements. - */ - @Override - public boolean retainAll(Collection c) - { - throw new UnsupportedOperationException(); + // place object in heap at appropriate place + object_indices.put(o, i); + heap.set(i, o); + + return i; + } + + /** + * Returns the index of the left child of the element at index i of the heap. + * + * @param i + * @return the index of the left child of the element at index i of the heap + */ + private int lChild(int i) { + return (i << 1) + 1; + } + + /** + * Returns the index of the right child of the element at index i of the heap. + * + * @param i + * @return the index of the right child of the element at index i of the heap + */ + private int rChild(int i) { + return (i << 1) + 2; + } + + /** + * Returns the index of the parent of the element at index i of the heap. + * + * @param i + * @return the index of the parent of the element at index i of the heap + */ + private int parent(int i) { + return (i - 1) >> 1; + } + + /** + * Swaps the positions of the elements at indices i and j of the heap. + * + * @param i + * @param j + */ + private void swap(int i, int j) { + T iElt = heap.get(i); + T jElt = heap.get(j); + + heap.set(i, jElt); + object_indices.put(jElt, i); + + heap.set(j, iElt); + object_indices.put(iElt, j); + } + + /** + * Comparator used if none is specified in the constructor. + * + * @author Joshua O'Madadhain + */ + private class ComparableComparator implements Comparator { + /** @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ + @SuppressWarnings("unchecked") + public int compare(T arg0, T arg1) { + if (!(arg0 instanceof Comparable) || !(arg1 instanceof Comparable)) + throw new IllegalArgumentException("Arguments must be Comparable"); + + return ((Comparable) arg0).compareTo(arg1); } + } + + /** Returns an Iterator that does not support modification of the heap. */ + @Override + public Iterator iterator() { + return Iterators.unmodifiableIterator(heap.iterator()); + } + + /** This data structure does not support the removal of arbitrary elements. */ + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + /** This data structure does not support the removal of arbitrary elements. */ + @Override + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException(); + } - public T element() throws NoSuchElementException - { - T top = this.peek(); - if (top == null) - throw new NoSuchElementException(); - return top; - } - - public boolean offer(T o) - { - return add(o); - } - - public T poll() - { - T top = this.peek(); - if (top != null) - { - int lastIndex = heap.size() - 1; - T bottom_elt = heap.get(lastIndex); - heap.set(TOP, bottom_elt); - object_indices.put(bottom_elt, TOP); - - heap.remove(lastIndex); // remove the last element - if (heap.size() > 1) - percolateDown(TOP); - - object_indices.remove(top); - } - return top; - } - - public T remove() - { - T top = this.poll(); - if (top == null) - throw new NoSuchElementException(); - return top; - } + /** This data structure does not support the removal of arbitrary elements. */ + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public T element() throws NoSuchElementException { + T top = this.peek(); + if (top == null) throw new NoSuchElementException(); + return top; + } + + public boolean offer(T o) { + return add(o); + } + + public T poll() { + T top = this.peek(); + if (top != null) { + int lastIndex = heap.size() - 1; + T bottom_elt = heap.get(lastIndex); + heap.set(TOP, bottom_elt); + object_indices.put(bottom_elt, TOP); + + heap.remove(lastIndex); // remove the last element + if (heap.size() > 1) percolateDown(TOP); + + object_indices.remove(top); + } + return top; + } + public T remove() { + T top = this.poll(); + if (top == null) throw new NoSuchElementException(); + return top; + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/MapSettableTransformer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/MapSettableTransformer.java index 267ea61b..1368d2f0 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/MapSettableTransformer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/MapSettableTransformer.java @@ -1,7 +1,7 @@ /* * Created on Aug 5, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -13,33 +13,29 @@ import java.util.Map; - /** * A SettableTransformer that operates on an underlying Map instance. * Similar to MapTransformer. - * + * * @author Joshua O'Madadhain */ -public class MapSettableTransformer implements SettableTransformer -{ - protected Map map; - - /** - * Creates an instance based on m. - * @param m the map on which this instance is based - */ - public MapSettableTransformer(Map m) - { - this.map = m; - } +public class MapSettableTransformer implements SettableTransformer { + protected Map map; + + /** + * Creates an instance based on m. + * + * @param m the map on which this instance is based + */ + public MapSettableTransformer(Map m) { + this.map = m; + } - public O apply(I input) - { - return map.get(input); - } + public O apply(I input) { + return map.get(input); + } - public void set(I input, O output) - { - map.put(input, output); - } + public void set(I input, O output) { + map.put(input, output); + } } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/SettableTransformer.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/SettableTransformer.java index dc0797bb..4ff3c58a 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/SettableTransformer.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/SettableTransformer.java @@ -1,7 +1,7 @@ /* * Created on Aug 5, 2007 * - * Copyright (c) 2007, The JUNG Authors + * Copyright (c) 2007, The JUNG Authors * * All rights reserved. * @@ -16,16 +16,16 @@ /** * An interface for classes that can set the value to be returned (from transform()) * when invoked on a given input. - * + * * @author Joshua O'Madadhain */ -public interface SettableTransformer extends Function -{ - /** - * Sets the value (output) to be returned by a call to - * transform(input)). - * @param input the value whose output value is being specified - * @param output the output value for {@code input} - */ - public void set(I input, O output); +public interface SettableTransformer extends Function { + /** + * Sets the value (output) to be returned by a call to transform(input) + * ). + * + * @param input the value whose output value is being specified + * @param output the output value for {@code input} + */ + public void set(I input, O output); } diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/WeightedChoice.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/WeightedChoice.java index 719caef6..e4119a33 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/WeightedChoice.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/util/WeightedChoice.java @@ -1,13 +1,10 @@ /** - * Copyright (c) 2009, The JUNG Authors + * Copyright (c) 2009, The JUNG Authors * - * All rights reserved. + *

    All rights reserved. * - * This software is open-source under the BSD license; see either - * "license.txt" or - * https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * Created on Jan 8, 2009 - * + *

    This software is open-source under the BSD license; see either "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. Created on Jan 8, 2009 */ package edu.uci.ics.jung.algorithms.util; @@ -19,185 +16,169 @@ import java.util.Random; /** - * Selects items according to their probability in an arbitrary probability - * distribution. The distribution is specified by a {@code Map} from - * items (of type {@code T}) to weights of type {@code Number}, supplied - * to the constructor; these weights are normalized internally to act as + * Selects items according to their probability in an arbitrary probability distribution. The + * distribution is specified by a {@code Map} from items (of type {@code T}) to weights of type + * {@code Number}, supplied to the constructor; these weights are normalized internally to act as * probabilities. - * + * *

    This implementation selects items in O(1) time, and requires O(n) space. - * + * * @author Joshua O'Madadhain */ -public class WeightedChoice -{ - private List item_pairs; - private Random random; - - /** - * The default minimum value that is treated as a valid probability - * (as opposed to rounding error from floating-point operations). - */ - public static final double DEFAULT_THRESHOLD = 0.00000000001; - - /** - * Equivalent to {@code this(item_weights, new Random(), DEFAULT_THRESHOLD)}. - * @param item_weights a map from items to their weights - */ - public WeightedChoice(Map item_weights) - { - this(item_weights, new Random(), DEFAULT_THRESHOLD); - } - - /** - * Equivalent to {@code this(item_weights, new Random(), threshold)}. - * @param item_weights a map from items to their weights - * @param threshold the minimum value that is treated as a probability - * (anything smaller will be considered equivalent to a floating-point rounding error) - */ - public WeightedChoice(Map item_weights, double threshold) - { - this(item_weights, new Random(), threshold); - } - - /** - * Equivalent to {@code this(item_weights, random, DEFAULT_THRESHOLD)}. - * @param item_weights a map from items to their weights - * @param random the Random instance to use for selection - */ - public WeightedChoice(Map item_weights, Random random) - { - this(item_weights, random, DEFAULT_THRESHOLD); - } - - /** - * Creates an instance with the specified mapping from items to weights, - * random number generator, and threshold value. - * - *

    The mapping defines the weight for each item to be selected; this - * will be proportional to the probability of its selection. - *

    The random number generator specifies the mechanism which will be - * used to provide uniform integer and double values. - *

    The threshold indicates default minimum value that is treated as a valid - * probability (as opposed to rounding error from floating-point operations). - * @param item_weights a map from items to their weights - * @param random the Random instance to use for selection - * @param threshold the minimum value that is treated as a probability - * (anything smaller will be considered equivalent to a floating-point rounding error) - */ - public WeightedChoice(Map item_weights, Random random, - double threshold) - { - if (item_weights.isEmpty()) - throw new IllegalArgumentException("Item weights must be non-empty"); - - int item_count = item_weights.size(); - item_pairs = new ArrayList(item_count); - - double sum = 0; - for (Map.Entry entry : item_weights.entrySet()) - { - double value = entry.getValue().doubleValue(); - if (value <= 0) - throw new IllegalArgumentException("Weights must be > 0"); - sum += value; - } - double bucket_weight = 1.0 / item_weights.size(); - - Queue light_weights = new LinkedList(); - Queue heavy_weights = new LinkedList(); - for (Map.Entry entry : item_weights.entrySet()) - { - double value = entry.getValue().doubleValue() / sum; - enqueueItem(entry.getKey(), value, bucket_weight, light_weights, heavy_weights); - } - - // repeat until both queues empty - while (!heavy_weights.isEmpty() || !light_weights.isEmpty()) - { - ItemPair heavy_item = heavy_weights.poll(); - ItemPair light_item = light_weights.poll(); - double light_weight = 0; - T light = null; - T heavy = null; - if (light_item != null) - { - light_weight = light_item.weight; - light = light_item.light; - } - if (heavy_item != null) - { - heavy = heavy_item.heavy; - // put the 'left over' weight from the heavy item--what wasn't - // needed to make up the difference between the light weight and - // 1/n--back in the appropriate queue - double new_weight = heavy_item.weight - (bucket_weight - light_weight); - if (new_weight > threshold) - enqueueItem(heavy, new_weight, bucket_weight, light_weights, heavy_weights); - } - light_weight *= item_count; - - item_pairs.add(new ItemPair(light, heavy, light_weight)); - } - - this.random = random; - } - - /** - * Adds key/value to the appropriate queue. Keys with values less than - * the threshold get added to {@code light_weights}, all others get added - * to {@code heavy_weights}. - */ - private void enqueueItem(T key, double value, double threshold, - Queue light_weights, Queue heavy_weights) - { - if (value < threshold) - light_weights.offer(new ItemPair(key, null, value)); - else - heavy_weights.offer(new ItemPair(null, key, value)); - } - - /** - * @param seed the seed to be used by the internal random number generator - */ - public void setRandomSeed(long seed) - { - this.random.setSeed(seed); - } - - /** - * Retrieves an item with probability proportional to its weight in the - * {@code Map} provided in the input. - * @return an item chosen randomly based on its specified weight - */ - public T nextItem() - { - ItemPair item_pair = item_pairs.get(random.nextInt(item_pairs.size())); - if (random.nextDouble() < item_pair.weight) - return item_pair.light; - return item_pair.heavy; - } - - /** - * Manages light object/heavy object/light conditional probability tuples. - */ - private class ItemPair - { - T light; - T heavy; - double weight; - - private ItemPair(T light, T heavy, double weight) - { - this.light = light; - this.heavy = heavy; - this.weight = weight; - } - - @Override - public String toString() - { - return String.format("[L:%s, H:%s, %.3f]", light, heavy, weight); - } - } +public class WeightedChoice { + private List item_pairs; + private Random random; + + /** + * The default minimum value that is treated as a valid probability (as opposed to rounding error + * from floating-point operations). + */ + public static final double DEFAULT_THRESHOLD = 0.00000000001; + + /** + * Equivalent to {@code this(item_weights, new Random(), DEFAULT_THRESHOLD)}. + * + * @param item_weights a map from items to their weights + */ + public WeightedChoice(Map item_weights) { + this(item_weights, new Random(), DEFAULT_THRESHOLD); + } + + /** + * Equivalent to {@code this(item_weights, new Random(), threshold)}. + * + * @param item_weights a map from items to their weights + * @param threshold the minimum value that is treated as a probability (anything smaller will be + * considered equivalent to a floating-point rounding error) + */ + public WeightedChoice(Map item_weights, double threshold) { + this(item_weights, new Random(), threshold); + } + + /** + * Equivalent to {@code this(item_weights, random, DEFAULT_THRESHOLD)}. + * + * @param item_weights a map from items to their weights + * @param random the Random instance to use for selection + */ + public WeightedChoice(Map item_weights, Random random) { + this(item_weights, random, DEFAULT_THRESHOLD); + } + + /** + * Creates an instance with the specified mapping from items to weights, random number generator, + * and threshold value. + * + *

    The mapping defines the weight for each item to be selected; this will be proportional to + * the probability of its selection. + * + *

    The random number generator specifies the mechanism which will be used to provide uniform + * integer and double values. + * + *

    The threshold indicates default minimum value that is treated as a valid probability (as + * opposed to rounding error from floating-point operations). + * + * @param item_weights a map from items to their weights + * @param random the Random instance to use for selection + * @param threshold the minimum value that is treated as a probability (anything smaller will be + * considered equivalent to a floating-point rounding error) + */ + public WeightedChoice(Map item_weights, Random random, double threshold) { + if (item_weights.isEmpty()) + throw new IllegalArgumentException("Item weights must be non-empty"); + + int item_count = item_weights.size(); + item_pairs = new ArrayList(item_count); + + double sum = 0; + for (Map.Entry entry : item_weights.entrySet()) { + double value = entry.getValue().doubleValue(); + if (value <= 0) throw new IllegalArgumentException("Weights must be > 0"); + sum += value; + } + double bucket_weight = 1.0 / item_weights.size(); + + Queue light_weights = new LinkedList(); + Queue heavy_weights = new LinkedList(); + for (Map.Entry entry : item_weights.entrySet()) { + double value = entry.getValue().doubleValue() / sum; + enqueueItem(entry.getKey(), value, bucket_weight, light_weights, heavy_weights); + } + + // repeat until both queues empty + while (!heavy_weights.isEmpty() || !light_weights.isEmpty()) { + ItemPair heavy_item = heavy_weights.poll(); + ItemPair light_item = light_weights.poll(); + double light_weight = 0; + T light = null; + T heavy = null; + if (light_item != null) { + light_weight = light_item.weight; + light = light_item.light; + } + if (heavy_item != null) { + heavy = heavy_item.heavy; + // put the 'left over' weight from the heavy item--what wasn't + // needed to make up the difference between the light weight and + // 1/n--back in the appropriate queue + double new_weight = heavy_item.weight - (bucket_weight - light_weight); + if (new_weight > threshold) + enqueueItem(heavy, new_weight, bucket_weight, light_weights, heavy_weights); + } + light_weight *= item_count; + + item_pairs.add(new ItemPair(light, heavy, light_weight)); + } + + this.random = random; + } + + /** + * Adds key/value to the appropriate queue. Keys with values less than the threshold get added to + * {@code light_weights}, all others get added to {@code heavy_weights}. + */ + private void enqueueItem( + T key, + double value, + double threshold, + Queue light_weights, + Queue heavy_weights) { + if (value < threshold) light_weights.offer(new ItemPair(key, null, value)); + else heavy_weights.offer(new ItemPair(null, key, value)); + } + + /** @param seed the seed to be used by the internal random number generator */ + public void setRandomSeed(long seed) { + this.random.setSeed(seed); + } + + /** + * Retrieves an item with probability proportional to its weight in the {@code Map} provided in + * the input. + * + * @return an item chosen randomly based on its specified weight + */ + public T nextItem() { + ItemPair item_pair = item_pairs.get(random.nextInt(item_pairs.size())); + if (random.nextDouble() < item_pair.weight) return item_pair.light; + return item_pair.heavy; + } + + /** Manages light object/heavy object/light conditional probability tuples. */ + private class ItemPair { + T light; + T heavy; + double weight; + + private ItemPair(T light, T heavy, double weight) { + this.light = light; + this.heavy = heavy; + this.weight = weight; + } + + @Override + public String toString() { + return String.format("[L:%s, H:%s, %.3f]", light, heavy, weight); + } + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/cluster/TestBicomponentClusterer.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/cluster/TestBicomponentClusterer.java index bcd480d4..5961c8d0 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/cluster/TestBicomponentClusterer.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/cluster/TestBicomponentClusterer.java @@ -1,262 +1,243 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.cluster; +import com.google.common.graph.Graph; +import com.google.common.graph.GraphBuilder; +import com.google.common.graph.MutableGraph; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; - -import com.google.common.graph.Graph; -import com.google.common.graph.GraphBuilder; -import com.google.common.graph.MutableGraph; - import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; - -/** - * @author Scott White - */ +/** @author Scott White */ public class TestBicomponentClusterer extends TestCase { - public static Test suite() { - return new TestSuite(TestBicomponentClusterer.class); - } - - @Override - protected void setUp() { - - } - - public void testExtract0() throws Exception - { - MutableGraph graph = GraphBuilder.undirected().build(); - String[] v = {"0"}; - graph.addNode(v[0]); - - List> c = new ArrayList>(); - c.add(0, new HashSet()); - c.get(0).add(v[0]); - -// Set[] c = {new HashSet()}; - -// c[0].add(v[0]); - - testComponents(graph, v, c); + public static Test suite() { + return new TestSuite(TestBicomponentClusterer.class); + } + + @Override + protected void setUp() {} + + public void testExtract0() throws Exception { + MutableGraph graph = GraphBuilder.undirected().build(); + String[] v = {"0"}; + graph.addNode(v[0]); + + List> c = new ArrayList>(); + c.add(0, new HashSet()); + c.get(0).add(v[0]); + + // Set[] c = {new HashSet()}; + + // c[0].add(v[0]); + + testComponents(graph, v, c); + } + + public void testExtractEdge() throws Exception { + MutableGraph graph = GraphBuilder.undirected().build(); + String[] v = {"0", "1"}; + graph.putEdge(v[0], v[1]); + + List> c = new ArrayList>(); + c.add(0, new HashSet()); + c.get(0).add(v[0]); + c.get(0).add(v[1]); + + // Set[] c = {new HashSet()}; + // + // c[0].add(v[0]); + // c[0].add(v[1]); + + testComponents(graph, v, c); + } + + public void testExtractV() throws Exception { + MutableGraph graph = GraphBuilder.undirected().build(); + String[] v = {"0", "1", "2"}; + graph.putEdge(v[0], v[1]); + graph.putEdge(v[0], v[2]); + + List> c = new ArrayList>(); + c.add(0, new HashSet()); + c.add(1, new HashSet()); + + c.get(0).add(v[0]); + c.get(0).add(v[1]); + + c.get(1).add(v[0]); + c.get(1).add(v[2]); + + // Set[] c = {new HashSet(), new HashSet()}; + // + // c[0].add(v[0]); + // c[0].add(v[1]); + // + // c[1].add(v[0]); + // c[1].add(v[2]); + + testComponents(graph, v, c); + } + + public void createEdges(String[] v, int[][] edge_array, MutableGraph g) { + for (int k = 0; k < edge_array.length; k++) { + int i = edge_array[k][0]; + int j = edge_array[k][1]; + String v1 = getVertex(v, i, g); + String v2 = getVertex(v, j, g); + + g.putEdge(v1, v2); } - - public void testExtractEdge() throws Exception - { - MutableGraph graph = GraphBuilder.undirected().build(); - String[] v = {"0","1"}; - graph.putEdge(v[0], v[1]); - - List> c = new ArrayList>(); - c.add(0, new HashSet()); - c.get(0).add(v[0]); - c.get(0).add(v[1]); - -// Set[] c = {new HashSet()}; -// -// c[0].add(v[0]); -// c[0].add(v[1]); - - testComponents(graph, v, c); + } + + public String getVertex(String[] v_array, int i, MutableGraph g) { + String v = v_array[i]; + if (v == null) { + v_array[i] = Character.toString((char) ('0' + i)); + g.addNode(v_array[i]); + v = v_array[i]; } - - public void testExtractV() throws Exception - { - MutableGraph graph = GraphBuilder.undirected().build(); - String[] v = {"0","1","2"}; - graph.putEdge(v[0], v[1]); - graph.putEdge(v[0], v[2]); - - List> c = new ArrayList>(); - c.add(0, new HashSet()); - c.add(1, new HashSet()); - - c.get(0).add(v[0]); - c.get(0).add(v[1]); - - c.get(1).add(v[0]); - c.get(1).add(v[2]); - -// Set[] c = {new HashSet(), new HashSet()}; -// -// c[0].add(v[0]); -// c[0].add(v[1]); -// -// c[1].add(v[0]); -// c[1].add(v[2]); - - testComponents(graph, v, c); - } - - public void createEdges(String[] v, int[][] edge_array, MutableGraph g) - { - for (int k = 0; k < edge_array.length; k++) - { - int i = edge_array[k][0]; - int j = edge_array[k][1]; - String v1 = getVertex(v, i, g); - String v2 = getVertex(v, j, g); - - g.putEdge(v1, v2); + return v; + } + + public void testExtract1() { + String[] v = new String[6]; + int[][] edges1 = {{0, 1}, {0, 5}, {0, 3}, {0, 4}, {1, 5}, {3, 4}, {2, 3}}; + MutableGraph graph = GraphBuilder.undirected().build(); + createEdges(v, edges1, graph); + + List> c = new ArrayList>(); + for (int i = 0; i < 3; i++) c.add(i, new HashSet()); + + c.get(0).add(v[0]); + c.get(0).add(v[1]); + c.get(0).add(v[5]); + + c.get(1).add(v[0]); + c.get(1).add(v[3]); + c.get(1).add(v[4]); + + c.get(2).add(v[2]); + c.get(2).add(v[3]); + + // Set[] c = new Set[3]; + // for (int i = 0; i < c.length; i++) + // c[i] = new HashSet(); + // + // c[0].add(v[0]); + // c[0].add(v[1]); + // c[0].add(v[5]); + // + // c[1].add(v[0]); + // c[1].add(v[3]); + // c[1].add(v[4]); + // + // c[2].add(v[2]); + // c[2].add(v[3]); + + testComponents(graph, v, c); + } + + public void testExtract2() { + String[] v = new String[9]; + int[][] edges1 = { + {0, 2}, {0, 4}, {1, 0}, {2, 1}, {3, 0}, {4, 3}, {5, 3}, {6, 7}, {6, 8}, {8, 7} + }; + MutableGraph graph = GraphBuilder.undirected().build(); + createEdges(v, edges1, graph); + + List> c = new ArrayList>(); + for (int i = 0; i < 4; i++) c.add(i, new HashSet()); + + c.get(0).add(v[0]); + c.get(0).add(v[1]); + c.get(0).add(v[2]); + + c.get(1).add(v[0]); + c.get(1).add(v[3]); + c.get(1).add(v[4]); + + c.get(2).add(v[5]); + c.get(2).add(v[3]); + + c.get(3).add(v[6]); + c.get(3).add(v[7]); + c.get(3).add(v[8]); + + // Set[] c = new Set[4]; + // for (int i = 0; i < c.length; i++) + // c[i] = new HashSet(); + // + // c[0].add(v[0]); + // c[0].add(v[1]); + // c[0].add(v[2]); + // + // c[1].add(v[0]); + // c[1].add(v[3]); + // c[1].add(v[4]); + // + // c[2].add(v[5]); + // c[2].add(v[3]); + // + // c[3].add(v[6]); + // c[3].add(v[7]); + // c[3].add(v[8]); + + testComponents(graph, v, c); + } + + public void testComponents(Graph graph, String[] vertices, List> c) { + BicomponentClusterer finder = new BicomponentClusterer(); + Set> bicomponents = finder.apply(graph); + + // check number of components + assertEquals(bicomponents.size(), c.size()); + + // diagnostic; should be commented out for typical unit tests + // for (int i = 0; i < bicomponents.size(); i++) + // { + // System.out.print("Component " + i + ": "); + // Set bicomponent = bicomponents.getCluster(i); + // for (Iterator iter = bicomponent.iterator(); iter.hasNext(); ) + // { + // Vertex w = (Vertex)iter.next(); + // System.out.print(sl.getLabel(w) + " "); + // } + // System.out.println(); + // } + // System.out.println(); + + // make sure that each set in c[] is found in bicomponents + List> clusterList = new ArrayList>(bicomponents); + boolean found = false; + for (int i = 0; i < c.size(); i++) { + for (int j = 0; j < bicomponents.size(); j++) + if (clusterList.get(j).equals(c.get(i))) { + found = true; + break; } + assertTrue(found); } - - public String getVertex(String[] v_array, int i, MutableGraph g) - { - String v = v_array[i]; - if (v == null) - { - v_array[i] = Character.toString((char)('0'+i)); - g.addNode(v_array[i]); - v = v_array[i]; - } - return v; + + // make sure that each vertex is represented in >=1 element of bicomponents + Set collapsedSet = new HashSet(); + for (Set set : bicomponents) { + collapsedSet.addAll(set); } - - public void testExtract1() { - String[] v = new String[6]; - int[][] edges1 = {{0,1}, {0,5}, {0,3}, {0,4}, {1,5}, {3,4}, {2,3}}; - MutableGraph graph = GraphBuilder.undirected().build(); - createEdges(v, edges1, graph); - - List> c = new ArrayList>(); - for (int i = 0; i < 3; i++) - c.add(i, new HashSet()); - - c.get(0).add(v[0]); - c.get(0).add(v[1]); - c.get(0).add(v[5]); - - c.get(1).add(v[0]); - c.get(1).add(v[3]); - c.get(1).add(v[4]); - - c.get(2).add(v[2]); - c.get(2).add(v[3]); - -// Set[] c = new Set[3]; -// for (int i = 0; i < c.length; i++) -// c[i] = new HashSet(); -// -// c[0].add(v[0]); -// c[0].add(v[1]); -// c[0].add(v[5]); -// -// c[1].add(v[0]); -// c[1].add(v[3]); -// c[1].add(v[4]); -// -// c[2].add(v[2]); -// c[2].add(v[3]); - - testComponents(graph, v, c); - } - - public void testExtract2() { - String[] v = new String[9]; - int[][] edges1 = {{0,2}, {0,4}, {1,0}, {2,1}, {3,0}, {4,3}, {5,3}, {6,7}, {6,8}, {8,7}}; - MutableGraph graph = GraphBuilder.undirected().build(); - createEdges(v, edges1, graph); - - List> c = new ArrayList>(); - for (int i = 0; i < 4; i++) - c.add(i, new HashSet()); - - c.get(0).add(v[0]); - c.get(0).add(v[1]); - c.get(0).add(v[2]); - - c.get(1).add(v[0]); - c.get(1).add(v[3]); - c.get(1).add(v[4]); - - c.get(2).add(v[5]); - c.get(2).add(v[3]); - - c.get(3).add(v[6]); - c.get(3).add(v[7]); - c.get(3).add(v[8]); - -// Set[] c = new Set[4]; -// for (int i = 0; i < c.length; i++) -// c[i] = new HashSet(); -// -// c[0].add(v[0]); -// c[0].add(v[1]); -// c[0].add(v[2]); -// -// c[1].add(v[0]); -// c[1].add(v[3]); -// c[1].add(v[4]); -// -// c[2].add(v[5]); -// c[2].add(v[3]); -// -// c[3].add(v[6]); -// c[3].add(v[7]); -// c[3].add(v[8]); - - testComponents(graph, v, c); - } - - public void testComponents(Graph graph, String[] vertices, List> c) - { - BicomponentClusterer finder = new BicomponentClusterer(); - Set> bicomponents = finder.apply(graph); - - // check number of components - assertEquals(bicomponents.size(), c.size()); - - // diagnostic; should be commented out for typical unit tests -// for (int i = 0; i < bicomponents.size(); i++) -// { -// System.out.print("Component " + i + ": "); -// Set bicomponent = bicomponents.getCluster(i); -// for (Iterator iter = bicomponent.iterator(); iter.hasNext(); ) -// { -// Vertex w = (Vertex)iter.next(); -// System.out.print(sl.getLabel(w) + " "); -// } -// System.out.println(); -// } -// System.out.println(); - - // make sure that each set in c[] is found in bicomponents - List> clusterList = new ArrayList>(bicomponents); - boolean found = false; - for (int i = 0; i < c.size(); i++) - { - for (int j = 0; j < bicomponents.size(); j++) - if (clusterList.get(j).equals(c.get(i))) - { - found = true; - break; - } - assertTrue(found); - } - - // make sure that each vertex is represented in >=1 element of bicomponents - Set collapsedSet = new HashSet(); - for(Set set : bicomponents) { - collapsedSet.addAll(set); - } - for (String v : graph.nodes()) - { - assertTrue(collapsedSet.contains(v)); -// assertFalse(((LinkedHashSet)vset).get(v).isEmpty()); - } + for (String v : graph.nodes()) { + assertTrue(collapsedSet.contains(v)); + // assertFalse(((LinkedHashSet)vset).get(v).isEmpty()); } - + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/cluster/TestEdgeBetweennessClusterer.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/cluster/TestEdgeBetweennessClusterer.java index c635b86f..1d4a7424 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/cluster/TestEdgeBetweennessClusterer.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/cluster/TestEdgeBetweennessClusterer.java @@ -1,63 +1,59 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.cluster; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.NetworkBuilder; import java.util.Collection; import java.util.Set; - import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.NetworkBuilder; - -/** - * @author Scott White - */ +/** @author Scott White */ public class TestEdgeBetweennessClusterer extends TestCase { - public static Test suite() { - return new TestSuite(TestEdgeBetweennessClusterer.class); - } + public static Test suite() { + return new TestSuite(TestEdgeBetweennessClusterer.class); + } - @Override - protected void setUp() { - } + @Override + protected void setUp() {} + + public void testRanker() { - public void testRanker() { - - MutableNetwork graph = NetworkBuilder.directed().build(); - for(int i=0; i<10; i++) { - graph.addNode(i+1); - } - int j=0; - graph.addEdge(1, 2, j++); - graph.addEdge(1, 3, j++); - graph.addEdge(2, 3, j++); - graph.addEdge(5, 6, j++); - graph.addEdge(5, 7, j++); - graph.addEdge(6, 7, j++); - graph.addEdge(8, 10, j++); - graph.addEdge(7, 8, j++); - graph.addEdge(7, 10, j++); - graph.addEdge(3, 4, j++); - graph.addEdge(4, 6, j++); - graph.addEdge(4, 8, j++); - - Assert.assertEquals(graph.nodes().size(),10); - Assert.assertEquals(graph.edges().size(),12); - - EdgeBetweennessClusterer clusterer = new EdgeBetweennessClusterer(3); - Collection> clusters = clusterer.apply(graph); - - Assert.assertEquals(clusters.size(),3); + MutableNetwork graph = NetworkBuilder.directed().build(); + for (int i = 0; i < 10; i++) { + graph.addNode(i + 1); } + int j = 0; + graph.addEdge(1, 2, j++); + graph.addEdge(1, 3, j++); + graph.addEdge(2, 3, j++); + graph.addEdge(5, 6, j++); + graph.addEdge(5, 7, j++); + graph.addEdge(6, 7, j++); + graph.addEdge(8, 10, j++); + graph.addEdge(7, 8, j++); + graph.addEdge(7, 10, j++); + graph.addEdge(3, 4, j++); + graph.addEdge(4, 6, j++); + graph.addEdge(4, 8, j++); + + Assert.assertEquals(graph.nodes().size(), 10); + Assert.assertEquals(graph.edges().size(), 12); + + EdgeBetweennessClusterer clusterer = + new EdgeBetweennessClusterer(3); + Collection> clusters = clusterer.apply(graph); + + Assert.assertEquals(clusters.size(), 3); + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/cluster/WeakComponentClustererTest.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/cluster/WeakComponentClustererTest.java index 14aaff37..3af4d9b7 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/cluster/WeakComponentClustererTest.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/cluster/WeakComponentClustererTest.java @@ -1,20 +1,17 @@ package edu.uci.ics.jung.algorithms.cluster; import com.google.common.graph.Network; - import edu.uci.ics.jung.graph.util.TestGraphs; import junit.framework.TestCase; public class WeakComponentClustererTest extends TestCase { - - Network graph = TestGraphs.getDemoGraph(); - - public void testWeakComponent() { - WeakComponentClusterer clusterer = - new WeakComponentClusterer(); -// Set> clusterSet = - clusterer.apply(graph); -// System.err.println("set is "+clusterSet); - } + Network graph = TestGraphs.getDemoGraph(); + + public void testWeakComponent() { + WeakComponentClusterer clusterer = new WeakComponentClusterer(); + // Set> clusterSet = + clusterer.apply(graph); + // System.err.println("set is "+clusterSet); + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/filters/impl/TestKNeighborhoodFilter.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/filters/impl/TestKNeighborhoodFilter.java index 8f273cfa..be52939a 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/filters/impl/TestKNeighborhoodFilter.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/filters/impl/TestKNeighborhoodFilter.java @@ -5,63 +5,62 @@ import com.google.common.graph.GraphBuilder; import com.google.common.graph.ImmutableGraph; import com.google.common.graph.MutableGraph; - import edu.uci.ics.jung.algorithms.filters.KNeighborhoodFilter; import junit.framework.TestCase; public class TestKNeighborhoodFilter extends TestCase { - - MutableGraph graph; - - // TODO: test multiple root nodes - public void testDirected() { - graph = GraphBuilder.directed().allowsSelfLoops(true).build(); - populateGraph(graph); - MutableGraph expected = GraphBuilder.directed().allowsSelfLoops(true).build(); - expected.putEdge(0, 1); - expected.putEdge(0, 2); - expected.putEdge(2, 3); - expected.putEdge(2, 4); - expected.putEdge(3, 0); - expected.putEdge(3, 3); - - Graph filtered - = KNeighborhoodFilter.filterGraph(ImmutableGraph.copyOf(graph), ImmutableSet.of(0), 2); - assertEquals(expected, filtered); - } - - public void testUndirected() { - graph = GraphBuilder.undirected().allowsSelfLoops(true).build(); - populateGraph(graph); - MutableGraph expected = GraphBuilder.undirected().allowsSelfLoops(true).build(); - expected.putEdge(0, 1); - expected.putEdge(0, 2); - expected.putEdge(2, 3); - expected.putEdge(2, 4); - expected.putEdge(0, 3); - expected.putEdge(3, 5); - expected.putEdge(5, 0); - expected.putEdge(5, 6); - expected.putEdge(3, 3); + MutableGraph graph; + + // TODO: test multiple root nodes + public void testDirected() { + graph = GraphBuilder.directed().allowsSelfLoops(true).build(); + populateGraph(graph); + MutableGraph expected = GraphBuilder.directed().allowsSelfLoops(true).build(); + expected.putEdge(0, 1); + expected.putEdge(0, 2); + expected.putEdge(2, 3); + expected.putEdge(2, 4); + expected.putEdge(3, 0); + expected.putEdge(3, 3); + + Graph filtered = + KNeighborhoodFilter.filterGraph(ImmutableGraph.copyOf(graph), ImmutableSet.of(0), 2); + + assertEquals(expected, filtered); + } + + public void testUndirected() { + graph = GraphBuilder.undirected().allowsSelfLoops(true).build(); + populateGraph(graph); + MutableGraph expected = GraphBuilder.undirected().allowsSelfLoops(true).build(); + expected.putEdge(0, 1); + expected.putEdge(0, 2); + expected.putEdge(2, 3); + expected.putEdge(2, 4); + expected.putEdge(0, 3); + expected.putEdge(3, 5); + expected.putEdge(5, 0); + expected.putEdge(5, 6); + expected.putEdge(3, 3); + + Graph filtered = + KNeighborhoodFilter.filterGraph(ImmutableGraph.copyOf(graph), ImmutableSet.of(0), 2); + + assertEquals(expected, filtered); + } - Graph filtered - = KNeighborhoodFilter.filterGraph(ImmutableGraph.copyOf(graph), ImmutableSet.of(0), 2); - - assertEquals(expected, filtered); - } - - private void populateGraph(MutableGraph graph) { - graph.putEdge(0, 1); - graph.putEdge(0, 2); - graph.putEdge(2, 3); - graph.putEdge(2, 4); - graph.putEdge(3, 5); - graph.putEdge(5, 6); - graph.putEdge(5, 0); - graph.putEdge(3, 0); - graph.putEdge(6, 7); - graph.putEdge(3, 3); - graph.addNode(8); - } + private void populateGraph(MutableGraph graph) { + graph.putEdge(0, 1); + graph.putEdge(0, 2); + graph.putEdge(2, 3); + graph.putEdge(2, 4); + graph.putEdge(3, 5); + graph.putEdge(5, 6); + graph.putEdge(5, 0); + graph.putEdge(3, 0); + graph.putEdge(6, 7); + graph.putEdge(3, 3); + graph.addNode(8); + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/flows/TestEdmondsKarpMaxFlow.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/flows/TestEdmondsKarpMaxFlow.java index c6629c4f..3d646bc6 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/flows/TestEdmondsKarpMaxFlow.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/flows/TestEdmondsKarpMaxFlow.java @@ -1,210 +1,203 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.flows; +import com.google.common.base.Functions; +import com.google.common.base.Supplier; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.NetworkBuilder; import java.util.HashMap; import java.util.Map; import java.util.Set; - import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; -import com.google.common.base.Functions; -import com.google.common.base.Supplier; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.NetworkBuilder; +/** @author Scott White, Joshua O'Madadhain, Tom Nelson */ +public class TestEdmondsKarpMaxFlow extends TestCase { + public static Test suite() { + return new TestSuite(TestEdmondsKarpMaxFlow.class); + } -/** - * @author Scott White, Joshua O'Madadhain, Tom Nelson - */ -public class TestEdmondsKarpMaxFlow extends TestCase { + @Override + protected void setUp() {} + + public void testSanityChecks() { + MutableNetwork g = NetworkBuilder.directed().build(); + Number source = new Integer(1); + Number sink = new Integer(2); + g.addNode(source); + g.addNode(sink); - public static Test suite() { - return new TestSuite(TestEdmondsKarpMaxFlow.class); - } - - @Override - protected void setUp() { - - } - - public void testSanityChecks() - { - MutableNetwork g = NetworkBuilder.directed().build(); - Number source = new Integer(1); - Number sink = new Integer(2); - g.addNode(source); - g.addNode(sink); - - Number v = new Integer(3); - - MutableNetwork h = NetworkBuilder.directed().build(); - Number w = new Integer(4); - g.addNode(w); - - try - { - new EdmondsKarpMaxFlow(g, source, source, null, null, null); - fail("source and sink vertices not distinct"); - } - catch (IllegalArgumentException iae) {} - - try - { - new EdmondsKarpMaxFlow(h, source, w, null, null, null); - fail("source and sink vertices not both part of specified graph"); - } - catch (IllegalArgumentException iae) {} - - try - { - new EdmondsKarpMaxFlow(g, source, v, null, null, null); - fail("source and sink vertices not both part of specified graph"); - } - catch (IllegalArgumentException iae) {} + Number v = new Integer(3); + + MutableNetwork h = NetworkBuilder.directed().build(); + Number w = new Integer(4); + g.addNode(w); + + try { + new EdmondsKarpMaxFlow(g, source, source, null, null, null); + fail("source and sink vertices not distinct"); + } catch (IllegalArgumentException iae) { + } + + try { + new EdmondsKarpMaxFlow(h, source, w, null, null, null); + fail("source and sink vertices not both part of specified graph"); + } catch (IllegalArgumentException iae) { + } + + try { + new EdmondsKarpMaxFlow(g, source, v, null, null, null); + fail("source and sink vertices not both part of specified graph"); + } catch (IllegalArgumentException iae) { + } + } + + public void testSimpleFlow() { + MutableNetwork graph = NetworkBuilder.directed().build(); + Supplier edgeFactory = + new Supplier() { + int count = 0; + + public Number get() { + return count++; + } + }; + + Map edgeCapacityMap = new HashMap<>(); + for (int i = 0; i < 6; i++) { + graph.addNode(i); } - - public void testSimpleFlow() { - MutableNetwork graph = NetworkBuilder.directed().build(); - Supplier edgeFactory = new Supplier() { - int count = 0; - public Number get() { - return count++; - } - }; - - Map edgeCapacityMap = new HashMap<>(); - for(int i=0; i<6; i++) { - graph.addNode(i); - } - - Map edgeFlowMap = new HashMap<>(); - - graph.addEdge(0,1, edgeFactory.get()); - edgeCapacityMap.put(0, 16); - - graph.addEdge(0,2, edgeFactory.get()); - edgeCapacityMap.put(1,13); - - graph.addEdge(1,2, edgeFactory.get()); - edgeCapacityMap.put(2, 6); - - graph.addEdge(1,3, edgeFactory.get()); - edgeCapacityMap.put(3, 12); - - graph.addEdge(2,4, edgeFactory.get()); - edgeCapacityMap.put(4, 14); - - graph.addEdge(3,2, edgeFactory.get()); - edgeCapacityMap.put(5, 9); - - graph.addEdge(3,5, edgeFactory.get()); - edgeCapacityMap.put(6, 20); - - graph.addEdge(4,3, edgeFactory.get()); - edgeCapacityMap.put(7, 7); - - graph.addEdge(4,5, edgeFactory.get()); - edgeCapacityMap.put(8, 4); - - EdmondsKarpMaxFlow ek = - new EdmondsKarpMaxFlow( - graph, - 0, - 5, - Functions.forMap(edgeCapacityMap, null), - edgeFlowMap, - edgeFactory); - ek.evaluate(); - - assertTrue(ek.getMaxFlow() == 23); - Set nodesInS = ek.getNodesInSourcePartition(); - assertEquals(4,nodesInS.size()); - - for (Number v : nodesInS) { - Assert.assertTrue(v.intValue() != 3 && v.intValue() != 5); - } - - Set nodesInT = ek.getNodesInSinkPartition(); - assertEquals(2,nodesInT.size()); - - for (Number v : nodesInT) { - Assert.assertTrue(v.intValue() == 3 || v.intValue() == 5); - } - - Set minCutEdges = ek.getMinCutEdges(); - int maxFlow = 0; - for (Number e : minCutEdges) { - Number flow = edgeFlowMap.get(e); - maxFlow += flow.intValue(); - } - Assert.assertEquals(23,maxFlow); - Assert.assertEquals(3,minCutEdges.size()); - } - - public void testAnotherSimpleFlow() { - MutableNetwork graph = NetworkBuilder.directed().build(); - Supplier edgeFactory = new Supplier() { - int count=0; - public Number get() { - return count++; - } - }; - - Map edgeCapacityMap = new HashMap<>(); - for(int i=0; i<6; i++) { - graph.addNode(i); - } - - Map edgeFlowMap = new HashMap<>(); - - graph.addEdge(0,1, edgeFactory.get()); - edgeCapacityMap.put(0,5); - - graph.addEdge(0,2, edgeFactory.get()); - edgeCapacityMap.put(1,3); - - graph.addEdge(1,5, edgeFactory.get()); - edgeCapacityMap.put(2,2); - - graph.addEdge(1,2, edgeFactory.get()); - edgeCapacityMap.put(3,8); - - graph.addEdge(2,3, edgeFactory.get()); - edgeCapacityMap.put(4,4); - - graph.addEdge(2,4, edgeFactory.get()); - edgeCapacityMap.put(5,2); - - graph.addEdge(3,4, edgeFactory.get()); - edgeCapacityMap.put(6,3); - - graph.addEdge(3,5, edgeFactory.get()); - edgeCapacityMap.put(7,6); - - graph.addEdge(4,5, edgeFactory.get()); - edgeCapacityMap.put(8,1); - - EdmondsKarpMaxFlow ek = - new EdmondsKarpMaxFlow( - graph, - 0, - 5, - Functions.forMap(edgeCapacityMap, null), - edgeFlowMap, - edgeFactory); - ek.evaluate(); - - assertTrue(ek.getMaxFlow() == 7); - } + + Map edgeFlowMap = new HashMap<>(); + + graph.addEdge(0, 1, edgeFactory.get()); + edgeCapacityMap.put(0, 16); + + graph.addEdge(0, 2, edgeFactory.get()); + edgeCapacityMap.put(1, 13); + + graph.addEdge(1, 2, edgeFactory.get()); + edgeCapacityMap.put(2, 6); + + graph.addEdge(1, 3, edgeFactory.get()); + edgeCapacityMap.put(3, 12); + + graph.addEdge(2, 4, edgeFactory.get()); + edgeCapacityMap.put(4, 14); + + graph.addEdge(3, 2, edgeFactory.get()); + edgeCapacityMap.put(5, 9); + + graph.addEdge(3, 5, edgeFactory.get()); + edgeCapacityMap.put(6, 20); + + graph.addEdge(4, 3, edgeFactory.get()); + edgeCapacityMap.put(7, 7); + + graph.addEdge(4, 5, edgeFactory.get()); + edgeCapacityMap.put(8, 4); + + EdmondsKarpMaxFlow ek = + new EdmondsKarpMaxFlow( + graph, + 0, + 5, + Functions.forMap(edgeCapacityMap, null), + edgeFlowMap, + edgeFactory); + ek.evaluate(); + + assertTrue(ek.getMaxFlow() == 23); + Set nodesInS = ek.getNodesInSourcePartition(); + assertEquals(4, nodesInS.size()); + + for (Number v : nodesInS) { + Assert.assertTrue(v.intValue() != 3 && v.intValue() != 5); + } + + Set nodesInT = ek.getNodesInSinkPartition(); + assertEquals(2, nodesInT.size()); + + for (Number v : nodesInT) { + Assert.assertTrue(v.intValue() == 3 || v.intValue() == 5); + } + + Set minCutEdges = ek.getMinCutEdges(); + int maxFlow = 0; + for (Number e : minCutEdges) { + Number flow = edgeFlowMap.get(e); + maxFlow += flow.intValue(); + } + Assert.assertEquals(23, maxFlow); + Assert.assertEquals(3, minCutEdges.size()); + } + + public void testAnotherSimpleFlow() { + MutableNetwork graph = NetworkBuilder.directed().build(); + Supplier edgeFactory = + new Supplier() { + int count = 0; + + public Number get() { + return count++; + } + }; + + Map edgeCapacityMap = new HashMap<>(); + for (int i = 0; i < 6; i++) { + graph.addNode(i); + } + + Map edgeFlowMap = new HashMap<>(); + + graph.addEdge(0, 1, edgeFactory.get()); + edgeCapacityMap.put(0, 5); + + graph.addEdge(0, 2, edgeFactory.get()); + edgeCapacityMap.put(1, 3); + + graph.addEdge(1, 5, edgeFactory.get()); + edgeCapacityMap.put(2, 2); + + graph.addEdge(1, 2, edgeFactory.get()); + edgeCapacityMap.put(3, 8); + + graph.addEdge(2, 3, edgeFactory.get()); + edgeCapacityMap.put(4, 4); + + graph.addEdge(2, 4, edgeFactory.get()); + edgeCapacityMap.put(5, 2); + + graph.addEdge(3, 4, edgeFactory.get()); + edgeCapacityMap.put(6, 3); + + graph.addEdge(3, 5, edgeFactory.get()); + edgeCapacityMap.put(7, 6); + + graph.addEdge(4, 5, edgeFactory.get()); + edgeCapacityMap.put(8, 1); + + EdmondsKarpMaxFlow ek = + new EdmondsKarpMaxFlow( + graph, + 0, + 5, + Functions.forMap(edgeCapacityMap, null), + edgeFlowMap, + edgeFactory); + ek.evaluate(); + + assertTrue(ek.getMaxFlow() == 7); + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/TestLattice2D.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/TestLattice2D.java index 7ce037b5..4c484c7d 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/TestLattice2D.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/TestLattice2D.java @@ -7,152 +7,149 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import com.google.common.base.Supplier; +import com.google.common.graph.Network; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; -import com.google.common.base.Supplier; -import com.google.common.graph.Network; - - @RunWith(Parameterized.class) public class TestLattice2D { - enum EdgeType { - UNDIRECTED, - DIRECTED - } - - enum Topology { - FLAT, - TOROIDAL - } - - private EdgeType edgeType; - private Topology topology; - - @Parameters - public static Object[][] data() { - return new Object[][] { - { UNDIRECTED, FLAT }, - { DIRECTED, FLAT }, - { UNDIRECTED, TOROIDAL }, - { DIRECTED, TOROIDAL } - }; - } - - public TestLattice2D(EdgeType edgeType, Topology topology) { - this.edgeType = edgeType; - this.topology = topology; - } - - protected Supplier vertexFactory; - protected Supplier edgeFactory; - - @Before - public void setUp() { - vertexFactory = new Supplier() { - int count; - public String get() { - return Character.toString((char)('a'+count++)); - } - }; - edgeFactory = - new Supplier() { - int count; - public Integer get() { - return count++; - } - }; - } - - @SuppressWarnings("rawtypes") - @Test - public void testCreateSingular() - { - try - { - @SuppressWarnings("unused") - Lattice2DGenerator unused = new Lattice2DGenerator(1, 2, toroidal()); - unused = new Lattice2DGenerator(2, 1, toroidal()); - fail("Did not reject lattice of size < 2"); - } - catch (IllegalArgumentException iae) {} - } - - - @Test - public void testGenerateNetwork() { - for (int rowCount = 4; rowCount <= 6; rowCount++) { - for (int colCount = 4; colCount <= 6; colCount++) { - Lattice2DGenerator generator = - new Lattice2DGenerator<>(rowCount, colCount, toroidal()); - Network graph = - generator.generateNetwork(directed(), vertexFactory, edgeFactory); - assertEquals(graph.nodes().size(), rowCount * colCount); - - int boundary_adjustment = (toroidal() ? 0 : 1); - int expectedEdgeCount = colCount * (rowCount - boundary_adjustment) + - rowCount * (colCount - boundary_adjustment); - if (directed()) { - expectedEdgeCount *= 2; - } - assertEquals(graph.edges().size(), expectedEdgeCount); - int expectedPerimeterNodes = toroidal() ? 0 : (rowCount - 1) * 2 + (colCount - 1) * 2; - int nodeCount = graph.nodes().size(); - int expectedInteriorNodes = toroidal() ? nodeCount : nodeCount - expectedPerimeterNodes; - int perimeterNodes = 0; - int interiorNodes = 0; - int degreeMultiplier = directed() ? 2 : 1; - for (String node : graph.nodes()) { - if (toroidal()) { - int expectedDegree = 4 * degreeMultiplier; - assertEquals(graph.degree(node), expectedDegree); - interiorNodes++; - } else { - int degree = graph.degree(node); - if (degree == 4 * degreeMultiplier) { - interiorNodes++; - } else if (degree == 3 * degreeMultiplier || degree == 2 * degreeMultiplier) { - perimeterNodes++; - } else { - String message = String.format("degree does not match expectations: " + - "degree: %s, multiplier: %s\n" + - "row count: %s, col count: %s, toroidal: %s\n" + - "graph: %s", degree, degreeMultiplier, rowCount, colCount, toroidal(), graph); - fail(message); - } - } - } - - assertEquals(expectedInteriorNodes, interiorNodes); - assertEquals(expectedPerimeterNodes, perimeterNodes); - } - } - } - - - - private boolean toroidal() { - switch (topology) { - case TOROIDAL: - return true; - case FLAT: - return false; - default: - throw new IllegalStateException("Unrecognized Topology type: " + topology); - } - } - - private boolean directed() { - switch (edgeType) { - case DIRECTED: - return true; - case UNDIRECTED: - return false; - default: - throw new IllegalStateException("Unrecognized edge type: " + edgeType); - } - } + enum EdgeType { + UNDIRECTED, + DIRECTED + } + + enum Topology { + FLAT, + TOROIDAL + } + + private EdgeType edgeType; + private Topology topology; + + @Parameters + public static Object[][] data() { + return new Object[][] { + {UNDIRECTED, FLAT}, {DIRECTED, FLAT}, {UNDIRECTED, TOROIDAL}, {DIRECTED, TOROIDAL} + }; + } + + public TestLattice2D(EdgeType edgeType, Topology topology) { + this.edgeType = edgeType; + this.topology = topology; + } + + protected Supplier vertexFactory; + protected Supplier edgeFactory; + + @Before + public void setUp() { + vertexFactory = + new Supplier() { + int count; + + public String get() { + return Character.toString((char) ('a' + count++)); + } + }; + edgeFactory = + new Supplier() { + int count; + + public Integer get() { + return count++; + } + }; + } + + @SuppressWarnings("rawtypes") + @Test + public void testCreateSingular() { + try { + @SuppressWarnings("unused") + Lattice2DGenerator unused = new Lattice2DGenerator(1, 2, toroidal()); + unused = new Lattice2DGenerator(2, 1, toroidal()); + fail("Did not reject lattice of size < 2"); + } catch (IllegalArgumentException iae) { + } + } + + @Test + public void testGenerateNetwork() { + for (int rowCount = 4; rowCount <= 6; rowCount++) { + for (int colCount = 4; colCount <= 6; colCount++) { + Lattice2DGenerator generator = + new Lattice2DGenerator<>(rowCount, colCount, toroidal()); + Network graph = + generator.generateNetwork(directed(), vertexFactory, edgeFactory); + assertEquals(graph.nodes().size(), rowCount * colCount); + + int boundary_adjustment = (toroidal() ? 0 : 1); + int expectedEdgeCount = + colCount * (rowCount - boundary_adjustment) + + rowCount * (colCount - boundary_adjustment); + if (directed()) { + expectedEdgeCount *= 2; + } + assertEquals(graph.edges().size(), expectedEdgeCount); + int expectedPerimeterNodes = toroidal() ? 0 : (rowCount - 1) * 2 + (colCount - 1) * 2; + int nodeCount = graph.nodes().size(); + int expectedInteriorNodes = toroidal() ? nodeCount : nodeCount - expectedPerimeterNodes; + int perimeterNodes = 0; + int interiorNodes = 0; + int degreeMultiplier = directed() ? 2 : 1; + for (String node : graph.nodes()) { + if (toroidal()) { + int expectedDegree = 4 * degreeMultiplier; + assertEquals(graph.degree(node), expectedDegree); + interiorNodes++; + } else { + int degree = graph.degree(node); + if (degree == 4 * degreeMultiplier) { + interiorNodes++; + } else if (degree == 3 * degreeMultiplier || degree == 2 * degreeMultiplier) { + perimeterNodes++; + } else { + String message = + String.format( + "degree does not match expectations: " + + "degree: %s, multiplier: %s\n" + + "row count: %s, col count: %s, toroidal: %s\n" + + "graph: %s", + degree, degreeMultiplier, rowCount, colCount, toroidal(), graph); + fail(message); + } + } + } + + assertEquals(expectedInteriorNodes, interiorNodes); + assertEquals(expectedPerimeterNodes, perimeterNodes); + } + } + } + + private boolean toroidal() { + switch (topology) { + case TOROIDAL: + return true; + case FLAT: + return false; + default: + throw new IllegalStateException("Unrecognized Topology type: " + topology); + } + } + + private boolean directed() { + switch (edgeType) { + case DIRECTED: + return true; + case UNDIRECTED: + return false; + default: + throw new IllegalStateException("Unrecognized edge type: " + edgeType); + } + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/random/TestBarabasiAlbert.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/random/TestBarabasiAlbert.java index 83fdc369..03e9f281 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/random/TestBarabasiAlbert.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/random/TestBarabasiAlbert.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, the JUNG Project and the Regents of the University + * Copyright (c) 2016, the JUNG Project and the Regents of the University * of California. All rights reserved. * * This software is open-source under the BSD license; see @@ -11,7 +11,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.graph.Network; import com.google.common.graph.NetworkBuilder; - import junit.framework.TestCase; /** @@ -20,126 +19,138 @@ * @author James Marchant */ public class TestBarabasiAlbert extends TestCase { - protected Supplier vertexFactory; - protected Supplier edgeFactory; - - protected int init_vertices = 1; - protected int edges_to_add_per_timestep = 1; - protected int random_seed = 0; - protected int num_timesteps = 10; - protected int num_tests = 10; - - @Override - protected void setUp() { - vertexFactory = new Supplier() { - int count; - - public Integer get() { - return count++; - } - }; - edgeFactory = new Supplier() { - int count; - - public Number get() { - return count++; - } - }; - } - - // TODO(jrtom): add tests for - // * parallel edges - // * undirected edges - // * ... - public void testDirected() - { - int init_vertices = 1; - int edges_to_add_per_timestep = 1; - int random_seed = 0; - int num_tests = 10; - int num_timesteps = 10; - - Supplier vertexFactory = - new Supplier() { - int count; - public Integer get() { - return count++; - }}; - Supplier edgeFactory = - new Supplier() { - int count; - public Number get() { - return count++; - }}; - - BarabasiAlbertGenerator generator = - new BarabasiAlbertGenerator<>(NetworkBuilder.directed(), vertexFactory, edgeFactory, - init_vertices, edges_to_add_per_timestep, random_seed); - for (int i = 1; i <= num_tests; i++) { - generator.evolveGraph(num_timesteps); - Network graph = generator.get(); - assertEquals(graph.nodes().size(), (i*num_timesteps) + init_vertices); - assertEquals(graph.edges().size(), edges_to_add_per_timestep * (i*num_timesteps)); - ImmutableSet seedNodes = generator.seedNodes(); - - for (Integer v : graph.nodes()) { - if (!seedNodes.contains(v)) { - // Every non-seed node should have an out-degree AT LEAST equal to the number of - // edges added per timestep (possibly more if the graph is undirected). - assertTrue(graph.outDegree(v) >= edges_to_add_per_timestep); - } - - // Check that not every edge goes to one node; the in-degree of any node - // should be strictly less than the number of edges. - assertTrue(graph.inDegree(v) < graph.edges().size()); - } - } + protected Supplier vertexFactory; + protected Supplier edgeFactory; + + protected int init_vertices = 1; + protected int edges_to_add_per_timestep = 1; + protected int random_seed = 0; + protected int num_timesteps = 10; + protected int num_tests = 10; + + @Override + protected void setUp() { + vertexFactory = + new Supplier() { + int count; + + public Integer get() { + return count++; + } + }; + edgeFactory = + new Supplier() { + int count; + + public Number get() { + return count++; + } + }; + } + + // TODO(jrtom): add tests for + // * parallel edges + // * undirected edges + // * ... + public void testDirected() { + int init_vertices = 1; + int edges_to_add_per_timestep = 1; + int random_seed = 0; + int num_tests = 10; + int num_timesteps = 10; + + Supplier vertexFactory = + new Supplier() { + int count; + + public Integer get() { + return count++; + } + }; + Supplier edgeFactory = + new Supplier() { + int count; + + public Number get() { + return count++; + } + }; + + BarabasiAlbertGenerator generator = + new BarabasiAlbertGenerator<>( + NetworkBuilder.directed(), + vertexFactory, + edgeFactory, + init_vertices, + edges_to_add_per_timestep, + random_seed); + for (int i = 1; i <= num_tests; i++) { + generator.evolveGraph(num_timesteps); + Network graph = generator.get(); + assertEquals(graph.nodes().size(), (i * num_timesteps) + init_vertices); + assertEquals(graph.edges().size(), edges_to_add_per_timestep * (i * num_timesteps)); + ImmutableSet seedNodes = generator.seedNodes(); + + for (Integer v : graph.nodes()) { + if (!seedNodes.contains(v)) { + // Every non-seed node should have an out-degree AT LEAST equal to the number of + // edges added per timestep (possibly more if the graph is undirected). + assertTrue(graph.outDegree(v) >= edges_to_add_per_timestep); + } + + // Check that not every edge goes to one node; the in-degree of any node + // should be strictly less than the number of edges. + assertTrue(graph.inDegree(v) < graph.edges().size()); + } } + } - @SuppressWarnings("unused") - public void testPreconditions() { - try { - BarabasiAlbertGenerator generator = - new BarabasiAlbertGenerator<>(NetworkBuilder.directed(), - vertexFactory, - edgeFactory, - 0, // init_vertices - edges_to_add_per_timestep, - random_seed); - fail("failed to reject init_vertices of <= 0"); - } catch (IllegalArgumentException e) { - // TODO: assert that the exception message contains "seed" - } - - // test edges_to_add_per_timestep = 0 - try { - BarabasiAlbertGenerator generator = - new BarabasiAlbertGenerator<>(NetworkBuilder.directed(), - vertexFactory, - edgeFactory, - init_vertices, - 0, // edges_to_add_per_timestep - random_seed); - fail("failed to reject edges_to_add_per_timestamp of <= 0"); - } catch (IllegalArgumentException e) { - // TODO: assert that the exception message is approx: - // "Number of edges to attach at each time step must be positive" - } - - // test edges_to_add_per_timestep > init_vertices - try { - int nodesToAdd = 5; - BarabasiAlbertGenerator generator = - new BarabasiAlbertGenerator<>(NetworkBuilder.directed(), - vertexFactory, - edgeFactory, - nodesToAdd, // init_vertices - nodesToAdd + 1, // edges_to_add_per_timestep - random_seed); - fail("failed to reject edges_to_add_per_timestamp of > init_vertices"); - } catch (IllegalArgumentException e) { - // TODO: assert that the exception message is appropriate (see above) - } - } + @SuppressWarnings("unused") + public void testPreconditions() { + try { + BarabasiAlbertGenerator generator = + new BarabasiAlbertGenerator<>( + NetworkBuilder.directed(), + vertexFactory, + edgeFactory, + 0, // init_vertices + edges_to_add_per_timestep, + random_seed); + fail("failed to reject init_vertices of <= 0"); + } catch (IllegalArgumentException e) { + // TODO: assert that the exception message contains "seed" + } + + // test edges_to_add_per_timestep = 0 + try { + BarabasiAlbertGenerator generator = + new BarabasiAlbertGenerator<>( + NetworkBuilder.directed(), + vertexFactory, + edgeFactory, + init_vertices, + 0, // edges_to_add_per_timestep + random_seed); + fail("failed to reject edges_to_add_per_timestamp of <= 0"); + } catch (IllegalArgumentException e) { + // TODO: assert that the exception message is approx: + // "Number of edges to attach at each time step must be positive" + } + // test edges_to_add_per_timestep > init_vertices + try { + int nodesToAdd = 5; + BarabasiAlbertGenerator generator = + new BarabasiAlbertGenerator<>( + NetworkBuilder.directed(), + vertexFactory, + edgeFactory, + nodesToAdd, // init_vertices + nodesToAdd + 1, // edges_to_add_per_timestep + random_seed); + fail("failed to reject edges_to_add_per_timestamp of > init_vertices"); + } catch (IllegalArgumentException e) { + // TODO: assert that the exception message is appropriate (see above) + } + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/random/TestEppsteinPowerLawGenerator.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/random/TestEppsteinPowerLawGenerator.java index 15dea63d..d977806b 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/random/TestEppsteinPowerLawGenerator.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/random/TestEppsteinPowerLawGenerator.java @@ -1,90 +1,87 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.generators.random; import com.google.common.base.Supplier; import com.google.common.graph.Graph; - import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; - -/** - * @author Scott White - */ +/** @author Scott White */ public class TestEppsteinPowerLawGenerator extends TestCase { - Supplier vertexFactory; + Supplier vertexFactory; - public static Test suite() { - return new TestSuite(TestEppsteinPowerLawGenerator.class); - } + public static Test suite() { + return new TestSuite(TestEppsteinPowerLawGenerator.class); + } - @Override - protected void setUp() { - vertexFactory = new Supplier() { - int count; - public Integer get() { - return count++; - } - }; - } + @Override + protected void setUp() { + vertexFactory = + new Supplier() { + int count; - public void testSimpleDirectedCase() { + public Integer get() { + return count++; + } + }; + } - for (int r=0; r<10; r++) { - EppsteinPowerLawGenerator generator = - new EppsteinPowerLawGenerator(vertexFactory, 10,40,r); - generator.setSeed(2); + public void testSimpleDirectedCase() { - Graph graph = generator.get(); - Assert.assertEquals(graph.nodes().size(),10); - Assert.assertEquals(graph.edges().size(),40); - } + for (int r = 0; r < 10; r++) { + EppsteinPowerLawGenerator generator = + new EppsteinPowerLawGenerator(vertexFactory, 10, 40, r); + generator.setSeed(2); + Graph graph = generator.get(); + Assert.assertEquals(graph.nodes().size(), 10); + Assert.assertEquals(graph.edges().size(), 40); } + } - // TODO: convert what is needed for this test -// public void testPowerLawProperties() { -// -// //long start = System.currentTimeMillis(); -// EppsteinPowerLawGenerator generator = new EppsteinPowerLawGenerator(vertexFactory, edgeFactory, -// 500,1500,100000); -// generator.setSeed(5); -// Graph graph = (Graph) generator.generateGraph(); -// //long stop = System.currentTimeMillis(); -// //System.out.println((stop-start)/1000l); -// -// DoubleArrayList degreeList = DegreeDistributions.getOutdegreeValues(graph.getVertices()); -// int maxDegree = (int) Descriptive.max(degreeList); -// Histogram degreeHistogram = GraphStatistics.createHistogram(degreeList,0,maxDegree,1); -// //for (int index=0;index degreeHistogram.binHeight(2) + degreeHistogram.binHeight(3)); -// -// generator = new EppsteinPowerLawGenerator(500,1500,0); -// graph = (Graph) generator.generateGraph(); -// degreeList = DegreeDistributions.getOutdegreeValues(graph.getVertices()); -// maxDegree = (int) Descriptive.max(degreeList); -// degreeHistogram = GraphStatistics.createHistogram(degreeList,0,maxDegree,1); -// //for (int index=0;index degreeHistogram.binHeight(2) + degreeHistogram.binHeight(3)); + // + // generator = new EppsteinPowerLawGenerator(500,1500,0); + // graph = (Graph) generator.generateGraph(); + // degreeList = DegreeDistributions.getOutdegreeValues(graph.getVertices()); + // maxDegree = (int) Descriptive.max(degreeList); + // degreeHistogram = GraphStatistics.createHistogram(degreeList,0,maxDegree,1); + // //for (int index=0;index vertexFactory; - public static Test suite() { - return new TestSuite(TestErdosRenyi.class); - } + Supplier vertexFactory; + + public static Test suite() { + return new TestSuite(TestErdosRenyi.class); + } - @Override + @Override protected void setUp() { - vertexFactory = new Supplier() { - int count; - public String get() { - return Character.toString((char)('A'+count++)); - } - }; - } - - public void test() { - - int numVertices = 100; - int total = 0; - for (int i = 1; i <= 10; i++) { - ErdosRenyiGenerator generator = - new ErdosRenyiGenerator<>(vertexFactory, numVertices,0.1); - generator.setSeed(0); - - Graph graph = generator.get(); - Assert.assertTrue(graph.nodes().size() == numVertices); - total += graph.edges().size(); - } - total /= 10.0; - Assert.assertTrue(total > 495-50 && total < 495+50); - - } - - + vertexFactory = + new Supplier() { + int count; + + public String get() { + return Character.toString((char) ('A' + count++)); + } + }; + } + + public void test() { + + int numVertices = 100; + int total = 0; + for (int i = 1; i <= 10; i++) { + ErdosRenyiGenerator generator = + new ErdosRenyiGenerator<>(vertexFactory, numVertices, 0.1); + generator.setSeed(0); + + Graph graph = generator.get(); + Assert.assertTrue(graph.nodes().size() == numVertices); + total += graph.edges().size(); + } + total /= 10.0; + Assert.assertTrue(total > 495 - 50 && total < 495 + 50); + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/random/TestKleinberg.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/random/TestKleinberg.java index 3b4cbad7..0b568f95 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/random/TestKleinberg.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/generators/random/TestKleinberg.java @@ -1,60 +1,56 @@ package edu.uci.ics.jung.algorithms.generators.random; - import static org.junit.Assert.assertEquals; +import com.google.common.base.Supplier; +import com.google.common.graph.MutableNetwork; +import edu.uci.ics.jung.algorithms.generators.Lattice2DGenerator; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import com.google.common.base.Supplier; -import com.google.common.graph.MutableNetwork; - -import edu.uci.ics.jung.algorithms.generators.Lattice2DGenerator; - -/** - * - * @author Joshua O'Madadhain - */ +/** @author Joshua O'Madadhain */ @RunWith(JUnit4.class) public class TestKleinberg { - protected Supplier vertexFactory; - protected Supplier edgeFactory; - - @Before - public void setUp() { - vertexFactory = new Supplier() { - int count; - public String get() { - return Character.toString((char)('a'+count++)); - } - }; - edgeFactory = - new Supplier() { - int count; - public Integer get() { - return count++; - } - }; - } - - @Test - public void testConnectionCount() { - Lattice2DGenerator generator = - new Lattice2DGenerator<>(4, 4, true /* toroidal */); - MutableNetwork graph = - generator.generateNetwork(true /* directed */, vertexFactory, edgeFactory); - final int connectionCount = 2; - - KleinbergSmallWorld ksw = - KleinbergSmallWorld.builder().connectionCount(connectionCount).build(); - ksw.addSmallWorldConnections(graph, generator.distance(graph.asGraph()), edgeFactory); - - for (String node : graph.nodes()) { - assertEquals(graph.outDegree(node), 4 + connectionCount); - } - } - + protected Supplier vertexFactory; + protected Supplier edgeFactory; + + @Before + public void setUp() { + vertexFactory = + new Supplier() { + int count; + + public String get() { + return Character.toString((char) ('a' + count++)); + } + }; + edgeFactory = + new Supplier() { + int count; + + public Integer get() { + return count++; + } + }; + } + + @Test + public void testConnectionCount() { + Lattice2DGenerator generator = + new Lattice2DGenerator<>(4, 4, true /* toroidal */); + MutableNetwork graph = + generator.generateNetwork(true /* directed */, vertexFactory, edgeFactory); + final int connectionCount = 2; + + KleinbergSmallWorld ksw = + KleinbergSmallWorld.builder().connectionCount(connectionCount).build(); + ksw.addSmallWorldConnections(graph, generator.distance(graph.asGraph()), edgeFactory); + + for (String node : graph.nodes()) { + assertEquals(graph.outDegree(node), 4 + connectionCount); + } + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/layout/FRLayout2Test.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/layout/FRLayout2Test.java index 9b0093f1..019bec36 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/layout/FRLayout2Test.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/layout/FRLayout2Test.java @@ -1,32 +1,30 @@ package edu.uci.ics.jung.algorithms.layout; -import java.awt.Dimension; -import java.util.HashSet; -import java.util.Set; - import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.util.Relaxer; import edu.uci.ics.jung.algorithms.layout.util.VisRunner; import edu.uci.ics.jung.algorithms.util.IterativeContext; import edu.uci.ics.jung.graph.util.TestGraphs; +import java.awt.Dimension; +import java.util.HashSet; +import java.util.Set; import junit.framework.TestCase; public class FRLayout2Test extends TestCase { - - protected Set seedVertices = new HashSet(); - public void testFRLayout() { - - Network graph = TestGraphs.getOneComponentGraph(); + protected Set seedVertices = new HashSet(); + + public void testFRLayout() { + + Network graph = TestGraphs.getOneComponentGraph(); - Layout layout = new FRLayout2(graph.asGraph()); - layout.setSize(new Dimension(600,600)); - if(layout instanceof IterativeContext) { - layout.initialize(); - Relaxer relaxer = new VisRunner((IterativeContext)layout); - relaxer.prerelax(); - relaxer.relax(); - } - } + Layout layout = new FRLayout2(graph.asGraph()); + layout.setSize(new Dimension(600, 600)); + if (layout instanceof IterativeContext) { + layout.initialize(); + Relaxer relaxer = new VisRunner((IterativeContext) layout); + relaxer.prerelax(); + relaxer.relax(); + } + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/layout/FRLayoutTest.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/layout/FRLayoutTest.java index b885833b..8002d9dd 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/layout/FRLayoutTest.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/layout/FRLayoutTest.java @@ -1,32 +1,30 @@ package edu.uci.ics.jung.algorithms.layout; -import java.awt.Dimension; -import java.util.HashSet; -import java.util.Set; - import com.google.common.graph.Network; - -import junit.framework.TestCase; import edu.uci.ics.jung.algorithms.layout.util.Relaxer; import edu.uci.ics.jung.algorithms.layout.util.VisRunner; import edu.uci.ics.jung.algorithms.util.IterativeContext; import edu.uci.ics.jung.graph.util.TestGraphs; +import java.awt.Dimension; +import java.util.HashSet; +import java.util.Set; +import junit.framework.TestCase; public class FRLayoutTest extends TestCase { - - protected Set seedVertices = new HashSet(); - public void testFRLayout() { - - Network graph = TestGraphs.getOneComponentGraph(); + protected Set seedVertices = new HashSet(); + + public void testFRLayout() { + + Network graph = TestGraphs.getOneComponentGraph(); - Layout layout = new FRLayout(graph.asGraph()); - layout.setSize(new Dimension(600,600)); - if(layout instanceof IterativeContext) { - layout.initialize(); - Relaxer relaxer = new VisRunner((IterativeContext)layout); - relaxer.prerelax(); - relaxer.relax(); - } - } + Layout layout = new FRLayout(graph.asGraph()); + layout.setSize(new Dimension(600, 600)); + if (layout instanceof IterativeContext) { + layout.initialize(); + Relaxer relaxer = new VisRunner((IterativeContext) layout); + relaxer.prerelax(); + relaxer.relax(); + } + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/metrics/TestTriad.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/metrics/TestTriad.java index 61f4f468..a113277c 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/metrics/TestTriad.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/metrics/TestTriad.java @@ -2,165 +2,164 @@ import com.google.common.graph.GraphBuilder; import com.google.common.graph.MutableGraph; - import junit.framework.TestCase; public class TestTriad extends TestCase { - public void testConfigurationFromPaper() { - MutableGraph g = GraphBuilder.directed().build(); - char u = 'u'; - g.addNode(u); - char v = 'v'; - g.addNode(v); - char w = 'w'; - g.addNode(w); - g.putEdge(w, u); - g.putEdge(u, v); - g.putEdge(v, u); - - assertEquals(35, TriadicCensus.triCode(g, u, v, w)); - assertEquals(7, TriadicCensus.triType(35)); - assertEquals("111D", TriadicCensus.TRIAD_NAMES[7]); - - assertEquals(7, TriadicCensus.triType(TriadicCensus.triCode(g, u, w, v))); - assertEquals(7, TriadicCensus.triType(TriadicCensus.triCode(g, v, u, w))); - - long[] counts = TriadicCensus.getCounts(g); - - for (int i = 1; i <= 16; i++) { - if (i == 7) { - assertEquals(1, counts[i]); - } else { - assertEquals(0, counts[i]); - } - } - } - - public void testFourVertexGraph() { - // we'll set up a graph of - // t->u - // u->v - // and that's it. - // total count: - // 2: 1(t, u, w)(u, v, w) - // 6: 1(t, u, v) - // 1: 1(u, v, w) - MutableGraph g = GraphBuilder.directed().build(); - char u = 'u'; - g.addNode(u); - char v = 'v'; - g.addNode(v); - char w = 'w'; - g.addNode(w); - char t = 't'; - g.addNode(t); - - g.putEdge(t, u ); - g.putEdge(u, v ); - - long[] counts = TriadicCensus.getCounts(g); - for (int i = 1; i <= 16; i++) { - if( i == 2 ) { - assertEquals("On " + i, 2, counts[i]); - } else if (i == 6 || i == 1 ) { - assertEquals("On " + i, 1, counts[i]); - } else { - assertEquals(0, counts[i]); - } - } - - // now let's tweak to - // t->u, u->v, v->t - // w->u, v->w - g.putEdge(v, t ); - g.putEdge(w, u ); - g.putEdge(v, w ); - - // that's two 030Cs. it's a 021D (v-t, v-w) and an 021U (t-u, w-u) - counts = TriadicCensus.getCounts(g); - - for (int i = 1; i <= 16; i++) { - if( i == 10 /* 030C */ ) { - assertEquals("On " + i, 2, counts[i]); - } else if (i == 4 || i == 5 ) { - assertEquals("On " + i, 1, counts[i]); - } else { - assertEquals("On " + i , 0, counts[i]); - } - } - } - - public void testThreeDotsThreeDashes() { - MutableGraph g = GraphBuilder.directed().build(); - char u = 'u'; - g.addNode(u); - char v = 'v'; - g.addNode(v); - char w = 'w'; - g.addNode(w); - - long[] counts = TriadicCensus.getCounts(g); - - for (int i = 1; i <= 16; i++) { - if (i == 1) { - assertEquals(1, counts[i]); - } else { - assertEquals(0, counts[i]); - } - } - - g.putEdge(v, u); - g.putEdge(u, v); - g.putEdge(v, w); - g.putEdge(w, v); - g.putEdge(u, w); - g.putEdge(w, u); - - counts = TriadicCensus.getCounts(g); - - for (int i = 1; i <= 16; i++) { - if (i == 16) { - assertEquals(1, counts[i]); - } else { - assertEquals("Count on " + i + " failed", 0, counts[i]); - } - } - } - - /** **************Boring accounting for zero graphs*********** */ - public void testNull() { - MutableGraph g = GraphBuilder.directed().build(); - long[] counts = TriadicCensus.getCounts(g); - - // t looks like a hashtable for the twelve keys - for (int i = 1; i < TriadicCensus.MAX_TRIADS; i++) { - assertEquals("Empty Graph doesn't have count 0", 0, counts[i]); - } - } - - public void testOneVertex() { - MutableGraph g = GraphBuilder.directed().build(); - g.addNode('u'); - long[] counts = TriadicCensus.getCounts(g); - - // t looks like a hashtable for the twelve keys - for (int i = 1; i < TriadicCensus.MAX_TRIADS; i++) { - assertEquals("One vertex Graph doesn't have count 0", 0, counts[i]); - } - } - - public void testTwoVertices() { - MutableGraph g = GraphBuilder.directed().build(); - char v1, v2; - g.addNode(v1 = 'u'); - g.addNode(v2 = 'v'); - g.putEdge(v1, v2); - long[] counts = TriadicCensus.getCounts(g); - - // t looks like a hashtable for the twelve keys - for (int i = 1; i < TriadicCensus.MAX_TRIADS; i++) { - assertEquals("Two vertex Graph doesn't have count 0", 0, counts[i]); - } - } + public void testConfigurationFromPaper() { + MutableGraph g = GraphBuilder.directed().build(); + char u = 'u'; + g.addNode(u); + char v = 'v'; + g.addNode(v); + char w = 'w'; + g.addNode(w); + g.putEdge(w, u); + g.putEdge(u, v); + g.putEdge(v, u); + + assertEquals(35, TriadicCensus.triCode(g, u, v, w)); + assertEquals(7, TriadicCensus.triType(35)); + assertEquals("111D", TriadicCensus.TRIAD_NAMES[7]); + + assertEquals(7, TriadicCensus.triType(TriadicCensus.triCode(g, u, w, v))); + assertEquals(7, TriadicCensus.triType(TriadicCensus.triCode(g, v, u, w))); + + long[] counts = TriadicCensus.getCounts(g); + + for (int i = 1; i <= 16; i++) { + if (i == 7) { + assertEquals(1, counts[i]); + } else { + assertEquals(0, counts[i]); + } + } + } + + public void testFourVertexGraph() { + // we'll set up a graph of + // t->u + // u->v + // and that's it. + // total count: + // 2: 1(t, u, w)(u, v, w) + // 6: 1(t, u, v) + // 1: 1(u, v, w) + MutableGraph g = GraphBuilder.directed().build(); + char u = 'u'; + g.addNode(u); + char v = 'v'; + g.addNode(v); + char w = 'w'; + g.addNode(w); + char t = 't'; + g.addNode(t); + + g.putEdge(t, u); + g.putEdge(u, v); + + long[] counts = TriadicCensus.getCounts(g); + for (int i = 1; i <= 16; i++) { + if (i == 2) { + assertEquals("On " + i, 2, counts[i]); + } else if (i == 6 || i == 1) { + assertEquals("On " + i, 1, counts[i]); + } else { + assertEquals(0, counts[i]); + } + } + + // now let's tweak to + // t->u, u->v, v->t + // w->u, v->w + g.putEdge(v, t); + g.putEdge(w, u); + g.putEdge(v, w); + + // that's two 030Cs. it's a 021D (v-t, v-w) and an 021U (t-u, w-u) + counts = TriadicCensus.getCounts(g); + + for (int i = 1; i <= 16; i++) { + if (i == 10 /* 030C */) { + assertEquals("On " + i, 2, counts[i]); + } else if (i == 4 || i == 5) { + assertEquals("On " + i, 1, counts[i]); + } else { + assertEquals("On " + i, 0, counts[i]); + } + } + } + + public void testThreeDotsThreeDashes() { + MutableGraph g = GraphBuilder.directed().build(); + char u = 'u'; + g.addNode(u); + char v = 'v'; + g.addNode(v); + char w = 'w'; + g.addNode(w); + + long[] counts = TriadicCensus.getCounts(g); + + for (int i = 1; i <= 16; i++) { + if (i == 1) { + assertEquals(1, counts[i]); + } else { + assertEquals(0, counts[i]); + } + } + + g.putEdge(v, u); + g.putEdge(u, v); + g.putEdge(v, w); + g.putEdge(w, v); + g.putEdge(u, w); + g.putEdge(w, u); + + counts = TriadicCensus.getCounts(g); + + for (int i = 1; i <= 16; i++) { + if (i == 16) { + assertEquals(1, counts[i]); + } else { + assertEquals("Count on " + i + " failed", 0, counts[i]); + } + } + } + + /** **************Boring accounting for zero graphs*********** */ + public void testNull() { + MutableGraph g = GraphBuilder.directed().build(); + long[] counts = TriadicCensus.getCounts(g); + + // t looks like a hashtable for the twelve keys + for (int i = 1; i < TriadicCensus.MAX_TRIADS; i++) { + assertEquals("Empty Graph doesn't have count 0", 0, counts[i]); + } + } + + public void testOneVertex() { + MutableGraph g = GraphBuilder.directed().build(); + g.addNode('u'); + long[] counts = TriadicCensus.getCounts(g); + + // t looks like a hashtable for the twelve keys + for (int i = 1; i < TriadicCensus.MAX_TRIADS; i++) { + assertEquals("One vertex Graph doesn't have count 0", 0, counts[i]); + } + } + + public void testTwoVertices() { + MutableGraph g = GraphBuilder.directed().build(); + char v1, v2; + g.addNode(v1 = 'u'); + g.addNode(v2 = 'v'); + g.putEdge(v1, v2); + long[] counts = TriadicCensus.getCounts(g); + + // t looks like a hashtable for the twelve keys + for (int i = 1; i < TriadicCensus.MAX_TRIADS; i++) { + assertEquals("Two vertex Graph doesn't have count 0", 0, counts[i]); + } + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestBetweennessCentrality.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestBetweennessCentrality.java index 4e8ab45f..8fea92cc 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestBetweennessCentrality.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestBetweennessCentrality.java @@ -1,134 +1,128 @@ /** - * Copyright (c) 2008, The JUNG Authors + * Copyright (c) 2008, The JUNG Authors * - * All rights reserved. + *

    All rights reserved. * - * This software is open-source under the BSD license; see either - * "license.txt" or - * https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * Created on Sep 17, 2008 - * + *

    This software is open-source under the BSD license; see either "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. Created on Sep 17, 2008 */ package edu.uci.ics.jung.algorithms.scoring; import com.google.common.base.Function; import com.google.common.graph.MutableNetwork; import com.google.common.graph.NetworkBuilder; - import junit.framework.TestCase; -/** - * - */ -public class TestBetweennessCentrality extends TestCase -{ -// public void testUndirected() { -// UndirectedGraph graph = -// new UndirectedSparseGraph(); -// for(int i=0; i<9; i++) { -// graph.addVertex(i); -// } -// -// int edge = 0; -// graph.addEdge(edge++, 0,1); -// graph.addEdge(edge++, 0,6); -// graph.addEdge(edge++, 1,2); -// graph.addEdge(edge++, 1,3); -// graph.addEdge(edge++, 2,4); -// graph.addEdge(edge++, 3,4); -// graph.addEdge(edge++, 4,5); -// graph.addEdge(edge++, 5,8); -// graph.addEdge(edge++, 7,8); -// graph.addEdge(edge++, 6,7); -// -// BetweennessCentrality bc = -// new BetweennessCentrality(graph); -// -//// System.out.println("scoring"); -//// for (int i = 0; i < graph.getVertexCount(); i++) -//// System.out.println(String.format("%d: %f", i, bc.getVertexScore(i))); -// -// Assert.assertEquals(bc.getVertexScore(0),6.000,.001); -// Assert.assertEquals(bc.getVertexScore(1),7.833,.001); -// Assert.assertEquals(bc.getVertexScore(2),2.500,.001); -// Assert.assertEquals(bc.getVertexScore(3),2.500,.001); -// Assert.assertEquals(bc.getVertexScore(4),7.833,.001); -// Assert.assertEquals(bc.getVertexScore(5),6.000,.001); -// Assert.assertEquals(bc.getVertexScore(6),4.666,.001); -// Assert.assertEquals(bc.getVertexScore(7),4.000,.001); -// Assert.assertEquals(bc.getVertexScore(8),4.666,.001); -// -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(0,1)),10.666,.001); -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(0,6)),9.333,.001); -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(1,2)),6.500,.001); -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(1,3)),6.500,.001); -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(2,4)),6.500,.001); -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(3,4)),6.500,.001); -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(4,5)),10.666,.001); -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(5,8)),9.333,.001); -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(6,7)),8.000,.001); -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(7,8)),8.000,.001); -// } -// -// public void testDirected() -// { -// DirectedGraph graph = new DirectedSparseGraph(); -// for(int i=0; i<5; i++) -// graph.addVertex(i); -// -// int edge=0; -// graph.addEdge(edge++, 0,1); -// graph.addEdge(edge++, 1,2); -// graph.addEdge(edge++, 3,1); -// graph.addEdge(edge++, 4,2); -// -// BetweennessCentrality bc = -// new BetweennessCentrality(graph); -// -// Assert.assertEquals(bc.getVertexScore(0),0,.001); -// Assert.assertEquals(bc.getVertexScore(1),2,.001); -// Assert.assertEquals(bc.getVertexScore(2),0,.001); -// Assert.assertEquals(bc.getVertexScore(3),0,.001); -// Assert.assertEquals(bc.getVertexScore(4),0,.001); -// -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(0,1)),2,.001); -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(1,2)),3,.001); -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(3,1)),2,.001); -// Assert.assertEquals(bc.getEdgeScore(graph.findEdge(4,2)),1,.001); -// } - - public void testWeighted() - { - MutableNetwork graph = NetworkBuilder.directed().build(); - - char edge='a'; - graph.addEdge(0, 1, edge++); - graph.addEdge(0, 2, edge++); - graph.addEdge(2, 3, edge++); - graph.addEdge(3, 1, edge++); - graph.addEdge(1, 4, edge++); +/** */ +public class TestBetweennessCentrality extends TestCase { + // public void testUndirected() { + // UndirectedGraph graph = + // new UndirectedSparseGraph(); + // for(int i=0; i<9; i++) { + // graph.addVertex(i); + // } + // + // int edge = 0; + // graph.addEdge(edge++, 0,1); + // graph.addEdge(edge++, 0,6); + // graph.addEdge(edge++, 1,2); + // graph.addEdge(edge++, 1,3); + // graph.addEdge(edge++, 2,4); + // graph.addEdge(edge++, 3,4); + // graph.addEdge(edge++, 4,5); + // graph.addEdge(edge++, 5,8); + // graph.addEdge(edge++, 7,8); + // graph.addEdge(edge++, 6,7); + // + // BetweennessCentrality bc = + // new BetweennessCentrality(graph); + // + //// System.out.println("scoring"); + //// for (int i = 0; i < graph.getVertexCount(); i++) + //// System.out.println(String.format("%d: %f", i, bc.getVertexScore(i))); + // + // Assert.assertEquals(bc.getVertexScore(0),6.000,.001); + // Assert.assertEquals(bc.getVertexScore(1),7.833,.001); + // Assert.assertEquals(bc.getVertexScore(2),2.500,.001); + // Assert.assertEquals(bc.getVertexScore(3),2.500,.001); + // Assert.assertEquals(bc.getVertexScore(4),7.833,.001); + // Assert.assertEquals(bc.getVertexScore(5),6.000,.001); + // Assert.assertEquals(bc.getVertexScore(6),4.666,.001); + // Assert.assertEquals(bc.getVertexScore(7),4.000,.001); + // Assert.assertEquals(bc.getVertexScore(8),4.666,.001); + // + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(0,1)),10.666,.001); + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(0,6)),9.333,.001); + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(1,2)),6.500,.001); + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(1,3)),6.500,.001); + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(2,4)),6.500,.001); + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(3,4)),6.500,.001); + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(4,5)),10.666,.001); + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(5,8)),9.333,.001); + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(6,7)),8.000,.001); + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(7,8)),8.000,.001); + // } + // + // public void testDirected() + // { + // DirectedGraph graph = new DirectedSparseGraph(); + // for(int i=0; i<5; i++) + // graph.addVertex(i); + // + // int edge=0; + // graph.addEdge(edge++, 0,1); + // graph.addEdge(edge++, 1,2); + // graph.addEdge(edge++, 3,1); + // graph.addEdge(edge++, 4,2); + // + // BetweennessCentrality bc = + // new BetweennessCentrality(graph); + // + // Assert.assertEquals(bc.getVertexScore(0),0,.001); + // Assert.assertEquals(bc.getVertexScore(1),2,.001); + // Assert.assertEquals(bc.getVertexScore(2),0,.001); + // Assert.assertEquals(bc.getVertexScore(3),0,.001); + // Assert.assertEquals(bc.getVertexScore(4),0,.001); + // + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(0,1)),2,.001); + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(1,2)),3,.001); + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(3,1)),2,.001); + // Assert.assertEquals(bc.getEdgeScore(graph.findEdge(4,2)),1,.001); + // } + + public void testWeighted() { + MutableNetwork graph = NetworkBuilder.directed().build(); + + char edge = 'a'; + graph.addEdge(0, 1, edge++); + graph.addEdge(0, 2, edge++); + graph.addEdge(2, 3, edge++); + graph.addEdge(3, 1, edge++); + graph.addEdge(1, 4, edge++); + + final int weights[] = {1, 1, 1, 1, 1}; + + Function edge_weights = + new Function() { + public Integer apply(Character arg0) { + return weights[arg0 - 'a']; + } + }; - final int weights[] = {1, 1, 1, 1, 1}; - - Function edge_weights = new Function() - { - public Integer apply(Character arg0) { return weights[arg0 - 'a']; } - }; - - BetweennessCentrality bc = - new BetweennessCentrality(graph, edge_weights); + BetweennessCentrality bc = + new BetweennessCentrality(graph, edge_weights); -// System.out.println("scoring"); -// System.out.println("(weighted)"); -// System.out.println("vertices:"); -// for (int i = 0; i < graph.getVertexCount(); i++) -// System.out.println(String.format("%d: %f", i, bc.getVertexScore(i))); -// System.out.println("edges:"); -// for (int i = 0; i < graph.getEdgeCount(); i++) -// { -// char e = (char)(i + 'a'); -// System.out.println(String.format("%c: (weight: %d), %f", e, -// edge_weights.apply(e), bc.getEdgeScore(e))); -// } - } + // System.out.println("scoring"); + // System.out.println("(weighted)"); + // System.out.println("vertices:"); + // for (int i = 0; i < graph.getVertexCount(); i++) + // System.out.println(String.format("%d: %f", i, bc.getVertexScore(i))); + // System.out.println("edges:"); + // for (int i = 0; i < graph.getEdgeCount(); i++) + // { + // char e = (char)(i + 'a'); + // System.out.println(String.format("%c: (weight: %d), %f", e, + // edge_weights.apply(e), bc.getEdgeScore(e))); + // } + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestHITS.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestHITS.java index 9ade7bd9..bd2078ae 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestHITS.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestHITS.java @@ -1,125 +1,120 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.scoring; import com.google.common.graph.MutableNetwork; import com.google.common.graph.NetworkBuilder; - import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; - /** * @author Scott White * @author Tom Nelson - adapted to jung2 */ public class TestHITS extends TestCase { - MutableNetwork graph; - - public static Test suite() { - return new TestSuite(TestHITS.class); - } + MutableNetwork graph; - @Override - protected void setUp() { - graph = NetworkBuilder.directed().allowsParallelEdges(true).build(); - for(int i=0; i<5; i++) { - graph.addNode(i); - } + public static Test suite() { + return new TestSuite(TestHITS.class); + } - int j=0; - graph.addEdge(0, 1, j++); - graph.addEdge(1, 2, j++); - graph.addEdge(2, 3, j++); - graph.addEdge(3, 0, j++); - graph.addEdge(2, 1, j++); + @Override + protected void setUp() { + graph = NetworkBuilder.directed().allowsParallelEdges(true).build(); + for (int i = 0; i < 5; i++) { + graph.addNode(i); } - // TODO(jrtom): add tests for - // * undirected graph - // * self-loops - // * parallel edges - // * weighted edges - public void testRanker() { + int j = 0; + graph.addEdge(0, 1, j++); + graph.addEdge(1, 2, j++); + graph.addEdge(2, 3, j++); + graph.addEdge(3, 0, j++); + graph.addEdge(2, 1, j++); + } - HITS ranker = new HITS(graph); - for (int i = 0; i < 10; i++) - { - ranker.step(); -// // check hub scores in terms of previous authority scores -// Assert.assertEquals(t.transform(0).hub, -// 0.5*ranker.getAuthScore(1) + 0.2*ranker.getAuthScore(4)); -// Assert.assertEquals(t.transform(1).hub, -// ranker.getAuthScore(2) + 0.2*ranker.getAuthScore(4)); -// Assert.assertEquals(t.transform(2).hub, -// 0.5*ranker.getAuthScore(1) + ranker.getAuthScore(3) + 0.2*ranker.getAuthScore(4)); -// Assert.assertEquals(t.transform(3).hub, -// ranker.getAuthScore(0) + 0.2*ranker.getAuthScore(4)); -// Assert.assertEquals(t.transform(4).hub, -// 0.2*ranker.getAuthScore(4)); -// -// // check authority scores in terms of previous hub scores -// Assert.assertEquals(t.transform(0).authority, -// ranker.getVertexScore(3) + 0.2*ranker.getVertexScore(4)); -// Assert.assertEquals(t.transform(1).authority, -// ranker.getVertexScore(0) + 0.5 * ranker.getVertexScore(2) + 0.2*ranker.getVertexScore(4)); -// Assert.assertEquals(t.transform(2).authority, -// ranker.getVertexScore(1) + 0.2*ranker.getVertexScore(4)); -// Assert.assertEquals(t.transform(3).authority, -// 0.5*ranker.getVertexScore(2) + 0.2*ranker.getVertexScore(4)); -// Assert.assertEquals(t.transform(4).authority, -// 0.2*ranker.getVertexScore(4)); -// - // verify that sums of each scores are 1.0 - double auth_sum = 0; - double hub_sum = 0; - for (int j = 0; j < 5; j++) - { -// auth_sum += ranker.getAuthScore(j); -// hub_sum += ranker.getVertexScore(j); -// auth_sum += (ranker.getAuthScore(j) * ranker.getAuthScore(j)); -// hub_sum += (ranker.getVertexScore(j) * ranker.getVertexScore(j)); - HITS.Scores score = ranker.getVertexScore(j); - auth_sum += score.authority * score.authority; - hub_sum += score.hub * score.hub; - } - Assert.assertEquals(auth_sum, 1.0, 0.0001); - Assert.assertEquals(hub_sum, 1.0, 0.0001); - } - - ranker.evaluate(); + // TODO(jrtom): add tests for + // * undirected graph + // * self-loops + // * parallel edges + // * weighted edges + public void testRanker() { - Assert.assertEquals(ranker.getVertexScore(0).authority, 0, .0001); - Assert.assertEquals(ranker.getVertexScore(1).authority, 0.8507, .001); - Assert.assertEquals(ranker.getVertexScore(2).authority, 0.0, .0001); - Assert.assertEquals(ranker.getVertexScore(3).authority, 0.5257, .001); + HITS ranker = new HITS(graph); + for (int i = 0; i < 10; i++) { + ranker.step(); + // // check hub scores in terms of previous authority scores + // Assert.assertEquals(t.transform(0).hub, + // 0.5*ranker.getAuthScore(1) + 0.2*ranker.getAuthScore(4)); + // Assert.assertEquals(t.transform(1).hub, + // ranker.getAuthScore(2) + 0.2*ranker.getAuthScore(4)); + // Assert.assertEquals(t.transform(2).hub, + // 0.5*ranker.getAuthScore(1) + ranker.getAuthScore(3) + 0.2*ranker.getAuthScore(4)); + // Assert.assertEquals(t.transform(3).hub, + // ranker.getAuthScore(0) + 0.2*ranker.getAuthScore(4)); + // Assert.assertEquals(t.transform(4).hub, + // 0.2*ranker.getAuthScore(4)); + // + // // check authority scores in terms of previous hub scores + // Assert.assertEquals(t.transform(0).authority, + // ranker.getVertexScore(3) + 0.2*ranker.getVertexScore(4)); + // Assert.assertEquals(t.transform(1).authority, + // ranker.getVertexScore(0) + 0.5 * ranker.getVertexScore(2) + 0.2*ranker.getVertexScore(4)); + // Assert.assertEquals(t.transform(2).authority, + // ranker.getVertexScore(1) + 0.2*ranker.getVertexScore(4)); + // Assert.assertEquals(t.transform(3).authority, + // 0.5*ranker.getVertexScore(2) + 0.2*ranker.getVertexScore(4)); + // Assert.assertEquals(t.transform(4).authority, + // 0.2*ranker.getVertexScore(4)); + // + // verify that sums of each scores are 1.0 + double auth_sum = 0; + double hub_sum = 0; + for (int j = 0; j < 5; j++) { + // auth_sum += ranker.getAuthScore(j); + // hub_sum += ranker.getVertexScore(j); + // auth_sum += (ranker.getAuthScore(j) * ranker.getAuthScore(j)); + // hub_sum += (ranker.getVertexScore(j) * ranker.getVertexScore(j)); + HITS.Scores score = ranker.getVertexScore(j); + auth_sum += score.authority * score.authority; + hub_sum += score.hub * score.hub; + } + Assert.assertEquals(auth_sum, 1.0, 0.0001); + Assert.assertEquals(hub_sum, 1.0, 0.0001); + } - Assert.assertEquals(ranker.getVertexScore(0).hub, 0.5257, .001); - Assert.assertEquals(ranker.getVertexScore(1).hub, 0.0, .0001); - Assert.assertEquals(ranker.getVertexScore(2).hub, 0.8507, .0001); - Assert.assertEquals(ranker.getVertexScore(3).hub, 0.0, .0001); + ranker.evaluate(); - // the values below assume scores sum to 1 - // (rather than that sum of squares of scores sum to 1) -// Assert.assertEquals(ranker.getVertexScore(0).authority, 0, .0001); -// Assert.assertEquals(ranker.getVertexScore(1).authority, 0.618, .001); -// Assert.assertEquals(ranker.getVertexScore(2).authority, 0.0, .0001); -// Assert.assertEquals(ranker.getVertexScore(3).authority, 0.3819, .001); -// -// Assert.assertEquals(ranker.getVertexScore(0).hub, 0.38196, .001); -// Assert.assertEquals(ranker.getVertexScore(1).hub, 0.0, .0001); -// Assert.assertEquals(ranker.getVertexScore(2).hub, 0.618, .0001); -// Assert.assertEquals(ranker.getVertexScore(3).hub, 0.0, .0001); - } + Assert.assertEquals(ranker.getVertexScore(0).authority, 0, .0001); + Assert.assertEquals(ranker.getVertexScore(1).authority, 0.8507, .001); + Assert.assertEquals(ranker.getVertexScore(2).authority, 0.0, .0001); + Assert.assertEquals(ranker.getVertexScore(3).authority, 0.5257, .001); + + Assert.assertEquals(ranker.getVertexScore(0).hub, 0.5257, .001); + Assert.assertEquals(ranker.getVertexScore(1).hub, 0.0, .0001); + Assert.assertEquals(ranker.getVertexScore(2).hub, 0.8507, .0001); + Assert.assertEquals(ranker.getVertexScore(3).hub, 0.0, .0001); + // the values below assume scores sum to 1 + // (rather than that sum of squares of scores sum to 1) + // Assert.assertEquals(ranker.getVertexScore(0).authority, 0, .0001); + // Assert.assertEquals(ranker.getVertexScore(1).authority, 0.618, .001); + // Assert.assertEquals(ranker.getVertexScore(2).authority, 0.0, .0001); + // Assert.assertEquals(ranker.getVertexScore(3).authority, 0.3819, .001); + // + // Assert.assertEquals(ranker.getVertexScore(0).hub, 0.38196, .001); + // Assert.assertEquals(ranker.getVertexScore(1).hub, 0.0, .0001); + // Assert.assertEquals(ranker.getVertexScore(2).hub, 0.618, .0001); + // Assert.assertEquals(ranker.getVertexScore(3).hub, 0.0, .0001); + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestHITSWithPriors.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestHITSWithPriors.java index b7f1d0c2..93fa3d59 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestHITSWithPriors.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestHITSWithPriors.java @@ -1,77 +1,71 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.scoring; -import java.util.HashSet; -import java.util.Set; - import com.google.common.graph.MutableNetwork; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.scoring.util.ScoringUtils; +import java.util.HashSet; +import java.util.Set; import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; - -/** - * Tests HITSWithPriors. - */ +/** Tests HITSWithPriors. */ public class TestHITSWithPriors extends TestCase { - MutableNetwork graph; - Set roots; - - public static Test suite() { - return new TestSuite(TestHITSWithPriors.class); - } - - @Override - protected void setUp() { - graph = NetworkBuilder.directed().build(); - for(int i=0; i<4; i++) { - graph.addNode(i); - } - int j=0; - graph.addEdge(0, 1, j++); - graph.addEdge(1, 2, j++); - graph.addEdge(2, 3, j++); - graph.addEdge(3, 0, j++); - graph.addEdge(2, 1, j++); + MutableNetwork graph; + Set roots; - roots = new HashSet(); - roots.add(2); + public static Test suite() { + return new TestSuite(TestHITSWithPriors.class); + } + + @Override + protected void setUp() { + graph = NetworkBuilder.directed().build(); + for (int i = 0; i < 4; i++) { + graph.addNode(i); } + int j = 0; + graph.addEdge(0, 1, j++); + graph.addEdge(1, 2, j++); + graph.addEdge(2, 3, j++); + graph.addEdge(3, 0, j++); + graph.addEdge(2, 1, j++); + + roots = new HashSet(); + roots.add(2); + } + + public void testRankings() { - public void testRankings() { + HITSWithPriors ranker = + new HITSWithPriors(graph, ScoringUtils.getHITSUniformRootPrior(roots), 0.3); + ranker.evaluate(); - HITSWithPriors ranker = - new HITSWithPriors(graph, ScoringUtils.getHITSUniformRootPrior(roots), 0.3); - ranker.evaluate(); - - double[] expected_auth = {0.0, 0.765, 0.365, 0.530}; - double[] expected_hub = {0.398, 0.190, 0.897, 0.0}; + double[] expected_auth = {0.0, 0.765, 0.365, 0.530}; + double[] expected_hub = {0.398, 0.190, 0.897, 0.0}; - double hub_sum = 0; - double auth_sum = 0; - for (Number n : graph.nodes()) - { - int i = n.intValue(); - double auth = ranker.getVertexScore(i).authority; - double hub = ranker.getVertexScore(i).hub; - Assert.assertEquals(auth, expected_auth[i], 0.001); - Assert.assertEquals(hub, expected_hub[i], 0.001); - hub_sum += hub * hub; - auth_sum += auth * auth; - } - Assert.assertEquals(1.0, hub_sum, 0.001); - Assert.assertEquals(1.0, auth_sum, 0.001); + double hub_sum = 0; + double auth_sum = 0; + for (Number n : graph.nodes()) { + int i = n.intValue(); + double auth = ranker.getVertexScore(i).authority; + double hub = ranker.getVertexScore(i).hub; + Assert.assertEquals(auth, expected_auth[i], 0.001); + Assert.assertEquals(hub, expected_hub[i], 0.001); + hub_sum += hub * hub; + auth_sum += auth * auth; } + Assert.assertEquals(1.0, hub_sum, 0.001); + Assert.assertEquals(1.0, auth_sum, 0.001); + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestKStepMarkov.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestKStepMarkov.java index e58d0c1b..0d09d933 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestKStepMarkov.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestKStepMarkov.java @@ -1,70 +1,59 @@ package edu.uci.ics.jung.algorithms.scoring; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - import com.google.common.base.Functions; import com.google.common.graph.MutableNetwork; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.scoring.util.ScoringUtils; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import junit.framework.TestCase; -public class TestKStepMarkov extends TestCase -{ - MutableNetwork mGraph; - double[][] mTransitionMatrix; - Map edgeWeights = new HashMap(); - - @Override - protected void setUp() - { - mGraph = NetworkBuilder.directed().allowsParallelEdges(true).build(); - mTransitionMatrix = new double[][] - {{0.0, 0.5, 0.5}, - {1.0/3.0, 0.0, 2.0/3.0}, - {1.0/3.0, 2.0/3.0, 0.0}}; - - for (int i = 0; i < mTransitionMatrix.length; i++) - mGraph.addNode(i); - - for (int i = 0; i < mTransitionMatrix.length; i++) { - for (int j = 0; j < mTransitionMatrix[i].length; j++) - { - if (mTransitionMatrix[i][j] > 0) - { - int edge = i*mTransitionMatrix.length+j; - mGraph.addEdge(i, j, edge); - edgeWeights.put(edge, mTransitionMatrix[i][j]); - } - } +public class TestKStepMarkov extends TestCase { + MutableNetwork mGraph; + double[][] mTransitionMatrix; + Map edgeWeights = new HashMap(); + + @Override + protected void setUp() { + mGraph = NetworkBuilder.directed().allowsParallelEdges(true).build(); + mTransitionMatrix = + new double[][] {{0.0, 0.5, 0.5}, {1.0 / 3.0, 0.0, 2.0 / 3.0}, {1.0 / 3.0, 2.0 / 3.0, 0.0}}; + + for (int i = 0; i < mTransitionMatrix.length; i++) mGraph.addNode(i); + + for (int i = 0; i < mTransitionMatrix.length; i++) { + for (int j = 0; j < mTransitionMatrix[i].length; j++) { + if (mTransitionMatrix[i][j] > 0) { + int edge = i * mTransitionMatrix.length + j; + mGraph.addEdge(i, j, edge); + edgeWeights.put(edge, mTransitionMatrix[i][j]); } + } } - - // TODO(jrtom): this isn't actually testing anything - public void testRanker() { - - Set priors = new HashSet(); - priors.add(1); - priors.add(2); - KStepMarkov ranker = - new KStepMarkov(mGraph, Functions.forMap(edgeWeights), - ScoringUtils.getUniformRootPrior(priors),2); -// ranker.evaluate(); -// System.out.println(ranker.getIterations()); - - for (int i = 0; i < 10; i++) - { -// System.out.println(ranker.getIterations()); -// for (Number n : mGraph.getVertices()) -// System.out.println(n + ": " + ranker.getVertexScore(n)); - ranker.step(); - } -// List> rankings = ranker.getRankings(); -// System.out.println("New version:"); -// System.out.println(rankings); + } + + // TODO(jrtom): this isn't actually testing anything + public void testRanker() { + + Set priors = new HashSet(); + priors.add(1); + priors.add(2); + KStepMarkov ranker = + new KStepMarkov( + mGraph, Functions.forMap(edgeWeights), ScoringUtils.getUniformRootPrior(priors), 2); + // ranker.evaluate(); + // System.out.println(ranker.getIterations()); + + for (int i = 0; i < 10; i++) { + // System.out.println(ranker.getIterations()); + // for (Number n : mGraph.getVertices()) + // System.out.println(n + ": " + ranker.getVertexScore(n)); + ranker.step(); } - + // List> rankings = ranker.getRankings(); + // System.out.println("New version:"); + // System.out.println(rankings); + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestPageRank.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestPageRank.java index 9e8cb7ee..25c9a9ed 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestPageRank.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestPageRank.java @@ -1,71 +1,71 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.scoring; -import java.util.HashMap; -import java.util.Map; - import com.google.common.base.Functions; import com.google.common.base.Supplier; import com.google.common.graph.MutableNetwork; import com.google.common.graph.NetworkBuilder; - +import java.util.HashMap; +import java.util.Map; import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; -/** - * @author Joshua O'Madadhain - */ +/** @author Joshua O'Madadhain */ public class TestPageRank extends TestCase { - - private Map edgeWeights; - private MutableNetwork graph; - private Supplier edgeFactory; - - public static Test suite() { - return new TestSuite(TestPageRank.class); - } - @Override - protected void setUp() { - edgeWeights = new HashMap(); - edgeFactory = new Supplier() { - int i=0; - public Integer get() { - return i++; - }}; - } + private Map edgeWeights; + private MutableNetwork graph; + private Supplier edgeFactory; + + public static Test suite() { + return new TestSuite(TestPageRank.class); + } + + @Override + protected void setUp() { + edgeWeights = new HashMap(); + edgeFactory = + new Supplier() { + int i = 0; + + public Integer get() { + return i++; + } + }; + } + + private void addEdge(Integer v1, Integer v2, double weight) { + Integer edge = edgeFactory.get(); + graph.addEdge(v1, v2, edge); + edgeWeights.put(edge, weight); + } + + public void testRanker() { + graph = NetworkBuilder.directed().build(); - private void addEdge(Integer v1, Integer v2, double weight) { - Integer edge = edgeFactory.get(); - graph.addEdge(v1, v2, edge); - edgeWeights.put(edge, weight); - } + addEdge(0, 1, 1.0); + addEdge(1, 2, 1.0); + addEdge(2, 3, 0.5); + addEdge(3, 1, 1.0); + addEdge(2, 1, 0.5); - public void testRanker() { - graph = NetworkBuilder.directed().build(); - - addEdge(0,1,1.0); - addEdge(1,2,1.0); - addEdge(2,3,0.5); - addEdge(3,1,1.0); - addEdge(2,1,0.5); + PageRank pr = + new PageRank(graph, Functions.forMap(edgeWeights), 0); + pr.evaluate(); - PageRank pr = new PageRank(graph, Functions.forMap(edgeWeights), 0); - pr.evaluate(); - - Assert.assertEquals(pr.getVertexScore(0), 0.0, pr.getTolerance()); - Assert.assertEquals(pr.getVertexScore(1), 0.4, pr.getTolerance()); - Assert.assertEquals(pr.getVertexScore(2), 0.4, pr.getTolerance()); - Assert.assertEquals(pr.getVertexScore(3), 0.2, pr.getTolerance()); - } + Assert.assertEquals(pr.getVertexScore(0), 0.0, pr.getTolerance()); + Assert.assertEquals(pr.getVertexScore(1), 0.4, pr.getTolerance()); + Assert.assertEquals(pr.getVertexScore(2), 0.4, pr.getTolerance()); + Assert.assertEquals(pr.getVertexScore(3), 0.2, pr.getTolerance()); + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestPageRankWithPriors.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestPageRankWithPriors.java index cd8547e4..4e679278 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestPageRankWithPriors.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestPageRankWithPriors.java @@ -1,85 +1,81 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.scoring; -import java.util.HashSet; -import java.util.Set; - import com.google.common.base.Supplier; import com.google.common.graph.MutableNetwork; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.scoring.util.ScoringUtils; +import java.util.HashSet; +import java.util.Set; import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; - -/** - * @author Scott White - */ +/** @author Scott White */ public class TestPageRankWithPriors extends TestCase { - private MutableNetwork graph; - private Supplier edgeFactory; - - public static Test suite() { - return new TestSuite(TestPageRankWithPriors.class); - } + private MutableNetwork graph; + private Supplier edgeFactory; - @Override - protected void setUp() { - edgeFactory = new Supplier() { - int i=0; - public Integer get() { - return i++; - }}; - } + public static Test suite() { + return new TestSuite(TestPageRankWithPriors.class); + } - private void addEdge(Integer v1, Integer v2) - { - Integer edge = edgeFactory.get(); - graph.addEdge(v1, v2, edge); - } + @Override + protected void setUp() { + edgeFactory = + new Supplier() { + int i = 0; - public void testGraphScoring() { - graph = NetworkBuilder.directed().allowsParallelEdges(true).build(); + public Integer get() { + return i++; + } + }; + } - double[] expected_score = new double[]{0.1157, 0.2463, 0.4724, 0.1653}; - - for(int i=0; i<4; i++) { - graph.addNode(i); - } - addEdge(0,1); - addEdge(1,2); - addEdge(2,3); - addEdge(3,0); - addEdge(2,1); + private void addEdge(Integer v1, Integer v2) { + Integer edge = edgeFactory.get(); + graph.addEdge(v1, v2, edge); + } - Set priors = new HashSet(); - priors.add(2); + public void testGraphScoring() { + graph = NetworkBuilder.directed().allowsParallelEdges(true).build(); - PageRankWithPriors pr = - new PageRankWithPriors(graph, ScoringUtils.getUniformRootPrior(priors), 0.3); - pr.evaluate(); + double[] expected_score = new double[] {0.1157, 0.2463, 0.4724, 0.1653}; - double score_sum = 0; - for (int i = 0; i < graph.nodes().size(); i++) - { - double score = pr.getVertexScore(i); - Assert.assertEquals(expected_score[i], score, pr.getTolerance()); - score_sum += score; - } - Assert.assertEquals(1.0, score_sum, pr.getTolerance() * graph.nodes().size()); + for (int i = 0; i < 4; i++) { + graph.addNode(i); } + addEdge(0, 1); + addEdge(1, 2); + addEdge(2, 3); + addEdge(3, 0); + addEdge(2, 1); + + Set priors = new HashSet(); + priors.add(2); - public void testHypergraphScoring() { + PageRankWithPriors pr = + new PageRankWithPriors( + graph, ScoringUtils.getUniformRootPrior(priors), 0.3); + pr.evaluate(); + + double score_sum = 0; + for (int i = 0; i < graph.nodes().size(); i++) { + double score = pr.getVertexScore(i); + Assert.assertEquals(expected_score[i], score, pr.getTolerance()); + score_sum += score; } + Assert.assertEquals(1.0, score_sum, pr.getTolerance() * graph.nodes().size()); + } + + public void testHypergraphScoring() {} } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestVoltageScore.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestVoltageScore.java index e92a44d0..24be1848 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestVoltageScore.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestVoltageScore.java @@ -1,79 +1,70 @@ /** - * Copyright (c) 2008, The JUNG Authors + * Copyright (c) 2008, The JUNG Authors * - * All rights reserved. + *

    All rights reserved. * - * This software is open-source under the BSD license; see either - * "license.txt" or - * https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * Created on Jul 14, 2008 - * + *

    This software is open-source under the BSD license; see either "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. Created on Jul 14, 2008 */ package edu.uci.ics.jung.algorithms.scoring; import com.google.common.base.Functions; import com.google.common.graph.MutableNetwork; import com.google.common.graph.NetworkBuilder; - import junit.framework.TestCase; +/** @author jrtom */ +public class TestVoltageScore extends TestCase { + protected MutableNetwork g; -/** - * @author jrtom - * - */ -public class TestVoltageScore extends TestCase -{ - protected MutableNetwork g; - - // TODO: - // * test multiple sources/targets - // * test weighted edges - // * test exceptional cases - - public final void testDirectedVoltagesSourceTarget() { - g = NetworkBuilder.directed().build(); - - int j = 0; - g.addEdge(0, 1, j++); - g.addEdge(1, 2, j++); - g.addEdge(2, 3, j++); - g.addEdge(2, 4, j++); - g.addEdge(3, 4, j++); - g.addEdge(4, 1, j++); - g.addEdge(4, 0, j++); - - VoltageScorer vr = new VoltageScorer<>(g, Functions.constant(1), 0, 3); - double[] voltages = {1.0, 2.0/3, 2.0/3, 0, 1.0/3}; - - vr.evaluate(); - checkVoltages(vr, voltages); - } - - public final void testUndirectedSourceTarget() { - g = NetworkBuilder.undirected().build(); - int j = 0; - g.addEdge(0,1, j++); - g.addEdge(0,2, j++); - g.addEdge(1,3, j++); - g.addEdge(2,3, j++); - g.addEdge(3,4, j++); - g.addEdge(3,5, j++); - g.addEdge(4,6, j++); - g.addEdge(5,6, j++); - VoltageScorer vr = new VoltageScorer<>(g, Functions.constant(1), 0, 6); - double[] voltages = {1.0, 0.75, 0.75, 0.5, 0.25, 0.25, 0}; + // TODO: + // * test multiple sources/targets + // * test weighted edges + // * test exceptional cases - vr.evaluate(); - checkVoltages(vr, voltages); - } - - private static final void checkVoltages(VoltageScorer vr, double[] voltages) { - assertEquals(vr.vertexScores().size(), voltages.length); - System.out.println("scores: " + vr.vertexScores()); - System.out.println("voltages: " + voltages.toString()); - for (int i = 0; i < voltages.length; i++) { - assertEquals(vr.getVertexScore(i), voltages[i], 0.01); - } + public final void testDirectedVoltagesSourceTarget() { + g = NetworkBuilder.directed().build(); + + int j = 0; + g.addEdge(0, 1, j++); + g.addEdge(1, 2, j++); + g.addEdge(2, 3, j++); + g.addEdge(2, 4, j++); + g.addEdge(3, 4, j++); + g.addEdge(4, 1, j++); + g.addEdge(4, 0, j++); + + VoltageScorer vr = new VoltageScorer<>(g, Functions.constant(1), 0, 3); + double[] voltages = {1.0, 2.0 / 3, 2.0 / 3, 0, 1.0 / 3}; + + vr.evaluate(); + checkVoltages(vr, voltages); + } + + public final void testUndirectedSourceTarget() { + g = NetworkBuilder.undirected().build(); + int j = 0; + g.addEdge(0, 1, j++); + g.addEdge(0, 2, j++); + g.addEdge(1, 3, j++); + g.addEdge(2, 3, j++); + g.addEdge(3, 4, j++); + g.addEdge(3, 5, j++); + g.addEdge(4, 6, j++); + g.addEdge(5, 6, j++); + VoltageScorer vr = new VoltageScorer<>(g, Functions.constant(1), 0, 6); + double[] voltages = {1.0, 0.75, 0.75, 0.5, 0.25, 0.25, 0}; + + vr.evaluate(); + checkVoltages(vr, voltages); + } + + private static final void checkVoltages(VoltageScorer vr, double[] voltages) { + assertEquals(vr.vertexScores().size(), voltages.length); + System.out.println("scores: " + vr.vertexScores()); + System.out.println("voltages: " + voltages.toString()); + for (int i = 0; i < voltages.length; i++) { + assertEquals(vr.getVertexScore(i), voltages[i], 0.01); } + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestWeightedNIPaths.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestWeightedNIPaths.java index bd726d01..0eb2d362 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestWeightedNIPaths.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/scoring/TestWeightedNIPaths.java @@ -1,90 +1,92 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.scoring; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableMap; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.NetworkBuilder; import java.util.HashSet; import java.util.Map; import java.util.Set; - import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; -import com.google.common.base.Supplier; -import com.google.common.collect.ImmutableMap; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.NetworkBuilder; +/** @author Scott White, adapted to jung2 by Tom Nelson */ +public class TestWeightedNIPaths extends TestCase { + Supplier vertexFactory; + Supplier edgeFactory; -/** - * @author Scott White, adapted to jung2 by Tom Nelson - */ -public class TestWeightedNIPaths extends TestCase { - - Supplier vertexFactory; - Supplier edgeFactory; + public static Test suite() { + return new TestSuite(TestWeightedNIPaths.class); + } - public static Test suite() { - return new TestSuite(TestWeightedNIPaths.class); - } + @Override + protected void setUp() { + vertexFactory = + new Supplier() { + char a = 'A'; - @Override - protected void setUp() { - vertexFactory = new Supplier() { - char a = 'A'; - public String get() { - return Character.toString(a++); - }}; - edgeFactory = new Supplier() { - int count; - public Number get() { - return count++; - }}; - } + public String get() { + return Character.toString(a++); + } + }; + edgeFactory = + new Supplier() { + int count; + + public Number get() { + return count++; + } + }; + } - public void testRanker() { + public void testRanker() { + + MutableNetwork graph = NetworkBuilder.directed().build(); + for (int i = 0; i < 5; i++) { + graph.addNode(vertexFactory.get()); + } - MutableNetwork graph = NetworkBuilder.directed().build(); - for(int i=0; i<5; i++) { - graph.addNode(vertexFactory.get()); - } + graph.addEdge("A", "B", edgeFactory.get()); + graph.addEdge("A", "C", edgeFactory.get()); + graph.addEdge("A", "D", edgeFactory.get()); + graph.addEdge("B", "A", edgeFactory.get()); + graph.addEdge("B", "E", edgeFactory.get()); + graph.addEdge("B", "D", edgeFactory.get()); + graph.addEdge("C", "A", edgeFactory.get()); + graph.addEdge("C", "E", edgeFactory.get()); + graph.addEdge("C", "D", edgeFactory.get()); + graph.addEdge("D", "A", edgeFactory.get()); + graph.addEdge("D", "B", edgeFactory.get()); + graph.addEdge("D", "C", edgeFactory.get()); + graph.addEdge("D", "E", edgeFactory.get()); - graph.addEdge("A", "B", edgeFactory.get()); - graph.addEdge("A", "C", edgeFactory.get()); - graph.addEdge("A", "D", edgeFactory.get()); - graph.addEdge("B", "A", edgeFactory.get()); - graph.addEdge("B", "E", edgeFactory.get()); - graph.addEdge("B", "D", edgeFactory.get()); - graph.addEdge("C", "A", edgeFactory.get()); - graph.addEdge("C", "E", edgeFactory.get()); - graph.addEdge("C", "D", edgeFactory.get()); - graph.addEdge("D", "A", edgeFactory.get()); - graph.addEdge("D", "B", edgeFactory.get()); - graph.addEdge("D", "C", edgeFactory.get()); - graph.addEdge("D", "E", edgeFactory.get()); - - Set priors = new HashSet(); - priors.add("A"); + Set priors = new HashSet(); + priors.add("A"); - WeightedNIPaths ranker = - new WeightedNIPaths(graph, vertexFactory, edgeFactory, 2.0,3,priors); + WeightedNIPaths ranker = + new WeightedNIPaths(graph, vertexFactory, edgeFactory, 2.0, 3, priors); - Map expectedScores = ImmutableMap.of( - "A", 0.277787, - "B", 0.166676, - "C", 0.166676, - "D", 0.222222, - "E", 0.166676); - for (String node : graph.nodes()) { - Assert.assertEquals(expectedScores.get(node), ranker.getVertexScore(node), 0.0001); - } + Map expectedScores = + ImmutableMap.of( + "A", 0.277787, + "B", 0.166676, + "C", 0.166676, + "D", 0.222222, + "E", 0.166676); + for (String node : graph.nodes()) { + Assert.assertEquals(expectedScores.get(node), ranker.getVertexScore(node), 0.0001); } -} \ No newline at end of file + } +} diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestBFSDistanceLabeler.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestBFSDistanceLabeler.java index e0a48c47..c178458a 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestBFSDistanceLabeler.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestBFSDistanceLabeler.java @@ -1,66 +1,60 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.algorithms.shortestpath; import com.google.common.graph.GraphBuilder; import com.google.common.graph.MutableGraph; - import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; -/** - * @author Scott White, adapted to jung2 by Tom Nelson - */ +/** @author Scott White, adapted to jung2 by Tom Nelson */ public class TestBFSDistanceLabeler extends TestCase { - public static Test suite() { - return new TestSuite(TestBFSDistanceLabeler.class); - } - - @Override - protected void setUp() { - - } - - public void test() { - MutableGraph graph = GraphBuilder.undirected().build(); - for(int i=0; i<6; i++) { - graph.addNode(i); - } - graph.putEdge(0,1); - graph.putEdge(0,5); - graph.putEdge(0,3); - graph.putEdge(0,4); - graph.putEdge(1,5); - graph.putEdge(3,4); - graph.putEdge(3,2); - graph.putEdge(5,2); - Number root = 0; - - BFSDistanceLabeler labeler = new BFSDistanceLabeler(); - labeler.labelDistances(graph,root); - - Assert.assertEquals(labeler.getPredecessors(root).size(),0); - Assert.assertEquals(labeler.getPredecessors(1).size(),1); - Assert.assertEquals(labeler.getPredecessors(2).size(),2); - Assert.assertEquals(labeler.getPredecessors(3).size(),1); - Assert.assertEquals(labeler.getPredecessors(4).size(),1); - Assert.assertEquals(labeler.getPredecessors(5).size(),1); - - Assert.assertEquals(labeler.getDistance(graph,0),0); - Assert.assertEquals(labeler.getDistance(graph,1),1); - Assert.assertEquals(labeler.getDistance(graph,2),2); - Assert.assertEquals(labeler.getDistance(graph,3),1); - Assert.assertEquals(labeler.getDistance(graph,4),1); - Assert.assertEquals(labeler.getDistance(graph,5),1); - - } -} \ No newline at end of file + public static Test suite() { + return new TestSuite(TestBFSDistanceLabeler.class); + } + + @Override + protected void setUp() {} + + public void test() { + MutableGraph graph = GraphBuilder.undirected().build(); + for (int i = 0; i < 6; i++) { + graph.addNode(i); + } + graph.putEdge(0, 1); + graph.putEdge(0, 5); + graph.putEdge(0, 3); + graph.putEdge(0, 4); + graph.putEdge(1, 5); + graph.putEdge(3, 4); + graph.putEdge(3, 2); + graph.putEdge(5, 2); + Number root = 0; + + BFSDistanceLabeler labeler = new BFSDistanceLabeler(); + labeler.labelDistances(graph, root); + + Assert.assertEquals(labeler.getPredecessors(root).size(), 0); + Assert.assertEquals(labeler.getPredecessors(1).size(), 1); + Assert.assertEquals(labeler.getPredecessors(2).size(), 2); + Assert.assertEquals(labeler.getPredecessors(3).size(), 1); + Assert.assertEquals(labeler.getPredecessors(4).size(), 1); + Assert.assertEquals(labeler.getPredecessors(5).size(), 1); + + Assert.assertEquals(labeler.getDistance(graph, 0), 0); + Assert.assertEquals(labeler.getDistance(graph, 1), 1); + Assert.assertEquals(labeler.getDistance(graph, 2), 2); + Assert.assertEquals(labeler.getDistance(graph, 3), 1); + Assert.assertEquals(labeler.getDistance(graph, 4), 1); + Assert.assertEquals(labeler.getDistance(graph, 5), 1); + } +} diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestMinimumSpanningTree.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestMinimumSpanningTree.java index c27d6e36..1603364a 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestMinimumSpanningTree.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestMinimumSpanningTree.java @@ -3,39 +3,38 @@ import com.google.common.graph.MutableNetwork; import com.google.common.graph.Network; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.graph.MutableCTreeNetwork; import edu.uci.ics.jung.graph.TreeNetworkBuilder; import junit.framework.TestCase; public class TestMinimumSpanningTree extends TestCase { - - public void testSimpleTree() { - MutableCTreeNetwork tree = TreeNetworkBuilder.builder().build(); - tree.addNode("A"); - tree.addEdge("A", "B0", 0); - tree.addEdge("A", "B1", 1); - - Network mst = MinimumSpanningTree.extractFrom(tree, e -> 1.0); - - assertEquals(tree.nodes(), mst.nodes()); - assertEquals(tree.edges(), mst.edges()); - } - - public void testDAG() { - MutableNetwork graph = NetworkBuilder.directed().build(); - graph.addNode("B0"); - graph.addEdge("A", "B0", 0); - graph.addEdge("A", "B1", 1); - - Network mst = MinimumSpanningTree.extractFrom(graph, e -> 1.0); - - assertEquals(graph.nodes(), mst.nodes()); - assertEquals(graph.edges(), mst.edges()); - } - - // TODO: add tests: - // - cycle - // - multiple components - // - self-loops + + public void testSimpleTree() { + MutableCTreeNetwork tree = TreeNetworkBuilder.builder().build(); + tree.addNode("A"); + tree.addEdge("A", "B0", 0); + tree.addEdge("A", "B1", 1); + + Network mst = MinimumSpanningTree.extractFrom(tree, e -> 1.0); + + assertEquals(tree.nodes(), mst.nodes()); + assertEquals(tree.edges(), mst.edges()); + } + + public void testDAG() { + MutableNetwork graph = NetworkBuilder.directed().build(); + graph.addNode("B0"); + graph.addEdge("A", "B0", 0); + graph.addEdge("A", "B1", 1); + + Network mst = MinimumSpanningTree.extractFrom(graph, e -> 1.0); + + assertEquals(graph.nodes(), mst.nodes()); + assertEquals(graph.edges(), mst.edges()); + } + + // TODO: add tests: + // - cycle + // - multiple components + // - self-loops } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestShortestPath.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestShortestPath.java index d4c3bacc..f1ac4d28 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestShortestPath.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestShortestPath.java @@ -4,14 +4,6 @@ */ package edu.uci.ics.jung.algorithms.shortestpath; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Set; - import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Supplier; @@ -19,445 +11,749 @@ import com.google.common.graph.MutableNetwork; import com.google.common.graph.Network; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.util.Indexer; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; import junit.framework.TestCase; - -/** - * @author Joshua O'Madadhain - */ +/** @author Joshua O'Madadhain */ // TODO: needs major cleanup -public class TestShortestPath extends TestCase -{ - private MutableNetwork dg; - private MutableNetwork ug; - // graph based on Weiss, _Data Structures and Algorithm Analysis_, - // 1992, p. 292 - private static int[][] edges = - {{1,2,2}, {1,4,1}, // 0, 1 - {2,4,3}, {2,5,10}, // 2, 3 - {3,1,4}, {3,6,5}, // 4, 5 - {4,3,2}, {4,5,2}, {4,6,8}, {4,7,4}, // 6,7,8,9 - {5,7,6}, // 10 - {7,6,1}, // 11 - {8,9,4}, // (12) these three edges define a second connected component - {9,10,1}, // 13 - {10,8,2}}; // 14 - - private static Integer[][] ug_incomingEdges = +public class TestShortestPath extends TestCase { + private MutableNetwork dg; + private MutableNetwork ug; + // graph based on Weiss, _Data Structures and Algorithm Analysis_, + // 1992, p. 292 + private static int[][] edges = { + {1, 2, 2}, + {1, 4, 1}, // 0, 1 + {2, 4, 3}, + {2, 5, 10}, // 2, 3 + {3, 1, 4}, + {3, 6, 5}, // 4, 5 + {4, 3, 2}, + {4, 5, 2}, + {4, 6, 8}, + {4, 7, 4}, // 6,7,8,9 + {5, 7, 6}, // 10 + {7, 6, 1}, // 11 + {8, 9, 4}, // (12) these three edges define a second connected component + {9, 10, 1}, // 13 + {10, 8, 2} + }; // 14 + + private static Integer[][] ug_incomingEdges = { { - {null, new Integer(0), new Integer(6), new Integer(1), new Integer(7), new Integer(11), new Integer(9), null, null, null}, - {new Integer(0), null, new Integer(6), new Integer(2), new Integer(7), new Integer(11), new Integer(9), null, null, null}, - {new Integer(1), new Integer(2), null, new Integer(6), new Integer(7), new Integer(5), new Integer(9), null, null, null}, - {new Integer(1), new Integer(2), new Integer(6), null, new Integer(7), new Integer(11), new Integer(9), null, null, null}, - {new Integer(1), new Integer(2), new Integer(6), new Integer(7), null, new Integer(11), new Integer(10), null, null, null}, - {new Integer(1), new Integer(2), new Integer(5), new Integer(9), new Integer(10), null, new Integer(11), null, null, null}, - {new Integer(1), new Integer(2), new Integer(5), new Integer(9), new Integer(10), new Integer(11), null, null, null, null}, - {null, null, null, null, null, null, null, null, new Integer(13), new Integer(14)}, - {null, null, null, null, null, null, null, new Integer(14), null, new Integer(13)}, - {null, null, null, null, null, null, null, new Integer(14), new Integer(13), null}, - }; - - private static Integer[][] dg_incomingEdges = - { - {null, new Integer(0), new Integer(6), new Integer(1), new Integer(7), new Integer(11), new Integer(9), null, null, null}, - {new Integer(4), null, new Integer(6), new Integer(2), new Integer(7), new Integer(11), new Integer(9), null, null, null}, - {new Integer(4), new Integer(0), null, new Integer(1), new Integer(7), new Integer(5), new Integer(9), null, null, null}, - {new Integer(4), new Integer(0), new Integer(6), null, new Integer(7), new Integer(11), new Integer(9), null, null, null}, - {null, null, null, null, null, new Integer(11), new Integer(10), null, null, null}, - {null, null, null, null, null, null, null, null, null, null}, - {null, null, null, null, null, new Integer(11), null, null, null, null}, - {null, null, null, null, null, null, null, null, new Integer(12), new Integer(13)}, - {null, null, null, null, null, null, null, new Integer(14), null, new Integer(13)}, - {null, null, null, null, null, null, null, new Integer(14), new Integer(12), null} - }; - - private static double[][] dg_distances = + null, + new Integer(0), + new Integer(6), + new Integer(1), + new Integer(7), + new Integer(11), + new Integer(9), + null, + null, + null + }, { - {0, 2, 3, 1, 3, 6, 5, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {9, 0, 5, 3, 5, 8, 7, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {4, 6, 0, 5, 7, 5, 9, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {6, 8, 2, 0, 2, 5, 4, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0, 7, 6, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 1, 0, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0, 4, 5}, - {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 3, 0, 1}, - {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 2, 6, 0} - }; - - private static double[][] ug_distances = + new Integer(0), + null, + new Integer(6), + new Integer(2), + new Integer(7), + new Integer(11), + new Integer(9), + null, + null, + null + }, { - {0, 2, 3, 1, 3, 6, 5, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {2, 0, 5, 3, 5, 8, 7, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {3, 5, 0, 2, 4, 5, 6, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {1, 3, 2, 0, 2, 5, 4, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {3, 5, 4, 2, 0, 7, 6, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {6, 8, 5, 5, 7, 0, 1, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {5, 7, 6, 4, 6, 1, 0, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0, 3, 2}, - {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 3, 0, 1}, - {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 2, 1, 0} - }; - - - private static Integer[][] shortestPaths1 = + new Integer(1), + new Integer(2), + null, + new Integer(6), + new Integer(7), + new Integer(5), + new Integer(9), + null, + null, + null + }, { - null, - {new Integer(0)}, - {new Integer(1), new Integer(6)}, - {new Integer(1)}, - {new Integer(1), new Integer(7)}, - {new Integer(1), new Integer(9), new Integer(11)}, - {new Integer(1), new Integer(9)}, - null, - null, - null - }; - - private Map,Integer[]> edgeArrays; - - private Map edgeWeights; - - private Function nev; - - private Supplier vertexFactoryDG = - new Supplier() { - int count = 0; - public String get() { - return "V"+count++; - }}; - private Supplier vertexFactoryUG = - new Supplier() { - int count = 0; - public String get() { - return "U"+count++; - }}; - - BiMap did; - BiMap uid; - - @Override - protected void setUp() { - edgeWeights = new HashMap(); - nev = Functions.forMap(edgeWeights); - dg = NetworkBuilder.directed().allowsParallelEdges(true).allowsSelfLoops(true).build(); - for(int i=0; icreate(dg.nodes(), 1); - Integer[] dg_array = new Integer[edges.length]; - addEdges(dg, did, dg_array); - - ug = NetworkBuilder.undirected().allowsParallelEdges(true).allowsSelfLoops(true).build(); - for(int i=0; icreate(ug.nodes(),1); - Integer[] ug_array = new Integer[edges.length]; - addEdges(ug, uid, ug_array); - - edgeArrays = new HashMap,Integer[]>(); - edgeArrays.put(dg, dg_array); - edgeArrays.put(ug, ug_array); - } - - @Override - protected void tearDown() throws Exception { - } + new Integer(1), + new Integer(2), + new Integer(6), + null, + new Integer(7), + new Integer(11), + new Integer(9), + null, + null, + null + }, + { + new Integer(1), + new Integer(2), + new Integer(6), + new Integer(7), + null, + new Integer(11), + new Integer(10), + null, + null, + null + }, + { + new Integer(1), + new Integer(2), + new Integer(5), + new Integer(9), + new Integer(10), + null, + new Integer(11), + null, + null, + null + }, + { + new Integer(1), + new Integer(2), + new Integer(5), + new Integer(9), + new Integer(10), + new Integer(11), + null, + null, + null, + null + }, + {null, null, null, null, null, null, null, null, new Integer(13), new Integer(14)}, + {null, null, null, null, null, null, null, new Integer(14), null, new Integer(13)}, + {null, null, null, null, null, null, null, new Integer(14), new Integer(13), null}, + }; - public void exceptionTest(MutableNetwork g, BiMap indexer, int index) + private static Integer[][] dg_incomingEdges = { { - DijkstraShortestPath dsp = - new DijkstraShortestPath(g, nev); - String start = indexer.inverse().get(index); - Integer e = null; - - String v = "NOT IN GRAPH"; - - try - { - dsp.getDistance(start, v); - fail("getDistance(): illegal destination vertex"); - } - catch (IllegalArgumentException iae) {} - try - { - dsp.getDistance(v, start); - fail("getDistance(): illegal source vertex"); - } - catch (IllegalArgumentException iae) {} - try - { - dsp.getDistanceMap(v, 1); - fail("getDistanceMap(): illegal source vertex"); - } - catch (IllegalArgumentException iae) {} - try - { - dsp.getDistanceMap(start, 0); - fail("getDistanceMap(): too few vertices requested"); - } - catch (IllegalArgumentException iae) {} - try - { - dsp.getDistanceMap(start, g.nodes().size()+1); - fail("getDistanceMap(): too many vertices requested"); - } - catch (IllegalArgumentException iae) {} + null, + new Integer(0), + new Integer(6), + new Integer(1), + new Integer(7), + new Integer(11), + new Integer(9), + null, + null, + null + }, + { + new Integer(4), + null, + new Integer(6), + new Integer(2), + new Integer(7), + new Integer(11), + new Integer(9), + null, + null, + null + }, + { + new Integer(4), + new Integer(0), + null, + new Integer(1), + new Integer(7), + new Integer(5), + new Integer(9), + null, + null, + null + }, + { + new Integer(4), + new Integer(0), + new Integer(6), + null, + new Integer(7), + new Integer(11), + new Integer(9), + null, + null, + null + }, + {null, null, null, null, null, new Integer(11), new Integer(10), null, null, null}, + {null, null, null, null, null, null, null, null, null, null}, + {null, null, null, null, null, new Integer(11), null, null, null, null}, + {null, null, null, null, null, null, null, null, new Integer(12), new Integer(13)}, + {null, null, null, null, null, null, null, new Integer(14), null, new Integer(13)}, + {null, null, null, null, null, null, null, new Integer(14), new Integer(12), null} + }; - try - { - dsp.getIncomingEdge(start, v); - fail("getIncomingEdge(): illegal destination vertex"); - } - catch (IllegalArgumentException iae) {} - try - { - dsp.getIncomingEdge(v, start); - fail("getIncomingEdge(): illegal source vertex"); - } - catch (IllegalArgumentException iae) {} - try - { - dsp.getIncomingEdgeMap(v, 1); - fail("getIncomingEdgeMap(): illegal source vertex"); - } - catch (IllegalArgumentException iae) {} - try - { - dsp.getIncomingEdgeMap(start, 0); - fail("getIncomingEdgeMap(): too few vertices requested"); - } - catch (IllegalArgumentException iae) {} - try - { - dsp.getDistanceMap(start, g.nodes().size()+1); - fail("getIncomingEdgeMap(): too many vertices requested"); - } - catch (IllegalArgumentException iae) {} - - try - { - // test negative edge weight exception - String v1 = indexer.inverse().get(1); - String v2 = indexer.inverse().get(7); - e = g.edges().size() + 1; - g.addEdge(v1, v2, e); - edgeWeights.put(e, -2); - dsp.reset(); - dsp.getDistanceMap(start); - fail("DijkstraShortestPath should not accept negative edge weights"); - } - catch (IllegalArgumentException iae) - { - g.removeEdge(e); - } - } - - public void testDijkstra() + private static double[][] dg_distances = { { - setUp(); - exceptionTest(dg, did, 1); - - setUp(); - exceptionTest(ug, uid, 1); - - setUp(); - getPathTest(dg, did, 1); - - setUp(); - getPathTest(ug, uid, 1); - - for (int i = 1; i <= dg_distances.length; i++) - { - setUp(); - weightedTest(dg, did, i, true); - - setUp(); - weightedTest(dg, did, i, false); - } - - for (int i = 1; i <= ug_distances.length; i++) - { - setUp(); - weightedTest(ug, uid, i, true); - - setUp(); - weightedTest(ug, uid, i, false); - } - + 0, + 2, + 3, + 1, + 3, + 6, + 5, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + 9, + 0, + 5, + 3, + 5, + 8, + 7, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + 4, + 6, + 0, + 5, + 7, + 5, + 9, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + 6, + 8, + 2, + 0, + 2, + 5, + 4, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + 0, + 7, + 6, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + 0, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + 1, + 0, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + 0, + 4, + 5 + }, + { + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + 3, + 0, + 1 + }, + { + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + 2, + 6, + 0 } + }; - private void getPathTest(Network g, BiMap indexer, int index) + private static double[][] ug_distances = { { - DijkstraShortestPath dsp = - new DijkstraShortestPath(g, nev); - String start = indexer.inverse().get(index); - Integer[] edge_array = edgeArrays.get(g); - Integer[] incomingEdges1 = g.isDirected() - ? dg_incomingEdges[index-1] - : ug_incomingEdges[index-1]; - assertEquals(incomingEdges1.length, g.nodes().size()); - - // test getShortestPath(start, v) - dsp.reset(); - for (int i = 1; i <= incomingEdges1.length; i++) - { - List shortestPath = dsp.getPath(start, indexer.inverse().get(i)); - Integer[] indices = shortestPaths1[i-1]; - for (ListIterator iter = shortestPath.listIterator(); iter.hasNext(); ) - { - int j = iter.nextIndex(); - Integer e = iter.next(); - if (e != null) - assertEquals(edge_array[indices[j].intValue()], e); - else - assertNull(indices[j]); - } - } + 0, + 2, + 3, + 1, + 3, + 6, + 5, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + 2, + 0, + 5, + 3, + 5, + 8, + 7, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + 3, + 5, + 0, + 2, + 4, + 5, + 6, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + 1, + 3, + 2, + 0, + 2, + 5, + 4, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + 3, + 5, + 4, + 2, + 0, + 7, + 6, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + 6, + 8, + 5, + 5, + 7, + 0, + 1, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + 5, + 7, + 6, + 4, + 6, + 1, + 0, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY + }, + { + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + 0, + 3, + 2 + }, + { + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + 3, + 0, + 1 + }, + { + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + 2, + 1, + 0 } - - private void weightedTest(Network g, BiMap indexer, int index, boolean cached) { - String start = indexer.inverse().get(index); - double[] distances1 = null; - Integer[] incomingEdges1 = null; - if (g.isDirected()) - { - distances1 = dg_distances[index-1]; - incomingEdges1 = dg_incomingEdges[index-1]; - } else { - distances1 = ug_distances[index-1]; - incomingEdges1 = ug_incomingEdges[index-1]; - } - assertEquals(distances1.length, g.nodes().size()); - assertEquals(incomingEdges1.length, g.nodes().size()); - DijkstraShortestPath dsp = - new DijkstraShortestPath(g, nev, cached); - Integer[] edge_array = edgeArrays.get(g); - - // test getDistance(start, v) - for (int i = 1; i <= distances1.length; i++) { - String v = indexer.inverse().get(i); - Number n = dsp.getDistance(start, v); - double d = distances1[i-1]; - double dist; - if (n == null) - dist = Double.POSITIVE_INFINITY; - else - dist = n.doubleValue(); - - assertEquals(d, dist, .001); - } + }; - // test getIncomingEdge(start, v) - dsp.reset(); - for (int i = 1; i <= incomingEdges1.length; i++) - { - String v = indexer.inverse().get(i); - Integer e = dsp.getIncomingEdge(start, v); - if (e != null) - assertEquals(edge_array[incomingEdges1[i-1].intValue()], e); - else - assertNull(incomingEdges1[i-1]); - } - - // test getDistanceMap(v) - dsp.reset(); - Map distances = dsp.getDistanceMap(start); - assertTrue(distances.size() <= g.nodes().size()); - double d_prev = 0; // smallest possible distance - Set reachable = new HashSet(); - for (Iterator d_iter = distances.keySet().iterator(); d_iter.hasNext(); ) - { - String cur = d_iter.next(); - double d_cur = ((Double)distances.get(cur)).doubleValue(); - assertTrue(d_cur >= d_prev); - - d_prev = d_cur; - int i = indexer.get(cur); - assertEquals(distances1[i-1], d_cur, .001); - reachable.add(cur); - } - // make sure that non-reachable vertices have no entries - for (Iterator v_iter = g.nodes().iterator(); v_iter.hasNext(); ) - { - String v = v_iter.next(); - assertEquals(reachable.contains(v), distances.keySet().contains(v)); - } - - // test getIncomingEdgeMap(v) - dsp.reset(); - Map incomingEdgeMap = dsp.getIncomingEdgeMap(start); - assertTrue(incomingEdgeMap.size() <= g.nodes().size()); - for (Iterator e_iter = incomingEdgeMap.keySet().iterator(); e_iter.hasNext(); ) - { - String v = e_iter.next(); - Integer e = incomingEdgeMap.get(v); - int i = indexer.get(v); - if (e != null) - assertEquals(edge_array[incomingEdges1[i-1].intValue()], e); - else - assertNull(incomingEdges1[i-1]); - } - - // test getDistanceMap(v, k) - dsp.reset(); - for (int i = 1; i <= distances1.length; i++) - { - distances = dsp.getDistanceMap(start, i); - assertTrue(distances.size() <= i); - d_prev = 0; // smallest possible distance - - reachable.clear(); - for (String cur : distances.keySet()) - { - double d_cur = ((Double)distances.get(cur)).doubleValue(); - assertTrue(d_cur >= d_prev); - - d_prev = d_cur; - int j = indexer.get(cur); - - assertEquals(distances1[j-1], d_cur, .001); - reachable.add(cur); - } - for (String node : g.nodes()) - { - assertEquals(reachable.contains(node), distances.keySet().contains(node)); - } + private static Integer[][] shortestPaths1 = { + null, + {new Integer(0)}, + {new Integer(1), new Integer(6)}, + {new Integer(1)}, + {new Integer(1), new Integer(7)}, + {new Integer(1), new Integer(9), new Integer(11)}, + {new Integer(1), new Integer(9)}, + null, + null, + null + }; + + private Map, Integer[]> edgeArrays; + + private Map edgeWeights; + + private Function nev; + + private Supplier vertexFactoryDG = + new Supplier() { + int count = 0; + + public String get() { + return "V" + count++; } - - // test getIncomingEdgeMap(v, k) - dsp.reset(); - for (int i = 1; i <= incomingEdges1.length; i++) - { - incomingEdgeMap = dsp.getIncomingEdgeMap(start, i); - assertTrue(incomingEdgeMap.size() <= i); - for (Iterator e_iter = incomingEdgeMap.keySet().iterator(); e_iter.hasNext(); ) - { - String v = e_iter.next(); - Integer e = incomingEdgeMap.get(v); - int j = indexer.get(v); - if (e != null) - assertEquals(edge_array[incomingEdges1[j-1].intValue()], e); - else - assertNull(incomingEdges1[j-1]); - } + }; + private Supplier vertexFactoryUG = + new Supplier() { + int count = 0; + + public String get() { + return "U" + count++; } + }; + + BiMap did; + BiMap uid; + + @Override + protected void setUp() { + edgeWeights = new HashMap(); + nev = Functions.forMap(edgeWeights); + dg = NetworkBuilder.directed().allowsParallelEdges(true).allowsSelfLoops(true).build(); + for (int i = 0; i < dg_distances.length; i++) { + dg.addNode(vertexFactoryDG.get()); } - - public void addEdges(MutableNetwork g, BiMap indexer, Integer[] edge_array) - { - for (int i = 0; i < edges.length; i++) - { - int[] edge = edges[i]; - Integer e = i; - g.addEdge(indexer.inverse().get(edge[0]), indexer.inverse().get(edge[1]), i); - edge_array[i] = e; - if (edge.length > 2) { - edgeWeights.put(e, edge[2]); - } - } + did = Indexer.create(dg.nodes(), 1); + Integer[] dg_array = new Integer[edges.length]; + addEdges(dg, did, dg_array); + + ug = NetworkBuilder.undirected().allowsParallelEdges(true).allowsSelfLoops(true).build(); + for (int i = 0; i < ug_distances.length; i++) { + ug.addNode(vertexFactoryUG.get()); + } + uid = Indexer.create(ug.nodes(), 1); + Integer[] ug_array = new Integer[edges.length]; + addEdges(ug, uid, ug_array); + + edgeArrays = new HashMap, Integer[]>(); + edgeArrays.put(dg, dg_array); + edgeArrays.put(ug, ug_array); + } + + @Override + protected void tearDown() throws Exception {} + + public void exceptionTest( + MutableNetwork g, BiMap indexer, int index) { + DijkstraShortestPath dsp = new DijkstraShortestPath(g, nev); + String start = indexer.inverse().get(index); + Integer e = null; + + String v = "NOT IN GRAPH"; + + try { + dsp.getDistance(start, v); + fail("getDistance(): illegal destination vertex"); + } catch (IllegalArgumentException iae) { + } + try { + dsp.getDistance(v, start); + fail("getDistance(): illegal source vertex"); + } catch (IllegalArgumentException iae) { + } + try { + dsp.getDistanceMap(v, 1); + fail("getDistanceMap(): illegal source vertex"); + } catch (IllegalArgumentException iae) { + } + try { + dsp.getDistanceMap(start, 0); + fail("getDistanceMap(): too few vertices requested"); + } catch (IllegalArgumentException iae) { + } + try { + dsp.getDistanceMap(start, g.nodes().size() + 1); + fail("getDistanceMap(): too many vertices requested"); + } catch (IllegalArgumentException iae) { + } + + try { + dsp.getIncomingEdge(start, v); + fail("getIncomingEdge(): illegal destination vertex"); + } catch (IllegalArgumentException iae) { + } + try { + dsp.getIncomingEdge(v, start); + fail("getIncomingEdge(): illegal source vertex"); + } catch (IllegalArgumentException iae) { + } + try { + dsp.getIncomingEdgeMap(v, 1); + fail("getIncomingEdgeMap(): illegal source vertex"); + } catch (IllegalArgumentException iae) { + } + try { + dsp.getIncomingEdgeMap(start, 0); + fail("getIncomingEdgeMap(): too few vertices requested"); + } catch (IllegalArgumentException iae) { + } + try { + dsp.getDistanceMap(start, g.nodes().size() + 1); + fail("getIncomingEdgeMap(): too many vertices requested"); + } catch (IllegalArgumentException iae) { + } + + try { + // test negative edge weight exception + String v1 = indexer.inverse().get(1); + String v2 = indexer.inverse().get(7); + e = g.edges().size() + 1; + g.addEdge(v1, v2, e); + edgeWeights.put(e, -2); + dsp.reset(); + dsp.getDistanceMap(start); + fail("DijkstraShortestPath should not accept negative edge weights"); + } catch (IllegalArgumentException iae) { + g.removeEdge(e); + } + } + + public void testDijkstra() { + setUp(); + exceptionTest(dg, did, 1); + + setUp(); + exceptionTest(ug, uid, 1); + + setUp(); + getPathTest(dg, did, 1); + + setUp(); + getPathTest(ug, uid, 1); + + for (int i = 1; i <= dg_distances.length; i++) { + setUp(); + weightedTest(dg, did, i, true); + + setUp(); + weightedTest(dg, did, i, false); + } + + for (int i = 1; i <= ug_distances.length; i++) { + setUp(); + weightedTest(ug, uid, i, true); + + setUp(); + weightedTest(ug, uid, i, false); + } + } + + private void getPathTest(Network g, BiMap indexer, int index) { + DijkstraShortestPath dsp = new DijkstraShortestPath(g, nev); + String start = indexer.inverse().get(index); + Integer[] edge_array = edgeArrays.get(g); + Integer[] incomingEdges1 = + g.isDirected() ? dg_incomingEdges[index - 1] : ug_incomingEdges[index - 1]; + assertEquals(incomingEdges1.length, g.nodes().size()); + + // test getShortestPath(start, v) + dsp.reset(); + for (int i = 1; i <= incomingEdges1.length; i++) { + List shortestPath = dsp.getPath(start, indexer.inverse().get(i)); + Integer[] indices = shortestPaths1[i - 1]; + for (ListIterator iter = shortestPath.listIterator(); iter.hasNext(); ) { + int j = iter.nextIndex(); + Integer e = iter.next(); + if (e != null) assertEquals(edge_array[indices[j].intValue()], e); + else assertNull(indices[j]); + } + } + } + + private void weightedTest( + Network g, BiMap indexer, int index, boolean cached) { + String start = indexer.inverse().get(index); + double[] distances1 = null; + Integer[] incomingEdges1 = null; + if (g.isDirected()) { + distances1 = dg_distances[index - 1]; + incomingEdges1 = dg_incomingEdges[index - 1]; + } else { + distances1 = ug_distances[index - 1]; + incomingEdges1 = ug_incomingEdges[index - 1]; + } + assertEquals(distances1.length, g.nodes().size()); + assertEquals(incomingEdges1.length, g.nodes().size()); + DijkstraShortestPath dsp = + new DijkstraShortestPath(g, nev, cached); + Integer[] edge_array = edgeArrays.get(g); + + // test getDistance(start, v) + for (int i = 1; i <= distances1.length; i++) { + String v = indexer.inverse().get(i); + Number n = dsp.getDistance(start, v); + double d = distances1[i - 1]; + double dist; + if (n == null) dist = Double.POSITIVE_INFINITY; + else dist = n.doubleValue(); + + assertEquals(d, dist, .001); + } + + // test getIncomingEdge(start, v) + dsp.reset(); + for (int i = 1; i <= incomingEdges1.length; i++) { + String v = indexer.inverse().get(i); + Integer e = dsp.getIncomingEdge(start, v); + if (e != null) assertEquals(edge_array[incomingEdges1[i - 1].intValue()], e); + else assertNull(incomingEdges1[i - 1]); + } + + // test getDistanceMap(v) + dsp.reset(); + Map distances = dsp.getDistanceMap(start); + assertTrue(distances.size() <= g.nodes().size()); + double d_prev = 0; // smallest possible distance + Set reachable = new HashSet(); + for (Iterator d_iter = distances.keySet().iterator(); d_iter.hasNext(); ) { + String cur = d_iter.next(); + double d_cur = ((Double) distances.get(cur)).doubleValue(); + assertTrue(d_cur >= d_prev); + + d_prev = d_cur; + int i = indexer.get(cur); + assertEquals(distances1[i - 1], d_cur, .001); + reachable.add(cur); + } + // make sure that non-reachable vertices have no entries + for (Iterator v_iter = g.nodes().iterator(); v_iter.hasNext(); ) { + String v = v_iter.next(); + assertEquals(reachable.contains(v), distances.keySet().contains(v)); + } + + // test getIncomingEdgeMap(v) + dsp.reset(); + Map incomingEdgeMap = dsp.getIncomingEdgeMap(start); + assertTrue(incomingEdgeMap.size() <= g.nodes().size()); + for (Iterator e_iter = incomingEdgeMap.keySet().iterator(); e_iter.hasNext(); ) { + String v = e_iter.next(); + Integer e = incomingEdgeMap.get(v); + int i = indexer.get(v); + if (e != null) assertEquals(edge_array[incomingEdges1[i - 1].intValue()], e); + else assertNull(incomingEdges1[i - 1]); + } + + // test getDistanceMap(v, k) + dsp.reset(); + for (int i = 1; i <= distances1.length; i++) { + distances = dsp.getDistanceMap(start, i); + assertTrue(distances.size() <= i); + d_prev = 0; // smallest possible distance + + reachable.clear(); + for (String cur : distances.keySet()) { + double d_cur = ((Double) distances.get(cur)).doubleValue(); + assertTrue(d_cur >= d_prev); + + d_prev = d_cur; + int j = indexer.get(cur); + + assertEquals(distances1[j - 1], d_cur, .001); + reachable.add(cur); + } + for (String node : g.nodes()) { + assertEquals(reachable.contains(node), distances.keySet().contains(node)); + } + } + + // test getIncomingEdgeMap(v, k) + dsp.reset(); + for (int i = 1; i <= incomingEdges1.length; i++) { + incomingEdgeMap = dsp.getIncomingEdgeMap(start, i); + assertTrue(incomingEdgeMap.size() <= i); + for (Iterator e_iter = incomingEdgeMap.keySet().iterator(); e_iter.hasNext(); ) { + String v = e_iter.next(); + Integer e = incomingEdgeMap.get(v); + int j = indexer.get(v); + if (e != null) assertEquals(edge_array[incomingEdges1[j - 1].intValue()], e); + else assertNull(incomingEdges1[j - 1]); + } + } + } + + public void addEdges( + MutableNetwork g, BiMap indexer, Integer[] edge_array) { + for (int i = 0; i < edges.length; i++) { + int[] edge = edges[i]; + Integer e = i; + g.addEdge(indexer.inverse().get(edge[0]), indexer.inverse().get(edge[1]), i); + edge_array[i] = e; + if (edge.length > 2) { + edgeWeights.put(e, edge[2]); + } } + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestUnweightedShortestPath.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestUnweightedShortestPath.java index d9672032..1e2be42d 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestUnweightedShortestPath.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestUnweightedShortestPath.java @@ -8,77 +8,73 @@ import com.google.common.collect.BiMap; import com.google.common.graph.GraphBuilder; import com.google.common.graph.MutableGraph; - import edu.uci.ics.jung.algorithms.util.Indexer; import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; -/** - * @author Scott White - */ -public class TestUnweightedShortestPath extends TestCase -{ - private Supplier vertexFactory = - new Supplier() { - int count = 0; - public String get() { - return "V"+count++; - }}; - - BiMap id; +/** @author Scott White */ +public class TestUnweightedShortestPath extends TestCase { + private Supplier vertexFactory = + new Supplier() { + int count = 0; + + public String get() { + return "V" + count++; + } + }; + + BiMap id; + + @Override + protected void setUp() {} - @Override - protected void setUp() { + public static Test suite() { + return new TestSuite(TestUnweightedShortestPath.class); + } + + public void testUndirected() { + MutableGraph ug = GraphBuilder.undirected().allowsSelfLoops(true).build(); + for (int i = 0; i < 5; i++) { + ug.addNode(vertexFactory.get()); } - public static Test suite() - { - return new TestSuite(TestUnweightedShortestPath.class); - } - - public void testUndirected() { - MutableGraph ug = GraphBuilder.undirected().allowsSelfLoops(true).build(); - for(int i=0; i<5; i++) { - ug.addNode(vertexFactory.get()); - } - id = Indexer.create(ug.nodes()); + id = Indexer.create(ug.nodes()); + + // GraphUtils.addVertices(ug,5); + // Indexer id = Indexer.getIndexer(ug); + ug.putEdge(id.inverse().get(0), id.inverse().get(1)); + ug.putEdge(id.inverse().get(1), id.inverse().get(2)); + ug.putEdge(id.inverse().get(2), id.inverse().get(3)); + ug.putEdge(id.inverse().get(0), id.inverse().get(4)); + ug.putEdge(id.inverse().get(4), id.inverse().get(3)); -// GraphUtils.addVertices(ug,5); -// Indexer id = Indexer.getIndexer(ug); - ug.putEdge(id.inverse().get(0), id.inverse().get(1)); - ug.putEdge(id.inverse().get(1), id.inverse().get(2)); - ug.putEdge(id.inverse().get(2), id.inverse().get(3)); - ug.putEdge(id.inverse().get(0), id.inverse().get(4)); - ug.putEdge(id.inverse().get(4), id.inverse().get(3)); - - UnweightedShortestPath usp = - new UnweightedShortestPath(ug); - Assert.assertEquals(usp.getDistance(id.inverse().get(0),id.inverse().get(3)).intValue(),2); - Assert.assertEquals((usp.getDistanceMap(id.inverse().get(0)).get(id.inverse().get(3))).intValue(),2); - Assert.assertNull(usp.getIncomingEdgeMap(id.inverse().get(0)).get(id.inverse().get(0))); - Assert.assertNotNull(usp.getIncomingEdgeMap(id.inverse().get(0)).get(id.inverse().get(3))); - } - - public void testDirected() { - MutableGraph dg = GraphBuilder.directed().allowsSelfLoops(true).build(); - for(int i=0; i<5; i++) { - dg.addNode(vertexFactory.get()); - } - id = Indexer.create(dg.nodes()); - dg.putEdge(id.inverse().get(0), id.inverse().get(1)); - dg.putEdge(id.inverse().get(1), id.inverse().get(2)); - dg.putEdge(id.inverse().get(2), id.inverse().get(3)); - dg.putEdge(id.inverse().get(0), id.inverse().get(4)); - dg.putEdge(id.inverse().get(4), id.inverse().get(3)); - dg.putEdge(id.inverse().get(3), id.inverse().get(0)); - - UnweightedShortestPath usp = - new UnweightedShortestPath(dg); - Assert.assertEquals(usp.getDistance(id.inverse().get(0),id.inverse().get(3)).intValue(),2); - Assert.assertEquals((usp.getDistanceMap(id.inverse().get(0)).get(id.inverse().get(3))).intValue(),2); - Assert.assertNull(usp.getIncomingEdgeMap(id.inverse().get(0)).get(id.inverse().get(0))); - Assert.assertNotNull(usp.getIncomingEdgeMap(id.inverse().get(0)).get(id.inverse().get(3))); + UnweightedShortestPath usp = new UnweightedShortestPath(ug); + Assert.assertEquals(usp.getDistance(id.inverse().get(0), id.inverse().get(3)).intValue(), 2); + Assert.assertEquals( + (usp.getDistanceMap(id.inverse().get(0)).get(id.inverse().get(3))).intValue(), 2); + Assert.assertNull(usp.getIncomingEdgeMap(id.inverse().get(0)).get(id.inverse().get(0))); + Assert.assertNotNull(usp.getIncomingEdgeMap(id.inverse().get(0)).get(id.inverse().get(3))); + } + + public void testDirected() { + MutableGraph dg = GraphBuilder.directed().allowsSelfLoops(true).build(); + for (int i = 0; i < 5; i++) { + dg.addNode(vertexFactory.get()); + } + id = Indexer.create(dg.nodes()); + dg.putEdge(id.inverse().get(0), id.inverse().get(1)); + dg.putEdge(id.inverse().get(1), id.inverse().get(2)); + dg.putEdge(id.inverse().get(2), id.inverse().get(3)); + dg.putEdge(id.inverse().get(0), id.inverse().get(4)); + dg.putEdge(id.inverse().get(4), id.inverse().get(3)); + dg.putEdge(id.inverse().get(3), id.inverse().get(0)); - } + UnweightedShortestPath usp = new UnweightedShortestPath(dg); + Assert.assertEquals(usp.getDistance(id.inverse().get(0), id.inverse().get(3)).intValue(), 2); + Assert.assertEquals( + (usp.getDistanceMap(id.inverse().get(0)).get(id.inverse().get(3))).intValue(), 2); + Assert.assertNull(usp.getIncomingEdgeMap(id.inverse().get(0)).get(id.inverse().get(0))); + Assert.assertNotNull(usp.getIncomingEdgeMap(id.inverse().get(0)).get(id.inverse().get(3))); + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/util/NotRandom.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/util/NotRandom.java index 9d311e6e..01779fd1 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/util/NotRandom.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/util/NotRandom.java @@ -3,58 +3,50 @@ import java.util.Random; /** - * A decidedly non-random extension of {@code Random} that may be useful - * for testing random algorithms that accept an instance of {@code Random} - * as a parameter. This algorithm maintains internal counters which are - * incremented after each call, and returns values which are functions of - * those counter values. Thus the output is not only deterministic (as is - * necessarily true of all software with no externalities) but precisely - * predictable in distribution. - * + * A decidedly non-random extension of {@code Random} that may be useful for testing random + * algorithms that accept an instance of {@code Random} as a parameter. This algorithm maintains + * internal counters which are incremented after each call, and returns values which are functions + * of those counter values. Thus the output is not only deterministic (as is necessarily true of all + * software with no externalities) but precisely predictable in distribution. + * * @author Joshua O'Madadhain */ @SuppressWarnings("serial") -public class NotRandom extends Random -{ - private int i = 0; - private int d = 0; - private int size = 100; - - /** - * Creates an instance with the specified sample size. - * @param size the sample size - */ - public NotRandom(int size) - { - this.size = size; - } - - /** - * Returns the post-incremented value of the internal counter modulo n. - */ - @Override - public int nextInt(int n) - { - return i++ % n; - } - - /** - * Returns the post-incremented value of the internal counter modulo - * {@code size}, divided by {@code size}. - */ - @Override - public double nextDouble() - { - return (d++ % size) / (double)size; - } - - /** - * Returns the post-incremented value of the internal counter modulo - * {@code size}, divided by {@code size}. - */ - @Override - public float nextFloat() - { - return (d++ % size) / (float)size; - } +public class NotRandom extends Random { + private int i = 0; + private int d = 0; + private int size = 100; + + /** + * Creates an instance with the specified sample size. + * + * @param size the sample size + */ + public NotRandom(int size) { + this.size = size; + } + + /** Returns the post-incremented value of the internal counter modulo n. */ + @Override + public int nextInt(int n) { + return i++ % n; + } + + /** + * Returns the post-incremented value of the internal counter modulo {@code size}, divided by + * {@code size}. + */ + @Override + public double nextDouble() { + return (d++ % size) / (double) size; + } + + /** + * Returns the post-incremented value of the internal counter modulo {@code size}, divided by + * {@code size}. + */ + @Override + public float nextFloat() { + return (d++ % size) / (float) size; + } } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/util/TestWeightedChoice.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/util/TestWeightedChoice.java index 3e89b3d9..a6b7ef2a 100644 --- a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/util/TestWeightedChoice.java +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/util/TestWeightedChoice.java @@ -1,78 +1,61 @@ /** - * Copyright (c) 2009, The JUNG Authors + * Copyright (c) 2009, The JUNG Authors * - * All rights reserved. + *

    All rights reserved. * - * This software is open-source under the BSD license; see either - * "license.txt" or - * https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * Created on Jan 13, 2009 - * + *

    This software is open-source under the BSD license; see either "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. Created on Jan 13, 2009 */ package edu.uci.ics.jung.algorithms.util; import java.util.HashMap; import java.util.Map; - import junit.framework.TestCase; -/** - * @author jrtom - * - */ -public class TestWeightedChoice extends TestCase -{ - private WeightedChoice weighted_choice; - private Map item_weights = new HashMap(); - private Map item_counts = new HashMap(); - - @Override - public void tearDown() - { - item_weights.clear(); - item_counts.clear(); - } +/** @author jrtom */ +public class TestWeightedChoice extends TestCase { + private WeightedChoice weighted_choice; + private Map item_weights = new HashMap(); + private Map item_counts = new HashMap(); + + @Override + public void tearDown() { + item_weights.clear(); + item_counts.clear(); + } + + private void initializeWeights(double[] weights) { + item_weights.put("a", weights[0]); + item_weights.put("b", weights[1]); + item_weights.put("c", weights[2]); + item_weights.put("d", weights[3]); + + for (String key : item_weights.keySet()) item_counts.put(key, 0); + } + + private void runWeightedChoice() { + weighted_choice = new WeightedChoice(item_weights, new NotRandom(100)); + + int max_iterations = 10000; + for (int i = 0; i < max_iterations; i++) { + String item = weighted_choice.nextItem(); + int count = item_counts.get(item); + item_counts.put(item, count + 1); + } + + for (String key : item_weights.keySet()) + assertEquals((int) (item_weights.get(key) * max_iterations), item_counts.get(key).intValue()); + } + + public void testUniform() { + initializeWeights(new double[] {0.25, 0.25, 0.25, 0.25}); - private void initializeWeights(double[] weights) - { - item_weights.put("a", weights[0]); - item_weights.put("b", weights[1]); - item_weights.put("c", weights[2]); - item_weights.put("d", weights[3]); - - for (String key : item_weights.keySet()) - item_counts.put(key, 0); + runWeightedChoice(); + } - } + public void testNonUniform() { + initializeWeights(new double[] {0.45, 0.10, 0.13, 0.32}); - private void runWeightedChoice() - { - weighted_choice = new WeightedChoice(item_weights, new NotRandom(100)); - - int max_iterations = 10000; - for (int i = 0; i < max_iterations; i++) - { - String item = weighted_choice.nextItem(); - int count = item_counts.get(item); - item_counts.put(item, count+1); - } - - for (String key : item_weights.keySet()) - assertEquals((int)(item_weights.get(key) * max_iterations), - item_counts.get(key).intValue()); - } - - public void testUniform() - { - initializeWeights(new double[]{0.25, 0.25, 0.25, 0.25}); - - runWeightedChoice(); - } - - public void testNonUniform() - { - initializeWeights(new double[]{0.45, 0.10, 0.13, 0.32}); - - runWeightedChoice(); - } + runWeightedChoice(); + } } diff --git a/jung-api/src/main/java/edu/uci/ics/jung/graph/BaseTree.java b/jung-api/src/main/java/edu/uci/ics/jung/graph/BaseTree.java index 3765ecda..1077bac7 100644 --- a/jung-api/src/main/java/edu/uci/ics/jung/graph/BaseTree.java +++ b/jung-api/src/main/java/edu/uci/ics/jung/graph/BaseTree.java @@ -4,38 +4,37 @@ /** * Interface defining tree operations common to all tree types. - * - * @author Joshua O'Madadhain * + * @author Joshua O'Madadhain * @param the node type */ interface BaseTree { - /** - * Returns the root of this tree (the single node in this tree with no predecessor), - * or {@code Optional#empty()} if the tree is empty. - */ - public Optional root(); + /** + * Returns the root of this tree (the single node in this tree with no predecessor), or {@code + * Optional#empty()} if the tree is empty. + */ + public Optional root(); - /** - * Returns the predecessor of {@code node} in this tree, or {@code Optional#empty()} if - * {@code node} is this tree's root. - * - * @throws IllegalArgumentException if {@code node} is not an element of this tree. - */ - public Optional predecessor(N node); + /** + * Returns the predecessor of {@code node} in this tree, or {@code Optional#empty()} if {@code + * node} is this tree's root. + * + * @throws IllegalArgumentException if {@code node} is not an element of this tree. + */ + public Optional predecessor(N node); - /** - * Returns the number of edges that one must traverse from the {@code root} of this - * tree in order to reach {@code node}. - * @param node the node whose depth is being requested - * - * @throws IllegalArgumentException if {@code node} is not an element of this tree. - */ - public int depth(N node); + /** + * Returns the number of edges that one must traverse from the {@code root} of this tree in order + * to reach {@code node}. + * + * @param node the node whose depth is being requested + * @throws IllegalArgumentException if {@code node} is not an element of this tree. + */ + public int depth(N node); - /** - * Returns the maximum depth of all nodes in this tree. - * If the tree is empty, returns {@code Optional#empty()}. - */ - public Optional height(); + /** + * Returns the maximum depth of all nodes in this tree. If the tree is empty, returns {@code + * Optional#empty()}. + */ + public Optional height(); } diff --git a/jung-api/src/main/java/edu/uci/ics/jung/graph/CTree.java b/jung-api/src/main/java/edu/uci/ics/jung/graph/CTree.java index 94516d6f..df12b67b 100644 --- a/jung-api/src/main/java/edu/uci/ics/jung/graph/CTree.java +++ b/jung-api/src/main/java/edu/uci/ics/jung/graph/CTree.java @@ -1,7 +1,7 @@ /* * Created on Feb 12, 2017 * - * Copyright (c) 2017, The JUNG Authors + * Copyright (c) 2017, The JUNG Authors * * All rights reserved. * @@ -13,20 +13,14 @@ import com.google.common.graph.Graph; -/** - * A subtype of Graph that is a directed rooted tree. - */ +/** A subtype of Graph that is a directed rooted tree. */ public interface CTree extends BaseTree, Graph { - /** - * Returns {@code true}; trees are always directed (away from the root). - */ - @Override - public boolean isDirected(); + /** Returns {@code true}; trees are always directed (away from the root). */ + @Override + public boolean isDirected(); - /** - * Returns {@code false}; trees may never have self-loops. - */ - @Override - public boolean allowsSelfLoops(); + /** Returns {@code false}; trees may never have self-loops. */ + @Override + public boolean allowsSelfLoops(); } diff --git a/jung-api/src/main/java/edu/uci/ics/jung/graph/CTreeNetwork.java b/jung-api/src/main/java/edu/uci/ics/jung/graph/CTreeNetwork.java index 51d3b297..fb0bbd45 100644 --- a/jung-api/src/main/java/edu/uci/ics/jung/graph/CTreeNetwork.java +++ b/jung-api/src/main/java/edu/uci/ics/jung/graph/CTreeNetwork.java @@ -4,28 +4,21 @@ /** * A subtype of Network that is a directed rooted tree. - * - * @author Joshua O'Madadhain * + * @author Joshua O'Madadhain * @param the node type * @param the edge type */ public interface CTreeNetwork extends BaseTree, Network { - /** - * Returns {@code true}; trees are always directed (away from the root). - */ - @Override - public boolean isDirected(); + /** Returns {@code true}; trees are always directed (away from the root). */ + @Override + public boolean isDirected(); - /** - * Returns {@code false}; trees may never have self-loops. - */ - @Override - public boolean allowsSelfLoops(); + /** Returns {@code false}; trees may never have self-loops. */ + @Override + public boolean allowsSelfLoops(); - /** - * Returns {@code false}; trees may never have parallel edges. - */ - @Override - public boolean allowsParallelEdges(); + /** Returns {@code false}; trees may never have parallel edges. */ + @Override + public boolean allowsParallelEdges(); } diff --git a/jung-api/src/main/java/edu/uci/ics/jung/graph/DelegateCTree.java b/jung-api/src/main/java/edu/uci/ics/jung/graph/DelegateCTree.java index 3691de1c..577f0df7 100644 --- a/jung-api/src/main/java/edu/uci/ics/jung/graph/DelegateCTree.java +++ b/jung-api/src/main/java/edu/uci/ics/jung/graph/DelegateCTree.java @@ -1,7 +1,7 @@ /* * Created on Feb 12, 2017 * - * Copyright (c) 2017, The JUNG Authors + * Copyright (c) 2017, The JUNG Authors * * All rights reserved. * @@ -11,193 +11,192 @@ */ package edu.uci.ics.jung.graph; -import java.util.List; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.common.graph.ElementOrder; import com.google.common.graph.EndpointPair; import com.google.common.graph.Graphs; import com.google.common.graph.MutableGraph; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; class DelegateCTree implements MutableCTree { - private final MutableGraph delegate; - private final Map depths; - private Optional height; - private Optional root; - - DelegateCTree(MutableGraph graph, Optional root) { - this.delegate = graph; - this.depths = new HashMap(); - if (root.isPresent()) { - this.addNode(root.get()); - } - } - - @Override - public Optional root() { - return root; - } - - @Override - public Optional predecessor(N node) { - Set predecessors = delegate.predecessors(node); - Preconditions.checkState(predecessors.size() <= 1); - return predecessors.isEmpty() - ? Optional.empty() - : Optional.of(Iterables.getOnlyElement(predecessors)); - } - - @Override - public int depth(N node) { - Preconditions.checkArgument(delegate.nodes().contains(node)); - return depths.get(node); - } - - @Override - public Optional height() { - if (!root.isPresent()) { - return Optional.empty(); - } - // Calculate the height - if (!height.isPresent()) { - calculateHeight(); - } - return height; - } - - @Override - public boolean isDirected() { - return true; - } - - @Override - public boolean allowsSelfLoops() { - return false; - } - - @Override - public Set adjacentNodes(N node) { - return delegate.adjacentNodes(node); - } - - @Override - public int degree(N node) { - return delegate.degree(node); - } - - @Override - public int inDegree(N node) { - return delegate.inDegree(node); - } - - @Override - public ElementOrder nodeOrder() { - return delegate.nodeOrder(); - } - - @Override - public Set nodes() { - return delegate.nodes(); - } - - @Override - public int outDegree(N node) { - return delegate.outDegree(node); - } - - @Override - public Set predecessors(N node) { - return delegate.predecessors(node); - } - - @Override - public Set successors(N node) { - return delegate.successors(node); - } - - @Override - public Set> edges() { - return delegate.edges(); - } - - @Override - public boolean addNode(N node) { - if (root().equals(node)) { - return false; - } - Preconditions.checkArgument(nodes().isEmpty()); - delegate.addNode(node); - this.root = Optional.of(node); - setDepth(node, null); - return true; - } - - @Override - public boolean putEdge(N nodeU, N nodeV) { - if (nodes().isEmpty()) { - this.addNode(nodeU); // set the root - } else { - Preconditions.checkArgument(nodes().contains(nodeU)); - if (successors(nodeU).contains(nodeV)) { - return false; // edge is already present; no-op - } - // verify that nodeV is not in the tree - Preconditions.checkArgument(!nodes().contains(nodeV)); - } - setDepth(nodeV, nodeU); - return delegate.putEdge(nodeU, nodeV); - } - - private void setDepth(N node, N parent) { - if (parent == null) { // root: both depth and height are 0 - depths.put(node, 0); - height = Optional.of(0); - } else { - int nodeDepth = depths.get(parent) + 1; - height = Optional.of(Math.max(nodeDepth, height.get())); - } - } - - private int calculateHeight() { - // This method is only called when the root is present, so we don't need to check for that. - int currentHeight = 0; - List currentLevel = new ArrayList(); - List nextLevel; - currentLevel.addAll(successors(root.get())); - while (!currentLevel.isEmpty()) { - nextLevel = new ArrayList(); - currentHeight++; // there's at least one node in the current level - for (N node : currentLevel) { - nextLevel.addAll(successors(node)); - } - currentLevel = nextLevel; - } - return currentHeight; - } - - @Override - public boolean removeNode(N node) { - if (!nodes().contains(node)) { - return false; - } - for (N nodeToRemove : Graphs.reachableNodes(delegate, node)) { - delegate.removeNode(nodeToRemove); - depths.remove(nodeToRemove); - } - // Reset the height, since we don't know how it was affected by removing the subtree. - this.height = Optional.empty(); - return true; - } - - @Override - public boolean removeEdge(N nodeU, N nodeV) { - delegate.removeEdge(nodeU, nodeV); - return this.removeNode(nodeV); - } + private final MutableGraph delegate; + private final Map depths; + private Optional height; + private Optional root; + + DelegateCTree(MutableGraph graph, Optional root) { + this.delegate = graph; + this.depths = new HashMap(); + if (root.isPresent()) { + this.addNode(root.get()); + } + } + + @Override + public Optional root() { + return root; + } + + @Override + public Optional predecessor(N node) { + Set predecessors = delegate.predecessors(node); + Preconditions.checkState(predecessors.size() <= 1); + return predecessors.isEmpty() + ? Optional.empty() + : Optional.of(Iterables.getOnlyElement(predecessors)); + } + + @Override + public int depth(N node) { + Preconditions.checkArgument(delegate.nodes().contains(node)); + return depths.get(node); + } + + @Override + public Optional height() { + if (!root.isPresent()) { + return Optional.empty(); + } + // Calculate the height + if (!height.isPresent()) { + calculateHeight(); + } + return height; + } + + @Override + public boolean isDirected() { + return true; + } + + @Override + public boolean allowsSelfLoops() { + return false; + } + + @Override + public Set adjacentNodes(N node) { + return delegate.adjacentNodes(node); + } + + @Override + public int degree(N node) { + return delegate.degree(node); + } + + @Override + public int inDegree(N node) { + return delegate.inDegree(node); + } + + @Override + public ElementOrder nodeOrder() { + return delegate.nodeOrder(); + } + + @Override + public Set nodes() { + return delegate.nodes(); + } + + @Override + public int outDegree(N node) { + return delegate.outDegree(node); + } + + @Override + public Set predecessors(N node) { + return delegate.predecessors(node); + } + + @Override + public Set successors(N node) { + return delegate.successors(node); + } + + @Override + public Set> edges() { + return delegate.edges(); + } + + @Override + public boolean addNode(N node) { + if (root().equals(node)) { + return false; + } + Preconditions.checkArgument(nodes().isEmpty()); + delegate.addNode(node); + this.root = Optional.of(node); + setDepth(node, null); + return true; + } + + @Override + public boolean putEdge(N nodeU, N nodeV) { + if (nodes().isEmpty()) { + this.addNode(nodeU); // set the root + } else { + Preconditions.checkArgument(nodes().contains(nodeU)); + if (successors(nodeU).contains(nodeV)) { + return false; // edge is already present; no-op + } + // verify that nodeV is not in the tree + Preconditions.checkArgument(!nodes().contains(nodeV)); + } + setDepth(nodeV, nodeU); + return delegate.putEdge(nodeU, nodeV); + } + + private void setDepth(N node, N parent) { + if (parent == null) { // root: both depth and height are 0 + depths.put(node, 0); + height = Optional.of(0); + } else { + int nodeDepth = depths.get(parent) + 1; + height = Optional.of(Math.max(nodeDepth, height.get())); + } + } + + private int calculateHeight() { + // This method is only called when the root is present, so we don't need to check for that. + int currentHeight = 0; + List currentLevel = new ArrayList(); + List nextLevel; + currentLevel.addAll(successors(root.get())); + while (!currentLevel.isEmpty()) { + nextLevel = new ArrayList(); + currentHeight++; // there's at least one node in the current level + for (N node : currentLevel) { + nextLevel.addAll(successors(node)); + } + currentLevel = nextLevel; + } + return currentHeight; + } + + @Override + public boolean removeNode(N node) { + if (!nodes().contains(node)) { + return false; + } + for (N nodeToRemove : Graphs.reachableNodes(delegate, node)) { + delegate.removeNode(nodeToRemove); + depths.remove(nodeToRemove); + } + // Reset the height, since we don't know how it was affected by removing the subtree. + this.height = Optional.empty(); + return true; + } + + @Override + public boolean removeEdge(N nodeU, N nodeV) { + delegate.removeEdge(nodeU, nodeV); + return this.removeNode(nodeV); + } } diff --git a/jung-api/src/main/java/edu/uci/ics/jung/graph/DelegateCTreeNetwork.java b/jung-api/src/main/java/edu/uci/ics/jung/graph/DelegateCTreeNetwork.java index 5b321ebe..0017be94 100644 --- a/jung-api/src/main/java/edu/uci/ics/jung/graph/DelegateCTreeNetwork.java +++ b/jung-api/src/main/java/edu/uci/ics/jung/graph/DelegateCTreeNetwork.java @@ -1,12 +1,5 @@ package edu.uci.ics.jung.graph; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.common.graph.ElementOrder; @@ -14,228 +7,233 @@ import com.google.common.graph.Graph; import com.google.common.graph.Graphs; import com.google.common.graph.MutableNetwork; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; class DelegateCTreeNetwork implements MutableCTreeNetwork { - private final MutableNetwork delegate; - private final Map depths; - private Optional height; - private Optional root = Optional.empty(); - - DelegateCTreeNetwork(MutableNetwork graph, Optional root) { - this.delegate = graph; - this.depths = new HashMap(); - if (root.isPresent()) { - this.addNode(root.get()); - } - } - - @Override - public Optional root() { - return root; - } - - @Override - public Optional predecessor(N node) { - Set predecessors = delegate.predecessors(node); - Preconditions.checkState(predecessors.size() <= 1); - return predecessors.isEmpty() - ? Optional.empty() - : Optional.of(Iterables.getOnlyElement(predecessors)); - } - - @Override - public int depth(N node) { - Preconditions.checkArgument(delegate.nodes().contains(node)); - return depths.get(node); - } - - @Override - public Optional height() { - if (!root.isPresent()) { - return Optional.empty(); - } - // Calculate the height - if (!height.isPresent()) { - calculateHeight(); - } - return height; - } - - @Override - public boolean isDirected() { - return true; - } - - @Override - public boolean allowsSelfLoops() { - return false; - } - - @Override - public Set adjacentNodes(N node) { - return delegate.adjacentNodes(node); - } - - @Override - public int degree(N node) { - return delegate.degree(node); - } - - @Override - public int inDegree(N node) { - return delegate.inDegree(node); - } - - @Override - public ElementOrder nodeOrder() { - return delegate.nodeOrder(); - } - - @Override - public Set nodes() { - return delegate.nodes(); - } - - @Override - public int outDegree(N node) { - return delegate.outDegree(node); - } - - @Override - public Set predecessors(N node) { - return delegate.predecessors(node); - } - - @Override - public Set successors(N node) { - return delegate.successors(node); - } - - @Override - public Set edges() { - return delegate.edges(); - } - - @Override - public Set adjacentEdges(E arg0) { - return delegate.adjacentEdges(arg0); - } - - @Override - public boolean allowsParallelEdges() { - return false; - } - - @Override - public Graph asGraph() { - return delegate.asGraph(); - } - - @Override - public ElementOrder edgeOrder() { - return delegate.edgeOrder(); - } - - @Override - public Set edgesConnecting(N nodeU, N nodeV) { - return delegate.edgesConnecting(nodeU, nodeV); - } - - @Override - public Set inEdges(N node) { - return delegate.inEdges(node); - } - - @Override - public Set incidentEdges(N node) { - return delegate.incidentEdges(node); - } - - @Override - public EndpointPair incidentNodes(E edge) { - return delegate.incidentNodes(edge); - } - - @Override - public Set outEdges(N node) { - return delegate.outEdges(node); - } - - @Override - public boolean addNode(N node) { - if (root.isPresent() && root.get().equals(node)) { - return false; - } - Preconditions.checkArgument(nodes().isEmpty()); - delegate.addNode(node); - this.root = Optional.of(node); - setDepth(node, null); - return true; - } - - @Override - public boolean addEdge(N nodeU, N nodeV, E edge) { - if (nodes().isEmpty()) { - this.addNode(nodeU); // set the root - } else { - Preconditions.checkArgument(nodes().contains(nodeU)); - if (successors(nodeU).contains(nodeV)) { - return false; // edge is already present; no-op - } - // verify that nodeV is not in the tree - Preconditions.checkArgument(!nodes().contains(nodeV)); - } - setDepth(nodeV, nodeU); - return delegate.addEdge(nodeU, nodeV, edge); - } - - private void setDepth(N node, N parent) { - if (parent == null) { // root: both depth and height are 0 - depths.put(node, 0); - height = Optional.of(0); - } else { - int nodeDepth = depths.get(parent) + 1; - height = Optional.of(Math.max(nodeDepth, height.get())); - depths.put(node, nodeDepth); - } - } - - private int calculateHeight() { - // This method is only called when the root is present, so we don't need to check for that. - int currentHeight = 0; - List currentLevel = new ArrayList(); - List nextLevel; - currentLevel.addAll(successors(root.get())); - while (!currentLevel.isEmpty()) { - nextLevel = new ArrayList(); - currentHeight++; // there's at least one node in the current level - for (N node : currentLevel) { - nextLevel.addAll(successors(node)); - } - currentLevel = nextLevel; - } - return currentHeight; - } - - @Override - public boolean removeNode(N node) { - if (!nodes().contains(node)) { - return false; - } - for (N nodeToRemove : Graphs.reachableNodes(delegate.asGraph(), node)) { - delegate.removeNode(nodeToRemove); - depths.remove(nodeToRemove); - } - // Reset the height, since we don't know how it was affected by removing the subtree. - this.height = Optional.empty(); - return true; - } - - @Override - public boolean removeEdge(E edge) { - delegate.removeEdge(edge); - // remove the subtree rooted at this edge's target - return this.removeNode(delegate.incidentNodes(edge).target()); - } - + private final MutableNetwork delegate; + private final Map depths; + private Optional height; + private Optional root = Optional.empty(); + + DelegateCTreeNetwork(MutableNetwork graph, Optional root) { + this.delegate = graph; + this.depths = new HashMap(); + if (root.isPresent()) { + this.addNode(root.get()); + } + } + + @Override + public Optional root() { + return root; + } + + @Override + public Optional predecessor(N node) { + Set predecessors = delegate.predecessors(node); + Preconditions.checkState(predecessors.size() <= 1); + return predecessors.isEmpty() + ? Optional.empty() + : Optional.of(Iterables.getOnlyElement(predecessors)); + } + + @Override + public int depth(N node) { + Preconditions.checkArgument(delegate.nodes().contains(node)); + return depths.get(node); + } + + @Override + public Optional height() { + if (!root.isPresent()) { + return Optional.empty(); + } + // Calculate the height + if (!height.isPresent()) { + calculateHeight(); + } + return height; + } + + @Override + public boolean isDirected() { + return true; + } + + @Override + public boolean allowsSelfLoops() { + return false; + } + + @Override + public Set adjacentNodes(N node) { + return delegate.adjacentNodes(node); + } + + @Override + public int degree(N node) { + return delegate.degree(node); + } + + @Override + public int inDegree(N node) { + return delegate.inDegree(node); + } + + @Override + public ElementOrder nodeOrder() { + return delegate.nodeOrder(); + } + + @Override + public Set nodes() { + return delegate.nodes(); + } + + @Override + public int outDegree(N node) { + return delegate.outDegree(node); + } + + @Override + public Set predecessors(N node) { + return delegate.predecessors(node); + } + + @Override + public Set successors(N node) { + return delegate.successors(node); + } + + @Override + public Set edges() { + return delegate.edges(); + } + + @Override + public Set adjacentEdges(E arg0) { + return delegate.adjacentEdges(arg0); + } + + @Override + public boolean allowsParallelEdges() { + return false; + } + + @Override + public Graph asGraph() { + return delegate.asGraph(); + } + + @Override + public ElementOrder edgeOrder() { + return delegate.edgeOrder(); + } + + @Override + public Set edgesConnecting(N nodeU, N nodeV) { + return delegate.edgesConnecting(nodeU, nodeV); + } + + @Override + public Set inEdges(N node) { + return delegate.inEdges(node); + } + + @Override + public Set incidentEdges(N node) { + return delegate.incidentEdges(node); + } + + @Override + public EndpointPair incidentNodes(E edge) { + return delegate.incidentNodes(edge); + } + + @Override + public Set outEdges(N node) { + return delegate.outEdges(node); + } + + @Override + public boolean addNode(N node) { + if (root.isPresent() && root.get().equals(node)) { + return false; + } + Preconditions.checkArgument(nodes().isEmpty()); + delegate.addNode(node); + this.root = Optional.of(node); + setDepth(node, null); + return true; + } + + @Override + public boolean addEdge(N nodeU, N nodeV, E edge) { + if (nodes().isEmpty()) { + this.addNode(nodeU); // set the root + } else { + Preconditions.checkArgument(nodes().contains(nodeU)); + if (successors(nodeU).contains(nodeV)) { + return false; // edge is already present; no-op + } + // verify that nodeV is not in the tree + Preconditions.checkArgument(!nodes().contains(nodeV)); + } + setDepth(nodeV, nodeU); + return delegate.addEdge(nodeU, nodeV, edge); + } + + private void setDepth(N node, N parent) { + if (parent == null) { // root: both depth and height are 0 + depths.put(node, 0); + height = Optional.of(0); + } else { + int nodeDepth = depths.get(parent) + 1; + height = Optional.of(Math.max(nodeDepth, height.get())); + depths.put(node, nodeDepth); + } + } + + private int calculateHeight() { + // This method is only called when the root is present, so we don't need to check for that. + int currentHeight = 0; + List currentLevel = new ArrayList(); + List nextLevel; + currentLevel.addAll(successors(root.get())); + while (!currentLevel.isEmpty()) { + nextLevel = new ArrayList(); + currentHeight++; // there's at least one node in the current level + for (N node : currentLevel) { + nextLevel.addAll(successors(node)); + } + currentLevel = nextLevel; + } + return currentHeight; + } + + @Override + public boolean removeNode(N node) { + if (!nodes().contains(node)) { + return false; + } + for (N nodeToRemove : Graphs.reachableNodes(delegate.asGraph(), node)) { + delegate.removeNode(nodeToRemove); + depths.remove(nodeToRemove); + } + // Reset the height, since we don't know how it was affected by removing the subtree. + this.height = Optional.empty(); + return true; + } + + @Override + public boolean removeEdge(E edge) { + delegate.removeEdge(edge); + // remove the subtree rooted at this edge's target + return this.removeNode(delegate.incidentNodes(edge).target()); + } } diff --git a/jung-api/src/main/java/edu/uci/ics/jung/graph/MutableCTree.java b/jung-api/src/main/java/edu/uci/ics/jung/graph/MutableCTree.java index 2f9b0e32..098f0d6b 100644 --- a/jung-api/src/main/java/edu/uci/ics/jung/graph/MutableCTree.java +++ b/jung-api/src/main/java/edu/uci/ics/jung/graph/MutableCTree.java @@ -1,7 +1,7 @@ /* * Created on Feb 12, 2017 * - * Copyright (c) 2017, The JUNG Authors + * Copyright (c) 2017, The JUNG Authors * * All rights reserved. * @@ -14,57 +14,57 @@ import com.google.common.graph.MutableGraph; /** - * A subtype of CTree that permits - * @author joshua + * A subtype of CTree that permits * + * @author joshua * @param */ public interface MutableCTree extends CTree, MutableGraph { - /** - * {@inheritDoc} - * - *

    May only be called if one of the following conditions holds: - *

      - *
    • the tree is empty (in this case {@code node} will become the tree's root) - *
    • {@code node} is already the root of this tree - *
    - * - * @throws IllegalArgumentException if neither of the above conditions holds - */ - @Override - boolean addNode(N node); + /** + * {@inheritDoc} + * + *

    May only be called if one of the following conditions holds: + * + *

      + *
    • the tree is empty (in this case {@code node} will become the tree's root) + *
    • {@code node} is already the root of this tree + *
    + * + * @throws IllegalArgumentException if neither of the above conditions holds + */ + @Override + boolean addNode(N node); - /** - * {@inheritDoc} - * - *

    Adds {@code nodeV} as a new successor of {@code nodeU}. - * Requirements: - *

      - *
    • {@code nodeU} must be already in the tree, or the tree must be empty - *
    • {@code nodeV} must either not be in the tree, or there must be - * an existing edge connecting {@code nodeU} to {@code nodeV} (in which - * case this method is a no-op). - *
    - */ - @Override - boolean putEdge(N nodeU, N nodeV); + /** + * {@inheritDoc} + * + *

    Adds {@code nodeV} as a new successor of {@code nodeU}. Requirements: + * + *

      + *
    • {@code nodeU} must be already in the tree, or the tree must be empty + *
    • {@code nodeV} must either not be in the tree, or there must be an existing edge + * connecting {@code nodeU} to {@code nodeV} (in which case this method is a no-op). + *
    + */ + @Override + boolean putEdge(N nodeU, N nodeV); - /** - * {@inheritDoc} - * - *

    Removes all nodes from the subtree rooted at {@code node}, so that this graph - * continues to be a rooted tree. - */ - @Override - boolean removeNode(N node); + /** + * {@inheritDoc} + * + *

    Removes all nodes from the subtree rooted at {@code node}, so that this graph continues to + * be a rooted tree. + */ + @Override + boolean removeNode(N node); - /** - * {@inheritDoc} - * - *

    Removes all nodes from the subtree rooted at {@code nodeV}, so that this graph - * continues to be a rooted tree. - */ - @Override - boolean removeEdge(N nodeU, N nodeV); + /** + * {@inheritDoc} + * + *

    Removes all nodes from the subtree rooted at {@code nodeV}, so that this graph continues to + * be a rooted tree. + */ + @Override + boolean removeEdge(N nodeU, N nodeV); } diff --git a/jung-api/src/main/java/edu/uci/ics/jung/graph/MutableCTreeNetwork.java b/jung-api/src/main/java/edu/uci/ics/jung/graph/MutableCTreeNetwork.java index 6e310fed..ce27c086 100644 --- a/jung-api/src/main/java/edu/uci/ics/jung/graph/MutableCTreeNetwork.java +++ b/jung-api/src/main/java/edu/uci/ics/jung/graph/MutableCTreeNetwork.java @@ -3,51 +3,51 @@ import com.google.common.graph.MutableNetwork; public interface MutableCTreeNetwork extends MutableNetwork, CTreeNetwork { - /** - * {@inheritDoc} - * - *

    May only be called if one of the following conditions holds: - *

      - *
    • the tree is empty (in this case {@code node} will become the tree's root) - *
    • {@code node} is already the root of this tree - *
    - * - * @throws IllegalArgumentException if neither of the above conditions holds - */ - @Override - boolean addNode(N node); + /** + * {@inheritDoc} + * + *

    May only be called if one of the following conditions holds: + * + *

      + *
    • the tree is empty (in this case {@code node} will become the tree's root) + *
    • {@code node} is already the root of this tree + *
    + * + * @throws IllegalArgumentException if neither of the above conditions holds + */ + @Override + boolean addNode(N node); - /** - * {@inheritDoc} - * - *

    Adds {@code nodeV} as a new successor of {@code nodeU}, and - * {@code edge} as a new outgoing edge of {@code nodeU}. - * Requirements: - *

      - *
    • {@code nodeU} must be already in the tree, or the tree must be empty - *
    • {@code nodeV} must either not be in the tree, or {@code edge} - * must already connect {@code nodeU} to {@code nodeV} (in which - * case this method is a no-op). - *
    - */ - @Override - boolean addEdge(N nodeU, N nodeV, E edge); + /** + * {@inheritDoc} + * + *

    Adds {@code nodeV} as a new successor of {@code nodeU}, and {@code edge} as a new outgoing + * edge of {@code nodeU}. Requirements: + * + *

      + *
    • {@code nodeU} must be already in the tree, or the tree must be empty + *
    • {@code nodeV} must either not be in the tree, or {@code edge} must already connect {@code + * nodeU} to {@code nodeV} (in which case this method is a no-op). + *
    + */ + @Override + boolean addEdge(N nodeU, N nodeV, E edge); - /** - * {@inheritDoc} - * - *

    Removes all nodes from the subtree rooted at {@code node}, so that this graph - * continues to be a rooted tree. - */ - @Override - boolean removeNode(N node); + /** + * {@inheritDoc} + * + *

    Removes all nodes from the subtree rooted at {@code node}, so that this graph continues to + * be a rooted tree. + */ + @Override + boolean removeNode(N node); - /** - * {@inheritDoc} - * - *

    Removes all nodes from the subtree rooted at the source of {@code edge}, - * so that this graph continues to be a rooted tree. - */ - @Override - boolean removeEdge(E edge); + /** + * {@inheritDoc} + * + *

    Removes all nodes from the subtree rooted at the source of {@code edge}, so that this graph + * continues to be a rooted tree. + */ + @Override + boolean removeEdge(E edge); } diff --git a/jung-api/src/main/java/edu/uci/ics/jung/graph/TreeBuilder.java b/jung-api/src/main/java/edu/uci/ics/jung/graph/TreeBuilder.java index 291b8b68..298b6868 100644 --- a/jung-api/src/main/java/edu/uci/ics/jung/graph/TreeBuilder.java +++ b/jung-api/src/main/java/edu/uci/ics/jung/graph/TreeBuilder.java @@ -1,7 +1,7 @@ /* * Created on Feb 12, 2017 * - * Copyright (c) 2017, The JUNG Authors + * Copyright (c) 2017, The JUNG Authors * * All rights reserved. * @@ -14,90 +14,78 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import java.util.Optional; - import com.google.common.graph.ElementOrder; import com.google.common.graph.GraphBuilder; import com.google.common.graph.MutableGraph; +import java.util.Optional; public class TreeBuilder { - ElementOrder nodeOrder = ElementOrder.insertion(); - Optional expectedNodeCount = Optional.empty(); - Optional root = Optional.empty(); + ElementOrder nodeOrder = ElementOrder.insertion(); + Optional expectedNodeCount = Optional.empty(); + Optional root = Optional.empty(); - /** - * Returns a {@link TreeBuilder} instance. - */ - public static TreeBuilder builder() { - return new TreeBuilder(); - } + /** Returns a {@link TreeBuilder} instance. */ + public static TreeBuilder builder() { + return new TreeBuilder(); + } - /** - * Returns a {@link TreeBuilder} initialized with all properties queryable - * from {@code tree}. - * - *

    - * The "queryable" properties are those that are exposed through the - * {@link CTree} interface, such as {@link CTree#nodeOrder()}. Other - * properties, such as {@link #expectedNodeCount(int)}, are not set in the - * new builder. - */ - public static TreeBuilder from(CTree tree) { - return new TreeBuilder().nodeOrder(tree.nodeOrder()); - } + /** + * Returns a {@link TreeBuilder} initialized with all properties queryable from {@code tree}. + * + *

    The "queryable" properties are those that are exposed through the {@link CTree} interface, + * such as {@link CTree#nodeOrder()}. Other properties, such as {@link #expectedNodeCount(int)}, + * are not set in the new builder. + */ + public static TreeBuilder from(CTree tree) { + return new TreeBuilder().nodeOrder(tree.nodeOrder()); + } - /** - * Specifies the root of this graph. - */ - public TreeBuilder withRoot(N1 root) { - checkNotNull(root); - TreeBuilder newBuilder = cast(); - newBuilder.root = Optional.of(root); - return newBuilder; - } + /** Specifies the root of this graph. */ + public TreeBuilder withRoot(N1 root) { + checkNotNull(root); + TreeBuilder newBuilder = cast(); + newBuilder.root = Optional.of(root); + return newBuilder; + } - /** - * Specifies the expected number of nodes in the graph. - * - * @throws IllegalArgumentException - * if {@code expectedNodeCount} is negative - */ - public TreeBuilder expectedNodeCount(int expectedNodeCount) { - checkArgument(expectedNodeCount >= 0); - this.expectedNodeCount = Optional.of(expectedNodeCount); - return this; - } + /** + * Specifies the expected number of nodes in the graph. + * + * @throws IllegalArgumentException if {@code expectedNodeCount} is negative + */ + public TreeBuilder expectedNodeCount(int expectedNodeCount) { + checkArgument(expectedNodeCount >= 0); + this.expectedNodeCount = Optional.of(expectedNodeCount); + return this; + } - /** - * Specifies the order of iteration for the elements of - * {@link Graph#nodes()}. - */ - public TreeBuilder nodeOrder(ElementOrder nodeOrder) { - TreeBuilder newBuilder = cast(); - newBuilder.nodeOrder = checkNotNull(nodeOrder); - return newBuilder; - } + /** + * Specifies the order of iteration for the elements of {@link + * com.google.common.graph.Graph#nodes()}. + */ + public TreeBuilder nodeOrder(ElementOrder nodeOrder) { + TreeBuilder newBuilder = cast(); + newBuilder.nodeOrder = checkNotNull(nodeOrder); + return newBuilder; + } - /** - * Returns an empty {@link MutableCTree} with the properties of this - * {@link TreeBuilder}. - */ - // TODO(jrtom): decide how we're going to handle different implementations. - // For the graph stuff, we don't really need different implementations, but - // for trees, maybe we do; at least for binary trees vs. trees with no restrictions on outgoing edges... - public MutableCTree build() { - GraphBuilder graphBuilder = GraphBuilder.directed().allowsSelfLoops(false); - if (expectedNodeCount.isPresent()) { - graphBuilder = graphBuilder.expectedNodeCount(expectedNodeCount.get()); - } - MutableGraph delegate = graphBuilder.nodeOrder(nodeOrder).build(); - @SuppressWarnings("unchecked") - Optional rootCast = (Optional) root; - return new DelegateCTree(delegate, rootCast); - } + /** Returns an empty {@link MutableCTree} with the properties of this {@link TreeBuilder}. */ + // TODO(jrtom): decide how we're going to handle different implementations. + // For the graph stuff, we don't really need different implementations, but + // for trees, maybe we do; at least for binary trees vs. trees with no restrictions on outgoing edges... + public MutableCTree build() { + GraphBuilder graphBuilder = GraphBuilder.directed().allowsSelfLoops(false); + if (expectedNodeCount.isPresent()) { + graphBuilder = graphBuilder.expectedNodeCount(expectedNodeCount.get()); + } + MutableGraph delegate = graphBuilder.nodeOrder(nodeOrder).build(); + @SuppressWarnings("unchecked") + Optional rootCast = (Optional) root; + return new DelegateCTree(delegate, rootCast); + } - @SuppressWarnings("unchecked") - private TreeBuilder cast() { - return (TreeBuilder) this; - } + @SuppressWarnings("unchecked") + private TreeBuilder cast() { + return (TreeBuilder) this; + } } diff --git a/jung-api/src/main/java/edu/uci/ics/jung/graph/TreeNetworkBuilder.java b/jung-api/src/main/java/edu/uci/ics/jung/graph/TreeNetworkBuilder.java index 0dc633c1..17fe85a7 100644 --- a/jung-api/src/main/java/edu/uci/ics/jung/graph/TreeNetworkBuilder.java +++ b/jung-api/src/main/java/edu/uci/ics/jung/graph/TreeNetworkBuilder.java @@ -1,7 +1,7 @@ /* * Created on Feb 12, 2017 * - * Copyright (c) 2017, The JUNG Authors + * Copyright (c) 2017, The JUNG Authors * * All rights reserved. * @@ -14,103 +14,88 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import java.util.Optional; - import com.google.common.graph.ElementOrder; import com.google.common.graph.MutableNetwork; import com.google.common.graph.NetworkBuilder; +import java.util.Optional; public class TreeNetworkBuilder { - ElementOrder nodeOrder = ElementOrder.insertion(); - ElementOrder edgeOrder = ElementOrder.insertion(); - Optional expectedNodeCount = Optional.empty(); - Optional root = Optional.empty(); + ElementOrder nodeOrder = ElementOrder.insertion(); + ElementOrder edgeOrder = ElementOrder.insertion(); + Optional expectedNodeCount = Optional.empty(); + Optional root = Optional.empty(); - /** - * Returns a {@link TreeNetworkBuilder} instance. - */ - public static TreeNetworkBuilder builder() { - return new TreeNetworkBuilder(); - } + /** Returns a {@link TreeNetworkBuilder} instance. */ + public static TreeNetworkBuilder builder() { + return new TreeNetworkBuilder(); + } - /** - * Returns a {@link TreeNetworkBuilder} initialized with all properties queryable - * from {@code tree}. - * - *

    - * The "queryable" properties are those that are exposed through the - * {@link CTree} interface, such as {@link CTree#nodeOrder()}. Other - * properties, such as {@link #expectedNodeCount(int)}, are not set in the - * new builder. - */ - public static TreeNetworkBuilder from(CTreeNetwork tree) { - return new TreeNetworkBuilder().nodeOrder(tree.nodeOrder()); - } + /** + * Returns a {@link TreeNetworkBuilder} initialized with all properties queryable from {@code + * tree}. + * + *

    The "queryable" properties are those that are exposed through the {@link CTree} interface, + * such as {@link CTree#nodeOrder()}. Other properties, such as {@link #expectedNodeCount(int)}, + * are not set in the new builder. + */ + public static TreeNetworkBuilder from(CTreeNetwork tree) { + return new TreeNetworkBuilder().nodeOrder(tree.nodeOrder()); + } - /** - * Specifies the root of this graph. - */ - public TreeNetworkBuilder withRoot(N1 root) { - checkNotNull(root); - TreeNetworkBuilder newBuilder = cast(); - newBuilder.root = Optional.of(root); - return newBuilder; - } + /** Specifies the root of this graph. */ + public TreeNetworkBuilder withRoot(N1 root) { + checkNotNull(root); + TreeNetworkBuilder newBuilder = cast(); + newBuilder.root = Optional.of(root); + return newBuilder; + } - /** - * Specifies the expected number of nodes in the graph. - * - * @throws IllegalArgumentException - * if {@code expectedNodeCount} is negative - */ - public TreeNetworkBuilder expectedNodeCount(int expectedNodeCount) { - checkArgument(expectedNodeCount >= 0); - this.expectedNodeCount = Optional.of(expectedNodeCount); - return this; - } + /** + * Specifies the expected number of nodes in the graph. + * + * @throws IllegalArgumentException if {@code expectedNodeCount} is negative + */ + public TreeNetworkBuilder expectedNodeCount(int expectedNodeCount) { + checkArgument(expectedNodeCount >= 0); + this.expectedNodeCount = Optional.of(expectedNodeCount); + return this; + } - /** - * Specifies the order of iteration for the elements of - * {@link Graph#nodes()}. - */ - public TreeNetworkBuilder nodeOrder(ElementOrder nodeOrder) { - TreeNetworkBuilder newBuilder = cast(); - newBuilder.nodeOrder = checkNotNull(nodeOrder); - return newBuilder; - } + /** Specifies the order of iteration for the elements of {@link Graph#nodes()}. */ + public TreeNetworkBuilder nodeOrder(ElementOrder nodeOrder) { + TreeNetworkBuilder newBuilder = cast(); + newBuilder.nodeOrder = checkNotNull(nodeOrder); + return newBuilder; + } - /** - * Specifies the order of iteration for the elements of - * {@link Graph#nodes()}. - */ - public TreeNetworkBuilder edgeOrder(ElementOrder edgeOrder) { - TreeNetworkBuilder newBuilder = cast(); - newBuilder.edgeOrder = checkNotNull(edgeOrder); - return newBuilder; - } + /** Specifies the order of iteration for the elements of {@link Graph#nodes()}. */ + public TreeNetworkBuilder edgeOrder(ElementOrder edgeOrder) { + TreeNetworkBuilder newBuilder = cast(); + newBuilder.edgeOrder = checkNotNull(edgeOrder); + return newBuilder; + } - /** - * Returns an empty {@link MutableCTree} with the properties of this - * {@link TreeNetworkBuilder}. - */ - // TODO(jrtom): decide how we're going to handle different implementations. - // For the graph stuff, we don't really need different implementations, but - // for trees, maybe we do; at least for binary trees vs. trees with no restrictions on outgoing edges... - public MutableCTreeNetwork build() { - NetworkBuilder graphBuilder = NetworkBuilder.directed() - .allowsSelfLoops(false) - .allowsParallelEdges(false); - if (expectedNodeCount.isPresent()) { - graphBuilder = graphBuilder.expectedNodeCount(expectedNodeCount.get()); - } - MutableNetwork delegate = graphBuilder.nodeOrder(nodeOrder).edgeOrder(edgeOrder).build(); - @SuppressWarnings("unchecked") - Optional rootCast = (Optional) root; - return new DelegateCTreeNetwork(delegate, rootCast); - } + /** + * Returns an empty {@link MutableCTree} with the properties of this {@link TreeNetworkBuilder}. + */ + // TODO(jrtom): decide how we're going to handle different implementations. + // For the graph stuff, we don't really need different implementations, but + // for trees, maybe we do; at least for binary trees vs. trees with no restrictions on outgoing edges... + public MutableCTreeNetwork build() { + NetworkBuilder graphBuilder = + NetworkBuilder.directed().allowsSelfLoops(false).allowsParallelEdges(false); + if (expectedNodeCount.isPresent()) { + graphBuilder = graphBuilder.expectedNodeCount(expectedNodeCount.get()); + } + MutableNetwork delegate = + graphBuilder.nodeOrder(nodeOrder).edgeOrder(edgeOrder).build(); + @SuppressWarnings("unchecked") + Optional rootCast = (Optional) root; + return new DelegateCTreeNetwork(delegate, rootCast); + } - @SuppressWarnings("unchecked") - private TreeNetworkBuilder cast() { - return (TreeNetworkBuilder) this; - } + @SuppressWarnings("unchecked") + private TreeNetworkBuilder cast() { + return (TreeNetworkBuilder) this; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/GraphIOException.java b/jung-io/src/main/java/edu/uci/ics/jung/io/GraphIOException.java index b1914c56..b0ac0859 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/GraphIOException.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/GraphIOException.java @@ -17,39 +17,38 @@ */ public class GraphIOException extends Exception { - private static final long serialVersionUID = 3773882099782535606L; - - /** - * Creates a new instance with no specified message or cause. - */ - public GraphIOException() { - super(); - } - - /** - * Creates a new instance with the specified message and cause. - * @param message a description of the exception-triggering event - * @param cause the exception which triggered this one - */ - public GraphIOException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Creates a new instance with the specified message and no - * specified cause. - * @param message a description of the exception-triggering event - */ - public GraphIOException(String message) { - super(message); - } - - /** - * Creats a new instance with the specified cause and no specified message. - * @param cause the exception which triggered this one - */ - public GraphIOException(Throwable cause) { - super(cause); - } - + private static final long serialVersionUID = 3773882099782535606L; + + /** Creates a new instance with no specified message or cause. */ + public GraphIOException() { + super(); + } + + /** + * Creates a new instance with the specified message and cause. + * + * @param message a description of the exception-triggering event + * @param cause the exception which triggered this one + */ + public GraphIOException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Creates a new instance with the specified message and no specified cause. + * + * @param message a description of the exception-triggering event + */ + public GraphIOException(String message) { + super(message); + } + + /** + * Creats a new instance with the specified cause and no specified message. + * + * @param cause the exception which triggered this one + */ + public GraphIOException(Throwable cause) { + super(cause); + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/GraphMLMetadata.java b/jung-io/src/main/java/edu/uci/ics/jung/io/GraphMLMetadata.java index 4212b6a4..6e59993d 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/GraphMLMetadata.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/GraphMLMetadata.java @@ -1,53 +1,39 @@ /** - * Copyright (c) 2008, The JUNG Authors + * Copyright (c) 2008, The JUNG Authors * - * All rights reserved. + *

    All rights reserved. * - * This software is open-source under the BSD license; see either - * "license.txt" or - * https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * Created on Jun 30, 2008 - * + *

    This software is open-source under the BSD license; see either "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. Created on Jun 30, 2008 */ package edu.uci.ics.jung.io; import com.google.common.base.Function; /** - * Maintains information relating to data for the specified type. - * This includes a Function from objects to their values, - * a default value, and a description. + * Maintains information relating to data for the specified type. This includes a Function from + * objects to their values, a default value, and a description. */ -public class GraphMLMetadata -{ - /** - * The description of this data type. - */ - public String description; - - /** - * The default value for objects of this type. - */ - public String default_value; - - /** - * A Function mapping objects to string representations of their values. - */ - public Function transformer; - - /** - * Creates a new instance with the specified description, - * default value, and function. - * - * @param description a textual description of the object - * @param default_value the default value for the object, as a String - * @param function maps objects of this type to string representations - */ - public GraphMLMetadata(String description, String default_value, - Function function) - { - this.description = description; - this.transformer = function; - this.default_value = default_value; - } +public class GraphMLMetadata { + /** The description of this data type. */ + public String description; + + /** The default value for objects of this type. */ + public String default_value; + + /** A Function mapping objects to string representations of their values. */ + public Function transformer; + + /** + * Creates a new instance with the specified description, default value, and function. + * + * @param description a textual description of the object + * @param default_value the default value for the object, as a String + * @param function maps objects of this type to string representations + */ + public GraphMLMetadata(String description, String default_value, Function function) { + this.description = description; + this.transformer = function; + this.default_value = default_value; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/GraphMLReader.java b/jung-io/src/main/java/edu/uci/ics/jung/io/GraphMLReader.java index 904fb79d..4900e5bb 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/GraphMLReader.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/GraphMLReader.java @@ -11,6 +11,12 @@ */ package edu.uci.ics.jung.io; +import com.google.common.base.Supplier; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.graph.MutableNetwork; +import edu.uci.ics.jung.algorithms.util.MapSettableTransformer; +import edu.uci.ics.jung.algorithms.util.SettableTransformer; import java.io.FileReader; import java.io.IOException; import java.io.Reader; @@ -18,802 +24,738 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; - import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; - import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.helpers.DefaultHandler; -import com.google.common.base.Supplier; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.graph.MutableNetwork; - -import edu.uci.ics.jung.algorithms.util.MapSettableTransformer; -import edu.uci.ics.jung.algorithms.util.SettableTransformer; - /** - * Reads in data from a GraphML-formatted file and generates graphs based on - * that data. Currently supports the following parts of the GraphML - * specification: + * Reads in data from a GraphML-formatted file and generates graphs based on that data. Currently + * supports the following parts of the GraphML specification: + * *

      - *
    • graphs and hypergraphs - *
    • directed and undirected edges - *
    • graph, vertex, edge data - *
    • graph, vertex, edge descriptions and data descriptions - *
    • vertex and edge IDs + *
    • graphs and hypergraphs + *
    • directed and undirected edges + *
    • graph, vertex, edge data + *
    • graph, vertex, edge descriptions and data descriptions + *
    • vertex and edge IDs *
    + * * Each of these is exposed via appropriate get methods. * - * Does not currently support nested graphs or ports. + *

    Does not currently support nested graphs or ports. * - *

    Note that the user is responsible for supplying a graph - * Factory that can support the edge types in the supplied - * GraphML file. If the graph generated by the Factory is - * not compatible (for example: if the graph does not accept directed - * edges, and the GraphML file contains a directed edge) then the results - * are graph-implementation-dependent. + *

    Note that the user is responsible for supplying a graph Factory that can support + * the edge types in the supplied GraphML file. If the graph generated by the Factory + * is not compatible (for example: if the graph does not accept directed edges, and the GraphML file + * contains a directed edge) then the results are graph-implementation-dependent. * * @see "http://graphml.graphdrawing.org/specification.html" */ -public class GraphMLReader, V, E> extends DefaultHandler -{ - protected enum TagState {NO_TAG, VERTEX, EDGE, HYPEREDGE, ENDPOINT, GRAPH, - DATA, KEY, DESC, DEFAULT_KEY, GRAPHML, OTHER} - - protected enum KeyType {NONE, VERTEX, EDGE, GRAPH, ALL} - - protected SAXParser saxp; - protected boolean default_directed; - protected G current_graph; - protected V current_vertex; - protected E current_edge; - protected String current_key; - protected LinkedList current_states; - protected BiMap tag_state; - protected Supplier graph_factory; - protected Supplier vertex_factory; - protected Supplier edge_factory; - protected BiMap vertex_ids; - protected BiMap edge_ids; - protected Map> graph_metadata; - protected Map> vertex_metadata; - protected Map> edge_metadata; - protected Map vertex_desc; - protected Map edge_desc; - protected Map graph_desc; - protected KeyType key_type; - - protected List graphs; - - protected StringBuilder current_text = new StringBuilder(); - - // TODO(jrtom): replace graph supplier with NetworkBuilder, or just provide another overload? - - /** - * Creates a GraphMLReader instance with the specified - * vertex and edge factories. - * - * @param vertex_factory the vertex supplier to use to create vertex objects - * @param edge_factory the edge supplier to use to create edge objects - * @throws ParserConfigurationException if a SAX parser cannot be constructed - * @throws SAXException if the SAX parser factory cannot be constructed - */ - public GraphMLReader(Supplier vertex_factory, - Supplier edge_factory) - throws ParserConfigurationException, SAXException - { - current_vertex = null; - current_edge = null; - - SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); - saxp = saxParserFactory.newSAXParser(); - - current_states = new LinkedList(); - - tag_state = HashBiMap.create(); - tag_state.put("node", TagState.VERTEX); - tag_state.put("edge", TagState.EDGE); - tag_state.put("hyperedge", TagState.HYPEREDGE); - tag_state.put("endpoint", TagState.ENDPOINT); - tag_state.put("graph", TagState.GRAPH); - tag_state.put("data", TagState.DATA); - tag_state.put("key", TagState.KEY); - tag_state.put("desc", TagState.DESC); - tag_state.put("default", TagState.DEFAULT_KEY); - tag_state.put("graphml", TagState.GRAPHML); - - this.key_type = KeyType.NONE; - - this.vertex_factory = vertex_factory; - this.edge_factory = edge_factory; - } - - /** - * Creates a GraphMLReader instance that assigns the vertex - * and edge id strings to be the vertex and edge objects, - * as well as their IDs. - * Note that this requires that (a) each edge have a valid ID, which is not - * normally a requirement for edges in GraphML, and (b) that the vertex - * and edge types be assignment-compatible with String. - * @throws ParserConfigurationException if a SAX parser cannot be constructed - * @throws SAXException if the SAX parser factory cannot be constructed - */ - public GraphMLReader() throws ParserConfigurationException, SAXException - { - this(null, null); - } - - /** - * Returns a list of the graphs parsed from the specified reader, as created by - * the specified Supplier. - * @param reader the source of the graph data in GraphML format - * @param graph_factory used to build graph instances - * @return the graphs parsed from the specified reader - * @throws IOException if an error is encountered while parsing the graph - */ - public List loadMultiple(Reader reader, Supplier graph_factory) - throws IOException - { - this.graph_factory = graph_factory; - initializeData(); - clearData(); - parse(reader); - - return graphs; - } - - /** - * Returns a list of the graphs parsed from the specified file, as created by - * the specified Supplier. - * @param filename the source of the graph data in GraphML format - * @param graph_factory used to build graph instances - * @return the graphs parsed from the specified file - * @throws IOException if an error is encountered while parsing the graph - */ - public List loadMultiple(String filename, Supplier graph_factory) throws IOException - { - return loadMultiple(new FileReader(filename), graph_factory); - } - - /** - * Populates the specified graph with the data parsed from the reader. - * @param reader the source of the graph data in GraphML format - * @param g the graph instance to populate - * @throws IOException if an error is encountered while parsing the graph - */ - public void load(Reader reader, G g) throws IOException - { - this.current_graph = g; - this.graph_factory = null; - initializeData(); - clearData(); - - parse(reader); - } - - /** - * Populates the specified graph with the data parsed from the specified file. - * @param filename the source of the graph data in GraphML format - * @param g the graph instance to populate - * @throws IOException if an error is encountered while parsing the graph - */ - public void load(String filename, G g) throws IOException - { - load(new FileReader(filename), g); - } - - protected void clearData() - { - this.vertex_ids.clear(); - this.vertex_desc.clear(); - - this.edge_ids.clear(); - this.edge_desc.clear(); - - this.graph_desc.clear(); +public class GraphMLReader, V, E> extends DefaultHandler { + protected enum TagState { + NO_TAG, + VERTEX, + EDGE, + HYPEREDGE, + ENDPOINT, + GRAPH, + DATA, + KEY, + DESC, + DEFAULT_KEY, + GRAPHML, + OTHER + } + + protected enum KeyType { + NONE, + VERTEX, + EDGE, + GRAPH, + ALL + } + + protected SAXParser saxp; + protected boolean default_directed; + protected G current_graph; + protected V current_vertex; + protected E current_edge; + protected String current_key; + protected LinkedList current_states; + protected BiMap tag_state; + protected Supplier graph_factory; + protected Supplier vertex_factory; + protected Supplier edge_factory; + protected BiMap vertex_ids; + protected BiMap edge_ids; + protected Map> graph_metadata; + protected Map> vertex_metadata; + protected Map> edge_metadata; + protected Map vertex_desc; + protected Map edge_desc; + protected Map graph_desc; + protected KeyType key_type; + + protected List graphs; + + protected StringBuilder current_text = new StringBuilder(); + + // TODO(jrtom): replace graph supplier with NetworkBuilder, or just provide another overload? + + /** + * Creates a GraphMLReader instance with the specified vertex and edge factories. + * + * @param vertex_factory the vertex supplier to use to create vertex objects + * @param edge_factory the edge supplier to use to create edge objects + * @throws ParserConfigurationException if a SAX parser cannot be constructed + * @throws SAXException if the SAX parser factory cannot be constructed + */ + public GraphMLReader(Supplier vertex_factory, Supplier edge_factory) + throws ParserConfigurationException, SAXException { + current_vertex = null; + current_edge = null; + + SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); + saxp = saxParserFactory.newSAXParser(); + + current_states = new LinkedList(); + + tag_state = HashBiMap.create(); + tag_state.put("node", TagState.VERTEX); + tag_state.put("edge", TagState.EDGE); + tag_state.put("hyperedge", TagState.HYPEREDGE); + tag_state.put("endpoint", TagState.ENDPOINT); + tag_state.put("graph", TagState.GRAPH); + tag_state.put("data", TagState.DATA); + tag_state.put("key", TagState.KEY); + tag_state.put("desc", TagState.DESC); + tag_state.put("default", TagState.DEFAULT_KEY); + tag_state.put("graphml", TagState.GRAPHML); + + this.key_type = KeyType.NONE; + + this.vertex_factory = vertex_factory; + this.edge_factory = edge_factory; + } + + /** + * Creates a GraphMLReader instance that assigns the vertex and edge id + * strings to be the vertex and edge objects, as well as their IDs. Note that this requires that + * (a) each edge have a valid ID, which is not normally a requirement for edges in GraphML, and + * (b) that the vertex and edge types be assignment-compatible with String. + * + * @throws ParserConfigurationException if a SAX parser cannot be constructed + * @throws SAXException if the SAX parser factory cannot be constructed + */ + public GraphMLReader() throws ParserConfigurationException, SAXException { + this(null, null); + } + + /** + * Returns a list of the graphs parsed from the specified reader, as created by the specified + * Supplier. + * + * @param reader the source of the graph data in GraphML format + * @param graph_factory used to build graph instances + * @return the graphs parsed from the specified reader + * @throws IOException if an error is encountered while parsing the graph + */ + public List loadMultiple(Reader reader, Supplier graph_factory) throws IOException { + this.graph_factory = graph_factory; + initializeData(); + clearData(); + parse(reader); + + return graphs; + } + + /** + * Returns a list of the graphs parsed from the specified file, as created by the specified + * Supplier. + * + * @param filename the source of the graph data in GraphML format + * @param graph_factory used to build graph instances + * @return the graphs parsed from the specified file + * @throws IOException if an error is encountered while parsing the graph + */ + public List loadMultiple(String filename, Supplier graph_factory) throws IOException { + return loadMultiple(new FileReader(filename), graph_factory); + } + + /** + * Populates the specified graph with the data parsed from the reader. + * + * @param reader the source of the graph data in GraphML format + * @param g the graph instance to populate + * @throws IOException if an error is encountered while parsing the graph + */ + public void load(Reader reader, G g) throws IOException { + this.current_graph = g; + this.graph_factory = null; + initializeData(); + clearData(); + + parse(reader); + } + + /** + * Populates the specified graph with the data parsed from the specified file. + * + * @param filename the source of the graph data in GraphML format + * @param g the graph instance to populate + * @throws IOException if an error is encountered while parsing the graph + */ + public void load(String filename, G g) throws IOException { + load(new FileReader(filename), g); + } + + protected void clearData() { + this.vertex_ids.clear(); + this.vertex_desc.clear(); + + this.edge_ids.clear(); + this.edge_desc.clear(); + + this.graph_desc.clear(); + } + + /** + * This is separate from initialize() because these data structures are shared among all graphs + * loaded (i.e., they're defined inside graphml rather than graph. + */ + protected void initializeData() { + this.vertex_ids = HashBiMap.create(); + this.vertex_desc = new HashMap(); + this.vertex_metadata = new HashMap>(); + + this.edge_ids = HashBiMap.create(); + this.edge_desc = new HashMap(); + this.edge_metadata = new HashMap>(); + + this.graph_desc = new HashMap(); + this.graph_metadata = new HashMap>(); + } + + protected void parse(Reader reader) throws IOException { + try { + saxp.parse(new InputSource(reader), this); + reader.close(); + } catch (SAXException saxe) { + throw new IOException(saxe.getMessage()); } + } + + @Override + public void startElement(String uri, String name, String qName, Attributes atts) + throws SAXNotSupportedException { + String tag = qName.toLowerCase(); + TagState state = tag_state.get(tag); + if (state == null) state = TagState.OTHER; + + switch (state) { + case GRAPHML: + break; + + case VERTEX: + if (this.current_graph == null) + throw new SAXNotSupportedException("Graph must be defined prior to elements"); + if (this.current_edge != null || this.current_vertex != null) + throw new SAXNotSupportedException("Nesting elements not supported"); + + createVertex(atts); + + break; + + case ENDPOINT: + if (this.current_graph == null) + throw new SAXNotSupportedException("Graph must be defined prior to elements"); + if (this.current_edge == null) + throw new SAXNotSupportedException("No edge defined for endpoint"); + if (this.current_states.getFirst() != TagState.HYPEREDGE) + throw new SAXNotSupportedException("Endpoints must be defined inside hyperedge"); + Map endpoint_atts = getAttributeMap(atts); + String node = endpoint_atts.remove("node"); + if (node == null) + throw new SAXNotSupportedException("Endpoint must include an 'id' attribute"); + V v = vertex_ids.inverse().get(node); + if (v == null) + throw new SAXNotSupportedException("Endpoint refers to nonexistent node ID: " + node); - /** - * This is separate from initialize() because these data structures are shared among all - * graphs loaded (i.e., they're defined inside graphml rather than graph. - */ - protected void initializeData() - { - this.vertex_ids = HashBiMap.create(); - this.vertex_desc = new HashMap(); - this.vertex_metadata = new HashMap>(); - - this.edge_ids = HashBiMap.create(); - this.edge_desc = new HashMap(); - this.edge_metadata = new HashMap>(); - - this.graph_desc = new HashMap(); - this.graph_metadata = new HashMap>(); - } - - protected void parse(Reader reader) throws IOException - { - try - { - saxp.parse(new InputSource(reader), this); - reader.close(); - } - catch (SAXException saxe) - { - throw new IOException(saxe.getMessage()); - } - } - - @Override - public void startElement(String uri, String name, String qName, Attributes atts) throws SAXNotSupportedException - { - String tag = qName.toLowerCase(); - TagState state = tag_state.get(tag); - if (state == null) - state = TagState.OTHER; - - switch (state) - { - case GRAPHML: - break; + this.current_vertex = v; + break; - case VERTEX: - if (this.current_graph == null) - throw new SAXNotSupportedException("Graph must be defined prior to elements"); - if (this.current_edge != null || this.current_vertex != null) - throw new SAXNotSupportedException("Nesting elements not supported"); + case EDGE: + if (this.current_graph == null) + throw new SAXNotSupportedException("Graph must be defined prior to elements"); + if (this.current_edge != null || this.current_vertex != null) + throw new SAXNotSupportedException("Nesting elements not supported"); - createVertex(atts); + createEdge(atts, state); + break; - break; + case HYPEREDGE: + throw new SAXNotSupportedException("Hyperedges not supported"); - case ENDPOINT: - if (this.current_graph == null) - throw new SAXNotSupportedException("Graph must be defined prior to elements"); - if (this.current_edge == null) - throw new SAXNotSupportedException("No edge defined for endpoint"); - if (this.current_states.getFirst() != TagState.HYPEREDGE) - throw new SAXNotSupportedException("Endpoints must be defined inside hyperedge"); - Map endpoint_atts = getAttributeMap(atts); - String node = endpoint_atts.remove("node"); - if (node == null) - throw new SAXNotSupportedException("Endpoint must include an 'id' attribute"); - V v = vertex_ids.inverse().get(node); - if (v == null) - throw new SAXNotSupportedException("Endpoint refers to nonexistent node ID: " + node); - - this.current_vertex = v; - break; - - case EDGE: - if (this.current_graph == null) - throw new SAXNotSupportedException("Graph must be defined prior to elements"); - if (this.current_edge != null || this.current_vertex != null) - throw new SAXNotSupportedException("Nesting elements not supported"); + case GRAPH: + if (this.current_graph != null && graph_factory != null) + throw new SAXNotSupportedException("Nesting graphs not currently supported"); - createEdge(atts, state); - break; + // graph Supplier is null if there's only one graph + if (graph_factory != null) current_graph = graph_factory.get(); - case HYPEREDGE: - throw new SAXNotSupportedException("Hyperedges not supported"); - - case GRAPH: - if (this.current_graph != null && graph_factory != null) - throw new SAXNotSupportedException("Nesting graphs not currently supported"); - - // graph Supplier is null if there's only one graph - if (graph_factory != null) - current_graph = graph_factory.get(); - - // reset all non-key data structures (to avoid collisions between different graphs) - clearData(); - - // set up default direction of edges - Map graph_atts = getAttributeMap(atts); - String default_direction = graph_atts.remove("edgedefault"); - if (default_direction == null) - throw new SAXNotSupportedException("All graphs must specify a default edge direction"); - if (default_direction.equals("directed")) - this.default_directed = true; - else if (default_direction.equals("undirected")) - this.default_directed = false; - else - throw new SAXNotSupportedException("Invalid or unrecognized default edge direction: " + default_direction); - - // put remaining attribute/value pairs in graph_data - addExtraData(graph_atts, graph_metadata, current_graph); + // reset all non-key data structures (to avoid collisions between different graphs) + clearData(); - break; + // set up default direction of edges + Map graph_atts = getAttributeMap(atts); + String default_direction = graph_atts.remove("edgedefault"); + if (default_direction == null) + throw new SAXNotSupportedException("All graphs must specify a default edge direction"); + if (default_direction.equals("directed")) this.default_directed = true; + else if (default_direction.equals("undirected")) this.default_directed = false; + else + throw new SAXNotSupportedException( + "Invalid or unrecognized default edge direction: " + default_direction); - case DATA: - if (this.current_states.contains(TagState.DATA)) - throw new SAXNotSupportedException("Nested data not supported"); - handleData(atts); - break; + // put remaining attribute/value pairs in graph_data + addExtraData(graph_atts, graph_metadata, current_graph); - case KEY: - createKey(atts); - break; + break; + case DATA: + if (this.current_states.contains(TagState.DATA)) + throw new SAXNotSupportedException("Nested data not supported"); + handleData(atts); + break; - default: - break; - } + case KEY: + createKey(atts); + break; - current_states.addFirst(state); + default: + break; } - /** - * - * @param - * @param atts - * @param metadata_map - * @param current_elt - */ - private void addExtraData(Map atts, - Map> metadata_map, T current_elt) - { - // load in the default values; these override anything that might - // be in the attribute map (because that's not really a proper - // way to associate data) - for (Map.Entry> entry: metadata_map.entrySet()) - { - GraphMLMetadata gmlm = entry.getValue(); - if (gmlm.default_value != null) - { - SettableTransformer st = - (SettableTransformer)gmlm.transformer; - st.set(current_elt, gmlm.default_value); - } - } - - // place remaining items in data - for (Map.Entry entry : atts.entrySet()) - { - String key = entry.getKey(); - GraphMLMetadata key_data = metadata_map.get(key); - SettableTransformer st; - if (key_data != null) - { - // if there's a default value, don't override it - if (key_data.default_value != null) - continue; - st = (SettableTransformer)key_data.transformer; - } - else - { - st = new MapSettableTransformer( - new HashMap()); - key_data = new GraphMLMetadata(null, null, st); - metadata_map.put(key, key_data); - } - st.set(current_elt, entry.getValue()); - } - } - - - @Override - public void characters(char[] ch, int start, int length) throws SAXNotSupportedException - { - this.current_text.append(new String(ch, start, length)); + current_states.addFirst(state); + } + + /** + * @param + * @param atts + * @param metadata_map + * @param current_elt + */ + private void addExtraData( + Map atts, Map> metadata_map, T current_elt) { + // load in the default values; these override anything that might + // be in the attribute map (because that's not really a proper + // way to associate data) + for (Map.Entry> entry : metadata_map.entrySet()) { + GraphMLMetadata gmlm = entry.getValue(); + if (gmlm.default_value != null) { + SettableTransformer st = (SettableTransformer) gmlm.transformer; + st.set(current_elt, gmlm.default_value); + } } - - protected void addDatum(Map> metadata, - T current_elt, String text) throws SAXNotSupportedException - { - if (metadata.containsKey(this.current_key)) - { - SettableTransformer st = - (SettableTransformer)(metadata.get(this.current_key).transformer); - st.set(current_elt, text); - } - else - throw new SAXNotSupportedException("key " + this.current_key + - " not valid for element " + current_elt); + // place remaining items in data + for (Map.Entry entry : atts.entrySet()) { + String key = entry.getKey(); + GraphMLMetadata key_data = metadata_map.get(key); + SettableTransformer st; + if (key_data != null) { + // if there's a default value, don't override it + if (key_data.default_value != null) continue; + st = (SettableTransformer) key_data.transformer; + } else { + st = new MapSettableTransformer(new HashMap()); + key_data = new GraphMLMetadata(null, null, st); + metadata_map.put(key, key_data); + } + st.set(current_elt, entry.getValue()); } + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXNotSupportedException { + this.current_text.append(new String(ch, start, length)); + } + + protected void addDatum(Map> metadata, T current_elt, String text) + throws SAXNotSupportedException { + if (metadata.containsKey(this.current_key)) { + SettableTransformer st = + (SettableTransformer) (metadata.get(this.current_key).transformer); + st.set(current_elt, text); + } else + throw new SAXNotSupportedException( + "key " + this.current_key + " not valid for element " + current_elt); + } + + @Override + public void endElement(String uri, String name, String qName) throws SAXNotSupportedException { + String text = current_text.toString().trim(); + current_text.setLength(0); + + String tag = qName.toLowerCase(); + TagState state = tag_state.get(tag); + if (state == null) state = TagState.OTHER; + if (state == TagState.OTHER) return; + + if (state != current_states.getFirst()) + throw new SAXNotSupportedException( + "Unbalanced tags: opened " + + tag_state.inverse().get(current_states.getFirst()) + + ", closed " + + tag); + + switch (state) { + case VERTEX: + case ENDPOINT: + current_vertex = null; + break; - @Override - public void endElement(String uri, String name, String qName) throws SAXNotSupportedException - { - String text = current_text.toString().trim(); - current_text.setLength(0); - - String tag = qName.toLowerCase(); - TagState state = tag_state.get(tag); - if (state == null) - state = TagState.OTHER; - if (state == TagState.OTHER) - return; - - if (state != current_states.getFirst()) - throw new SAXNotSupportedException("Unbalanced tags: opened " + - tag_state.inverse().get(current_states.getFirst()) + - ", closed " + tag); - - switch(state) - { - case VERTEX: - case ENDPOINT: - current_vertex = null; - break; - - case EDGE: - current_edge = null; - break; - - case HYPEREDGE: - throw new SAXNotSupportedException("Hypergraphs not currently supported"); - - case GRAPH: - current_graph = null; - break; - - case KEY: - current_key = null; - break; - - case DESC: - switch (this.current_states.get(1)) // go back one - { - case GRAPH: - graph_desc.put(current_graph, text); - break; - case VERTEX: - case ENDPOINT: - vertex_desc.put(current_vertex, text); - break; - case EDGE: - case HYPEREDGE: - edge_desc.put(current_edge, text); - break; - case DATA: - switch (key_type) - { - case GRAPH: - graph_metadata.get(current_key).description = text; - break; - case VERTEX: - vertex_metadata.get(current_key).description = text; - break; - case EDGE: - edge_metadata.get(current_key).description = text; - break; - case ALL: - graph_metadata.get(current_key).description = text; - vertex_metadata.get(current_key).description = text; - edge_metadata.get(current_key).description = text; - break; - default: - throw new SAXNotSupportedException("Invalid key type" + - " specified for default: " + key_type); - } - - break; - default: - break; - } - break; - case DATA: - this.key_type = KeyType.NONE; - switch (this.current_states.get(1)) - { - case GRAPH: - addDatum(graph_metadata, current_graph, text); - break; - case VERTEX: - case ENDPOINT: - addDatum(vertex_metadata, current_vertex, text); - break; - case EDGE: - case HYPEREDGE: - addDatum(edge_metadata, current_edge, text); - break; - default: - break; - } - break; - case DEFAULT_KEY: - if (this.current_states.get(1) != TagState.KEY) - throw new SAXNotSupportedException("'default' only defined in context of 'key' tag: " + - "stack: " + current_states.toString()); - - switch (key_type) - { - case GRAPH: - graph_metadata.get(current_key).default_value = text; - break; - case VERTEX: - vertex_metadata.get(current_key).default_value = text; - break; - case EDGE: - edge_metadata.get(current_key).default_value = text; - break; - case ALL: - graph_metadata.get(current_key).default_value = text; - vertex_metadata.get(current_key).default_value = text; - edge_metadata.get(current_key).default_value = text; - break; - default: - throw new SAXNotSupportedException("Invalid key type" + - " specified for default: " + key_type); - } + case EDGE: + current_edge = null; + break; - break; - default: - break; - } + case HYPEREDGE: + throw new SAXNotSupportedException("Hypergraphs not currently supported"); - current_states.removeFirst(); - } + case GRAPH: + current_graph = null; + break; - protected Map getAttributeMap(Attributes atts) - { - Map att_map = new HashMap(); - for (int i = 0; i < atts.getLength(); i++) - att_map.put(atts.getQName(i), atts.getValue(i)); + case KEY: + current_key = null; + break; - return att_map; - } - - protected void handleData(Attributes atts) throws SAXNotSupportedException - { - switch (this.current_states.getFirst()) + case DESC: + switch (this.current_states.get(1)) // go back one { - case GRAPH: + case GRAPH: + graph_desc.put(current_graph, text); + break; + case VERTEX: + case ENDPOINT: + vertex_desc.put(current_vertex, text); + break; + case EDGE: + case HYPEREDGE: + edge_desc.put(current_edge, text); + break; + case DATA: + switch (key_type) { + case GRAPH: + graph_metadata.get(current_key).description = text; break; - case VERTEX: - case ENDPOINT: + case VERTEX: + vertex_metadata.get(current_key).description = text; break; - case EDGE: + case EDGE: + edge_metadata.get(current_key).description = text; break; - case HYPEREDGE: + case ALL: + graph_metadata.get(current_key).description = text; + vertex_metadata.get(current_key).description = text; + edge_metadata.get(current_key).description = text; break; - default: - throw new SAXNotSupportedException("'data' tag only defined " + - "if immediately containing tag is 'graph', 'node', " + - "'edge', or 'hyperedge'"); - } - this.current_key = getAttributeMap(atts).get("key"); - if (this.current_key == null) - throw new SAXNotSupportedException("'data' tag requires a key specification"); - if (this.current_key.equals("")) - throw new SAXNotSupportedException("'data' tag requires a non-empty key"); - if (!getGraphMetadata().containsKey(this.current_key) && - !getVertexMetadata().containsKey(this.current_key) && - !getEdgeMetadata().containsKey(this.current_key)) - { - throw new SAXNotSupportedException("'data' tag's key specification must reference a defined key"); - } - - } - - protected void createKey(Attributes atts) throws SAXNotSupportedException - { - Map key_atts = getAttributeMap(atts); - String id = key_atts.remove("id"); - String for_type = key_atts.remove("for"); - - if (for_type == null || for_type.equals("") || for_type.equals("all")) - { - vertex_metadata.put(id, - new GraphMLMetadata(null, null, - new MapSettableTransformer(new HashMap()))); - edge_metadata.put(id, - new GraphMLMetadata(null, null, - new MapSettableTransformer(new HashMap()))); - graph_metadata.put(id, - new GraphMLMetadata(null, null, - new MapSettableTransformer(new HashMap()))); - key_type = KeyType.ALL; - } - else - { - TagState type = tag_state.get(for_type); - switch (type) - { - case VERTEX: - vertex_metadata.put(id, - new GraphMLMetadata(null, null, - new MapSettableTransformer(new HashMap()))); - key_type = KeyType.VERTEX; - break; - case EDGE: - case HYPEREDGE: - edge_metadata.put(id, - new GraphMLMetadata(null, null, - new MapSettableTransformer(new HashMap()))); - key_type = KeyType.EDGE; - break; - case GRAPH: - graph_metadata.put(id, - new GraphMLMetadata(null, null, - new MapSettableTransformer(new HashMap()))); - key_type = KeyType.GRAPH; - break; - default: - throw new SAXNotSupportedException( - "Invalid metadata target type: " + for_type); + default: + throw new SAXNotSupportedException( + "Invalid key type" + " specified for default: " + key_type); } - } - - this.current_key = id; - - } - @SuppressWarnings("unchecked") - protected void createVertex(Attributes atts) throws SAXNotSupportedException - { - Map vertex_atts = getAttributeMap(atts); - String id = vertex_atts.remove("id"); - if (id == null) - throw new SAXNotSupportedException("node attribute list missing " + - "'id': " + atts.toString()); - V v = vertex_ids.inverse().get(id); - - if (v == null) - { - if (vertex_factory != null) - v = vertex_factory.get(); - else - v = (V)id; - vertex_ids.put(v, id); - this.current_graph.addNode(v); - - // put remaining attribute/value pairs in vertex_data - addExtraData(vertex_atts, vertex_metadata, v); + break; + default: + break; } - else - throw new SAXNotSupportedException("Node id \"" + id + - " is a duplicate of an existing node ID"); - - this.current_vertex = v; - } - - - @SuppressWarnings("unchecked") - protected void createEdge(Attributes atts, TagState state) - throws SAXNotSupportedException - { - Map edge_atts = getAttributeMap(atts); - - String id = edge_atts.remove("id"); - E e; - if (edge_factory != null) - e = edge_factory.get(); - else - if (id != null) - e = (E)id; - else - throw new IllegalArgumentException("If no edge Supplier is supplied, " + - "edge id may not be null: " + edge_atts); - - if (id != null) - { - if (edge_ids.containsKey(e)) - throw new SAXNotSupportedException("Edge id \"" + id + - "\" is a duplicate of an existing edge ID"); - edge_ids.put(e, id); + break; + case DATA: + this.key_type = KeyType.NONE; + switch (this.current_states.get(1)) { + case GRAPH: + addDatum(graph_metadata, current_graph, text); + break; + case VERTEX: + case ENDPOINT: + addDatum(vertex_metadata, current_vertex, text); + break; + case EDGE: + case HYPEREDGE: + addDatum(edge_metadata, current_edge, text); + break; + default: + break; } - - if (state == TagState.EDGE) - assignEdgeSourceTarget(e, atts, edge_atts); //, id); - - // put remaining attribute/value pairs in edge_data - addExtraData(edge_atts, edge_metadata, e); - - this.current_edge = e; - } - - protected void assignEdgeSourceTarget(E e, Attributes atts, - Map edge_atts)//, String id) - throws SAXNotSupportedException - { - String source_id = edge_atts.remove("source"); - if (source_id == null) - throw new SAXNotSupportedException("edge attribute list missing " + - "'source': " + atts.toString()); - V source = vertex_ids.inverse().get(source_id); - if (source == null) - throw new SAXNotSupportedException("specified 'source' attribute " + - "\"" + source_id + "\" does not match any node ID"); - - String target_id = edge_atts.remove("target"); - if (target_id == null) - throw new SAXNotSupportedException("edge attribute list missing " + - "'target': " + atts.toString()); - V target = vertex_ids.inverse().get(target_id); - if (target == null) - throw new SAXNotSupportedException("specified 'target' attribute " + - "\"" + target_id + "\" does not match any node ID"); - - String directed = edge_atts.remove("directed"); - if (directed != null) { - boolean isDirected = directed.equals("true"); - boolean isUndirected = directed.equals("false"); - if (!isDirected && !isUndirected) { - throw new SAXNotSupportedException("Unrecognized edge direction specifier 'direction=\"" + - directed + "\"': " + "source: " + source_id + ", target: " + target_id); - } - if (isDirected != default_directed) { - throw new SAXNotSupportedException(String.format("Parser does not support graphs with directed and " - + "undirected edges; default direction: %b, edge direction: %b: ", - default_directed, (isDirected ? isDirected : isUndirected))); - } + break; + case DEFAULT_KEY: + if (this.current_states.get(1) != TagState.KEY) + throw new SAXNotSupportedException( + "'default' only defined in context of 'key' tag: " + + "stack: " + + current_states.toString()); + + switch (key_type) { + case GRAPH: + graph_metadata.get(current_key).default_value = text; + break; + case VERTEX: + vertex_metadata.get(current_key).default_value = text; + break; + case EDGE: + edge_metadata.get(current_key).default_value = text; + break; + case ALL: + graph_metadata.get(current_key).default_value = text; + vertex_metadata.get(current_key).default_value = text; + edge_metadata.get(current_key).default_value = text; + break; + default: + throw new SAXNotSupportedException( + "Invalid key type" + " specified for default: " + key_type); } - current_graph.addEdge(source, target, e); - } - - /** - * @return a bidirectional map relating vertices and IDs. - */ - public BiMap getVertexIDs() - { - return vertex_ids; - } - /** - * Returns a bidirectional map relating edges and IDs. - * This is not guaranteed to always be populated (edge IDs are not - * required in GraphML files. - * @return a bidirectional map relating edges and IDs. - */ - public BiMap getEdgeIDs() - { - return edge_ids; + break; + default: + break; } - /** - * @return a map from graph type name to type metadata - */ - public Map> getGraphMetadata() - { - return graph_metadata; + current_states.removeFirst(); + } + + protected Map getAttributeMap(Attributes atts) { + Map att_map = new HashMap(); + for (int i = 0; i < atts.getLength(); i++) att_map.put(atts.getQName(i), atts.getValue(i)); + + return att_map; + } + + protected void handleData(Attributes atts) throws SAXNotSupportedException { + switch (this.current_states.getFirst()) { + case GRAPH: + break; + case VERTEX: + case ENDPOINT: + break; + case EDGE: + break; + case HYPEREDGE: + break; + default: + throw new SAXNotSupportedException( + "'data' tag only defined " + + "if immediately containing tag is 'graph', 'node', " + + "'edge', or 'hyperedge'"); } - - /** - * @return a map from vertex type name to type metadata - */ - public Map> getVertexMetadata() - { - return vertex_metadata; - } - - /** - * @return a map from edge type name to type metadata - */ - public Map> getEdgeMetadata() - { - return edge_metadata; + this.current_key = getAttributeMap(atts).get("key"); + if (this.current_key == null) + throw new SAXNotSupportedException("'data' tag requires a key specification"); + if (this.current_key.equals("")) + throw new SAXNotSupportedException("'data' tag requires a non-empty key"); + if (!getGraphMetadata().containsKey(this.current_key) + && !getVertexMetadata().containsKey(this.current_key) + && !getEdgeMetadata().containsKey(this.current_key)) { + throw new SAXNotSupportedException( + "'data' tag's key specification must reference a defined key"); } - - /** - * @return a map from graphs to graph descriptions - */ - public Map getGraphDescriptions() - { - return graph_desc; + } + + protected void createKey(Attributes atts) throws SAXNotSupportedException { + Map key_atts = getAttributeMap(atts); + String id = key_atts.remove("id"); + String for_type = key_atts.remove("for"); + + if (for_type == null || for_type.equals("") || for_type.equals("all")) { + vertex_metadata.put( + id, + new GraphMLMetadata( + null, null, new MapSettableTransformer(new HashMap()))); + edge_metadata.put( + id, + new GraphMLMetadata( + null, null, new MapSettableTransformer(new HashMap()))); + graph_metadata.put( + id, + new GraphMLMetadata( + null, null, new MapSettableTransformer(new HashMap()))); + key_type = KeyType.ALL; + } else { + TagState type = tag_state.get(for_type); + switch (type) { + case VERTEX: + vertex_metadata.put( + id, + new GraphMLMetadata( + null, null, new MapSettableTransformer(new HashMap()))); + key_type = KeyType.VERTEX; + break; + case EDGE: + case HYPEREDGE: + edge_metadata.put( + id, + new GraphMLMetadata( + null, null, new MapSettableTransformer(new HashMap()))); + key_type = KeyType.EDGE; + break; + case GRAPH: + graph_metadata.put( + id, + new GraphMLMetadata( + null, null, new MapSettableTransformer(new HashMap()))); + key_type = KeyType.GRAPH; + break; + default: + throw new SAXNotSupportedException("Invalid metadata target type: " + for_type); + } } - /** - * @return a map from vertices to vertex descriptions - */ - public Map getVertexDescriptions() - { - return vertex_desc; + this.current_key = id; + } + + @SuppressWarnings("unchecked") + protected void createVertex(Attributes atts) throws SAXNotSupportedException { + Map vertex_atts = getAttributeMap(atts); + String id = vertex_atts.remove("id"); + if (id == null) + throw new SAXNotSupportedException( + "node attribute list missing " + "'id': " + atts.toString()); + V v = vertex_ids.inverse().get(id); + + if (v == null) { + if (vertex_factory != null) v = vertex_factory.get(); + else v = (V) id; + vertex_ids.put(v, id); + this.current_graph.addNode(v); + + // put remaining attribute/value pairs in vertex_data + addExtraData(vertex_atts, vertex_metadata, v); + } else + throw new SAXNotSupportedException( + "Node id \"" + id + " is a duplicate of an existing node ID"); + + this.current_vertex = v; + } + + @SuppressWarnings("unchecked") + protected void createEdge(Attributes atts, TagState state) throws SAXNotSupportedException { + Map edge_atts = getAttributeMap(atts); + + String id = edge_atts.remove("id"); + E e; + if (edge_factory != null) e = edge_factory.get(); + else if (id != null) e = (E) id; + else + throw new IllegalArgumentException( + "If no edge Supplier is supplied, " + "edge id may not be null: " + edge_atts); + + if (id != null) { + if (edge_ids.containsKey(e)) + throw new SAXNotSupportedException( + "Edge id \"" + id + "\" is a duplicate of an existing edge ID"); + edge_ids.put(e, id); } - /** - * @return a map from edges to edge descriptions - */ - public Map getEdgeDescriptions() - { - return edge_desc; + if (state == TagState.EDGE) assignEdgeSourceTarget(e, atts, edge_atts); //, id); + + // put remaining attribute/value pairs in edge_data + addExtraData(edge_atts, edge_metadata, e); + + this.current_edge = e; + } + + protected void assignEdgeSourceTarget( + E e, Attributes atts, Map edge_atts) //, String id) + throws SAXNotSupportedException { + String source_id = edge_atts.remove("source"); + if (source_id == null) + throw new SAXNotSupportedException( + "edge attribute list missing " + "'source': " + atts.toString()); + V source = vertex_ids.inverse().get(source_id); + if (source == null) + throw new SAXNotSupportedException( + "specified 'source' attribute " + "\"" + source_id + "\" does not match any node ID"); + + String target_id = edge_atts.remove("target"); + if (target_id == null) + throw new SAXNotSupportedException( + "edge attribute list missing " + "'target': " + atts.toString()); + V target = vertex_ids.inverse().get(target_id); + if (target == null) + throw new SAXNotSupportedException( + "specified 'target' attribute " + "\"" + target_id + "\" does not match any node ID"); + + String directed = edge_atts.remove("directed"); + if (directed != null) { + boolean isDirected = directed.equals("true"); + boolean isUndirected = directed.equals("false"); + if (!isDirected && !isUndirected) { + throw new SAXNotSupportedException( + "Unrecognized edge direction specifier 'direction=\"" + + directed + + "\"': " + + "source: " + + source_id + + ", target: " + + target_id); + } + if (isDirected != default_directed) { + throw new SAXNotSupportedException( + String.format( + "Parser does not support graphs with directed and " + + "undirected edges; default direction: %b, edge direction: %b: ", + default_directed, (isDirected ? isDirected : isUndirected))); + } } + current_graph.addEdge(source, target, e); + } + + /** @return a bidirectional map relating vertices and IDs. */ + public BiMap getVertexIDs() { + return vertex_ids; + } + + /** + * Returns a bidirectional map relating edges and IDs. This is not guaranteed to always be + * populated (edge IDs are not required in GraphML files. + * + * @return a bidirectional map relating edges and IDs. + */ + public BiMap getEdgeIDs() { + return edge_ids; + } + + /** @return a map from graph type name to type metadata */ + public Map> getGraphMetadata() { + return graph_metadata; + } + + /** @return a map from vertex type name to type metadata */ + public Map> getVertexMetadata() { + return vertex_metadata; + } + + /** @return a map from edge type name to type metadata */ + public Map> getEdgeMetadata() { + return edge_metadata; + } + + /** @return a map from graphs to graph descriptions */ + public Map getGraphDescriptions() { + return graph_desc; + } + + /** @return a map from vertices to vertex descriptions */ + public Map getVertexDescriptions() { + return vertex_desc; + } + + /** @return a map from edges to edge descriptions */ + public Map getEdgeDescriptions() { + return edge_desc; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/GraphMLWriter.java b/jung-io/src/main/java/edu/uci/ics/jung/io/GraphMLWriter.java index c2b02e97..15194c99 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/GraphMLWriter.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/GraphMLWriter.java @@ -1,7 +1,7 @@ /* * Created on June 16, 2008 * - * Copyright (c) 2008, The JUNG Authors + * Copyright (c) 2008, The JUNG Authors * * All rights reserved. * @@ -11,6 +11,10 @@ */ package edu.uci.ics.jung.io; +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.graph.EndpointPair; +import com.google.common.graph.Network; import java.io.BufferedWriter; import java.io.IOException; import java.io.Writer; @@ -18,379 +22,332 @@ import java.util.HashMap; import java.util.Map; -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.graph.EndpointPair; -import com.google.common.graph.Network; - /** * Writes graphs out in GraphML format. * - * Current known issues: + *

    Current known issues: + * *

      - *
    • Only supports one graph per output file. - *
    • Does not indent lines for text-format readability. + *
    • Only supports one graph per output file. + *
    • Does not indent lines for text-format readability. *
    - * */ -public class GraphMLWriter -{ - protected Function vertex_ids; - protected Function edge_ids; - protected Map>> graph_data; - protected Map> vertex_data; - protected Map> edge_data; - protected Function vertex_desc; - protected Function edge_desc; - protected Function, String> graph_desc; - protected boolean directed; - protected int nest_level; - - public GraphMLWriter() - { - vertex_ids = new Function() - { - public String apply(V v) - { - return v.toString(); - } - }; - edge_ids = Functions.constant(null); - graph_data = Collections.emptyMap(); - vertex_data = Collections.emptyMap(); - edge_data = Collections.emptyMap(); - vertex_desc = Functions.constant(null); - edge_desc = Functions.constant(null); - graph_desc = Functions.constant(null); - nest_level = 0; - } - - - /** - * Writes {@code graph} out using {@code w}. - * @param g the graph to write out - * @param w the writer instance to which the graph data will be written out - * @throws IOException if writing the graph fails - */ - public void save(Network g, Writer w) throws IOException - { - BufferedWriter bw = new BufferedWriter(w); +public class GraphMLWriter { + protected Function vertex_ids; + protected Function edge_ids; + protected Map>> graph_data; + protected Map> vertex_data; + protected Map> edge_data; + protected Function vertex_desc; + protected Function edge_desc; + protected Function, String> graph_desc; + protected boolean directed; + protected int nest_level; + + public GraphMLWriter() { + vertex_ids = + new Function() { + public String apply(V v) { + return v.toString(); + } + }; + edge_ids = Functions.constant(null); + graph_data = Collections.emptyMap(); + vertex_data = Collections.emptyMap(); + edge_data = Collections.emptyMap(); + vertex_desc = Functions.constant(null); + edge_desc = Functions.constant(null); + graph_desc = Functions.constant(null); + nest_level = 0; + } + + /** + * Writes {@code graph} out using {@code w}. + * + * @param g the graph to write out + * @param w the writer instance to which the graph data will be written out + * @throws IOException if writing the graph fails + */ + public void save(Network g, Writer w) throws IOException { + BufferedWriter bw = new BufferedWriter(w); + + // write out boilerplate header + bw.write("\n"); + bw.write( + "\n"); + + // write out data specifiers, including defaults + for (String key : graph_data.keySet()) + writeKeySpecification(key, "graph", graph_data.get(key), bw); + for (String key : vertex_data.keySet()) + writeKeySpecification(key, "node", vertex_data.get(key), bw); + for (String key : edge_data.keySet()) + writeKeySpecification(key, "edge", edge_data.get(key), bw); + + // write out graph-level information + // set edge default direction + bw.write("\n"); + } else { + bw.write("undirected\">\n"); + } + + // write graph description, if any + String desc = graph_desc.apply(g); + if (desc != null) bw.write("" + desc + "\n"); + + // write graph data out if any + for (String key : graph_data.keySet()) { + Function, ?> t = graph_data.get(key).transformer; + Object value = t.apply(g); + if (value != null) bw.write(format("data", "key", key, value.toString()) + "\n"); + } + + // write vertex information + writeVertexData(g, bw); + + // write edge information + writeEdgeData(g, bw); + + // close graph + bw.write("\n"); + bw.write("\n"); + bw.flush(); + + bw.close(); + } + + protected void writeIndentedText(BufferedWriter w, String to_write) throws IOException { + for (int i = 0; i < nest_level; i++) w.write(" "); + w.write(to_write); + } + + protected void writeVertexData(Network graph, BufferedWriter w) throws IOException { + for (V v : graph.nodes()) { + String v_string = String.format("\n"); + closed = true; + w.write("" + desc + "\n"); + } + // write data out if any + for (String key : vertex_data.keySet()) { + Function t = vertex_data.get(key).transformer; + if (t != null) { + Object value = t.apply(v); + if (value != null) { + if (!closed) { + w.write(v_string + ">\n"); + closed = true; + } + w.write(format("data", "key", key, value.toString()) + "\n"); + } + } + } + if (!closed) w.write(v_string + "/>\n"); // no contents; close the node with "/>" + else w.write("\n"); + } + } + + protected void writeEdgeData(Network g, Writer w) throws IOException { + for (E e : g.edges()) { + EndpointPair endpoints = g.incidentNodes(e); + String id = edge_ids.apply(e); + String e_string; + e_string = "\n"); + closed = true; + w.write("" + desc + "\n"); + } + // write data out if any + for (String key : edge_data.keySet()) { + Function t = edge_data.get(key).transformer; + Object value = t.apply(e); + if (value != null) { + if (!closed) { + w.write(e_string + ">\n"); + closed = true; + } + w.write(format("data", "key", key, value.toString()) + "\n"); + } + } - // write out boilerplate header - bw.write("\n"); - bw.write("\n"); - - // write out data specifiers, including defaults - for (String key : graph_data.keySet()) - writeKeySpecification(key, "graph", graph_data.get(key), bw); - for (String key : vertex_data.keySet()) - writeKeySpecification(key, "node", vertex_data.get(key), bw); - for (String key : edge_data.keySet()) - writeKeySpecification(key, "edge", edge_data.get(key), bw); + if (!closed) w.write(e_string + "/>\n"); // no contents; close the edge with "/>" + else w.write("\n"); + } + } - // write out graph-level information - // set edge default direction - bw.write("\n"); - } - else { - bw.write("undirected\">\n"); - } + protected void writeKeySpecification( + String key, String type, GraphMLMetadata ds, BufferedWriter bw) throws IOException { + bw.write("\n"); + closed = true; + } + bw.write("" + desc + "\n"); + } + // write out default if any + Object def = ds.default_value; + if (def != null) { + if (!closed) { + bw.write(">\n"); + closed = true; + } + bw.write("" + def.toString() + "\n"); + } + if (!closed) bw.write("/>\n"); + else bw.write("\n"); + } - // write graph description, if any - String desc = graph_desc.apply(g); - if (desc != null) - bw.write("" + desc + "\n"); - - // write graph data out if any - for (String key : graph_data.keySet()) - { - Function, ?> t = graph_data.get(key).transformer; - Object value = t.apply(g); - if (value != null) - bw.write(format("data", "key", key, value.toString()) + "\n"); - } - - // write vertex information - writeVertexData(g, bw); - - // write edge information - writeEdgeData(g, bw); + protected String format(String type, String attr, String value, String contents) { + return String.format("<%s %s=\"%s\">%s", type, attr, value, contents, type); + } - // close graph - bw.write("\n"); - bw.write("\n"); - bw.flush(); - - bw.close(); - } + /** + * Provides an ID that will be used to identify a vertex in the output file. If the vertex IDs are + * not set, the ID for each vertex will default to the output of toString (and thus + * not guaranteed to be unique). + * + * @param vertex_ids a mapping from vertex to ID + */ + public void setVertexIDs(Function vertex_ids) { + this.vertex_ids = vertex_ids; + } - protected void writeIndentedText(BufferedWriter w, String to_write) throws IOException - { - for (int i = 0; i < nest_level; i++) - w.write(" "); - w.write(to_write); - } - - protected void writeVertexData(Network graph, BufferedWriter w) throws IOException - { - for (V v: graph.nodes()) - { - String v_string = String.format("\n"); - closed = true; - w.write("" + desc + "\n"); - } - // write data out if any - for (String key : vertex_data.keySet()) - { - Function t = vertex_data.get(key).transformer; - if (t != null) - { - Object value = t.apply(v); - if (value != null) - { - if (!closed) - { - w.write(v_string + ">\n"); - closed = true; - } - w.write(format("data", "key", key, value.toString()) + "\n"); - } - } - } - if (!closed) - w.write(v_string + "/>\n"); // no contents; close the node with "/>" - else - w.write("\n"); - } - } + /** + * Provides an ID that will be used to identify an edge in the output file. If any edge ID is + * missing, no ID will be written out for the corresponding edge. + * + * @param edge_ids a mapping from edge to ID + */ + public void setEdgeIDs(Function edge_ids) { + this.edge_ids = edge_ids; + } - protected void writeEdgeData(Network g, Writer w) throws IOException - { - for (E e: g.edges()) - { - EndpointPair endpoints = g.incidentNodes(e); - String id = edge_ids.apply(e); - String e_string; - e_string = "\n"); - closed = true; - w.write("" + desc + "\n"); - } - // write data out if any - for (String key : edge_data.keySet()) - { - Function t = edge_data.get(key).transformer; - Object value = t.apply(e); - if (value != null) - { - if (!closed) - { - w.write(e_string + ">\n"); - closed = true; - } - w.write(format("data", "key", key, value.toString()) + "\n"); - } - } - - if (!closed) - w.write(e_string + "/>\n"); // no contents; close the edge with "/>" - else - w.write("\n"); - } - } + /** + * Provides a map from data type name to graph data. + * + * @param graph_map map from data type name to graph data + */ + public void setGraphData(Map>> graph_map) { + graph_data = graph_map; + } - protected void writeKeySpecification(String key, String type, - GraphMLMetadata ds, BufferedWriter bw) throws IOException - { - bw.write("\n"); - closed = true; - } - bw.write("" + desc + "\n"); - } - // write out default if any - Object def = ds.default_value; - if (def != null) - { - if (!closed) - { - bw.write(">\n"); - closed = true; - } - bw.write("" + def.toString() + "\n"); - } - if (!closed) - bw.write("/>\n"); - else - bw.write("\n"); - } - - protected String format(String type, String attr, String value, String contents) - { - return String.format("<%s %s=\"%s\">%s", - type, attr, value, contents, type); - } - - /** - * Provides an ID that will be used to identify a vertex in the output file. - * If the vertex IDs are not set, the ID for each vertex will default to - * the output of toString - * (and thus not guaranteed to be unique). - * - * @param vertex_ids a mapping from vertex to ID - */ - public void setVertexIDs(Function vertex_ids) - { - this.vertex_ids = vertex_ids; - } + /** + * Provides a map from data type name to vertex data. + * + * @param vertex_map map from data type name to vertex data + */ + public void setVertexData(Map> vertex_map) { + vertex_data = vertex_map; + } + /** + * Provides a map from data type name to edge data. + * + * @param edge_map map from data type name to edge data + */ + public void setEdgeData(Map> edge_map) { + edge_data = edge_map; + } - /** - * Provides an ID that will be used to identify an edge in the output file. - * If any edge ID is missing, no ID will be written out for the - * corresponding edge. - * - * @param edge_ids a mapping from edge to ID - */ - public void setEdgeIDs(Function edge_ids) - { - this.edge_ids = edge_ids; - } + /** + * Adds a new graph data specification. + * + * @param id the ID of the data to add + * @param description a description of the data to add + * @param default_value a default value for the data type + * @param graph_transformer a mapping from graphs to their string representations + */ + public void addGraphData( + String id, + String description, + String default_value, + Function, String> graph_transformer) { + if (graph_data.equals(Collections.EMPTY_MAP)) + graph_data = new HashMap>>(); + graph_data.put( + id, new GraphMLMetadata>(description, default_value, graph_transformer)); + } - /** - * Provides a map from data type name to graph data. - * - * @param graph_map map from data type name to graph data - */ - public void setGraphData(Map>> graph_map) - { - graph_data = graph_map; - } - - /** - * Provides a map from data type name to vertex data. - * - * @param vertex_map map from data type name to vertex data - */ - public void setVertexData(Map> vertex_map) - { - vertex_data = vertex_map; - } - - /** - * Provides a map from data type name to edge data. - * - * @param edge_map map from data type name to edge data - */ - public void setEdgeData(Map> edge_map) - { - edge_data = edge_map; - } - - /** - * Adds a new graph data specification. - * - * @param id the ID of the data to add - * @param description a description of the data to add - * @param default_value a default value for the data type - * @param graph_transformer a mapping from graphs to their string representations - */ - public void addGraphData(String id, String description, String default_value, - Function, String> graph_transformer) - { - if (graph_data.equals(Collections.EMPTY_MAP)) - graph_data = new HashMap>>(); - graph_data.put(id, new GraphMLMetadata>(description, - default_value, graph_transformer)); - } - - /** - * Adds a new vertex data specification. - * - * @param id the ID of the data to add - * @param description a description of the data to add - * @param default_value a default value for the data type - * @param vertex_transformer a mapping from vertices to their string representations - */ - public void addVertexData(String id, String description, String default_value, - Function vertex_transformer) - { - if (vertex_data.equals(Collections.EMPTY_MAP)) - vertex_data = new HashMap>(); - vertex_data.put(id, new GraphMLMetadata(description, default_value, - vertex_transformer)); - } + /** + * Adds a new vertex data specification. + * + * @param id the ID of the data to add + * @param description a description of the data to add + * @param default_value a default value for the data type + * @param vertex_transformer a mapping from vertices to their string representations + */ + public void addVertexData( + String id, String description, String default_value, Function vertex_transformer) { + if (vertex_data.equals(Collections.EMPTY_MAP)) + vertex_data = new HashMap>(); + vertex_data.put(id, new GraphMLMetadata(description, default_value, vertex_transformer)); + } - /** - * Adds a new edge data specification. - * - * @param id the ID of the data to add - * @param description a description of the data to add - * @param default_value a default value for the data type - * @param edge_transformer a mapping from edges to their string representations - */ - public void addEdgeData(String id, String description, String default_value, - Function edge_transformer) - { - if (edge_data.equals(Collections.EMPTY_MAP)) - edge_data = new HashMap>(); - edge_data.put(id, new GraphMLMetadata(description, default_value, - edge_transformer)); - } + /** + * Adds a new edge data specification. + * + * @param id the ID of the data to add + * @param description a description of the data to add + * @param default_value a default value for the data type + * @param edge_transformer a mapping from edges to their string representations + */ + public void addEdgeData( + String id, String description, String default_value, Function edge_transformer) { + if (edge_data.equals(Collections.EMPTY_MAP)) + edge_data = new HashMap>(); + edge_data.put(id, new GraphMLMetadata(description, default_value, edge_transformer)); + } - /** - * Provides vertex descriptions. - * @param vertex_desc a mapping from vertices to their descriptions - */ - public void setVertexDescriptions(Function vertex_desc) - { - this.vertex_desc = vertex_desc; - } + /** + * Provides vertex descriptions. + * + * @param vertex_desc a mapping from vertices to their descriptions + */ + public void setVertexDescriptions(Function vertex_desc) { + this.vertex_desc = vertex_desc; + } - /** - * Provides edge descriptions. - * @param edge_desc a mapping from edges to their descriptions - */ - public void setEdgeDescriptions(Function edge_desc) - { - this.edge_desc = edge_desc; - } + /** + * Provides edge descriptions. + * + * @param edge_desc a mapping from edges to their descriptions + */ + public void setEdgeDescriptions(Function edge_desc) { + this.edge_desc = edge_desc; + } - /** - * Provides graph descriptions. - * @param graph_desc a mapping from graphs to their descriptions - */ - public void setGraphDescriptions(Function, String> graph_desc) - { - this.graph_desc = graph_desc; - } + /** + * Provides graph descriptions. + * + * @param graph_desc a mapping from graphs to their descriptions + */ + public void setGraphDescriptions(Function, String> graph_desc) { + this.graph_desc = graph_desc; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/GraphReader.java b/jung-io/src/main/java/edu/uci/ics/jung/io/GraphReader.java index 01c61924..a5abd64c 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/GraphReader.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/GraphReader.java @@ -16,30 +16,24 @@ * Interface for a reader of graph objects * * @author Nathan Mittler - nathan.mittler@gmail.com - * - * @param - * the graph type - * @param the vertex type - * the vertex type - * @param the edge type - * the edge type + * @param the graph type + * @param the vertex type the vertex type + * @param the edge type the edge type */ public interface GraphReader, V, E> { - /** - * Reads a single graph object, if one is available. - * - * @return the next graph object, or null if none exists. - * @throws GraphIOException - * thrown if an error occurred. - */ - G readGraph() throws GraphIOException; + /** + * Reads a single graph object, if one is available. + * + * @return the next graph object, or null if none exists. + * @throws GraphIOException thrown if an error occurred. + */ + G readGraph() throws GraphIOException; - /** - * Closes this resource and frees any resources. - * - * @throws GraphIOException - * thrown if an error occurred. - */ - void close() throws GraphIOException; + /** + * Closes this resource and frees any resources. + * + * @throws GraphIOException thrown if an error occurred. + */ + void close() throws GraphIOException; } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/PajekNetReader.java b/jung-io/src/main/java/edu/uci/ics/jung/io/PajekNetReader.java index 6a9dec8e..0fce7272 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/PajekNetReader.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/PajekNetReader.java @@ -1,7 +1,7 @@ /* * Created on May 3, 2004 * - * Copyright (c) 2004, The JUNG Authors + * Copyright (c) 2004, The JUNG Authors * * All rights reserved. * @@ -11,6 +11,13 @@ */ package edu.uci.ics.jung.io; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.base.Supplier; +import com.google.common.graph.MutableNetwork; +import edu.uci.ics.jung.algorithms.util.MapSettableTransformer; +import edu.uci.ics.jung.algorithms.util.SettableTransformer; import java.awt.geom.Point2D; import java.io.BufferedReader; import java.io.FileReader; @@ -21,508 +28,448 @@ import java.util.List; import java.util.StringTokenizer; -import com.google.common.base.Preconditions; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.base.Supplier; -import com.google.common.graph.MutableNetwork; - -import edu.uci.ics.jung.algorithms.util.MapSettableTransformer; -import edu.uci.ics.jung.algorithms.util.SettableTransformer; - - /** * Reads a Graph from a Pajek NET formatted source. - * - *

    If the edge constraints specify that the graph is strictly undirected, - * and an "*Arcs" section is encountered, or if the edge constraints specify that the - * graph is strictly directed, and an "*Edges" section is encountered, - * an IllegalArgumentException is thrown. - * - *

    If the edge constraints do not permit parallel edges, only the first encountered - * of a set of parallel edges will be read; subsequent edges in that set will be ignored. - * - *

    More restrictive edge constraints will cause vertices to be generated - * that are more time- and space-efficient. - * - * At the moment, only supports the - * part of the specification that defines: + * + *

    If the edge constraints specify that the graph is strictly undirected, and an "*Arcs" section + * is encountered, or if the edge constraints specify that the graph is strictly directed, and an + * "*Edges" section is encountered, an IllegalArgumentException is thrown. + * + *

    If the edge constraints do not permit parallel edges, only the first encountered of a set of + * parallel edges will be read; subsequent edges in that set will be ignored. + * + *

    More restrictive edge constraints will cause vertices to be generated that are more time- and + * space-efficient. + * + *

    At the moment, only supports the part of the specification that defines: + * *

      - *
    • vertex ids (each must have a value from 1 to n, where n is the number of vertices) - *
    • vertex labels (must be in quotes if interrupted by whitespace) - *
    • directed edge connections (single or list) - *
    • undirected edge connections (single or list) - *
    • edge weights (not compatible with edges specified in list form) - *
      note: this version of PajekNetReader does not support multiple edge - * weights, as PajekNetFile does; this behavior is consistent with the NET format. - *
    • vertex locations (x and y; z coordinate is ignored) - *

    + *

  • vertex ids (each must have a value from 1 to n, where n is the number of vertices) + *
  • vertex labels (must be in quotes if interrupted by whitespace) + *
  • directed edge connections (single or list) + *
  • undirected edge connections (single or list) + *
  • edge weights (not compatible with edges specified in list form)
    + * note: this version of PajekNetReader does not support multiple edge weights, as + * PajekNetFile does; this behavior is consistent with the NET format. + *
  • vertex locations (x and y; z coordinate is ignored) + * + * + *

    Here is an example format for a directed graph without edge weights and edges specified in + * list form:
    * - * Here is an example format for a directed graph without edge weights - * and edges specified in list form:
    *

    - * *vertices [# of vertices] 
    - * 1 "a" 
    - * 2 "b" 
    - * 3 "c" 
    - * *arcslist 
    - * 1 2 3 
    - * 2 3  
    + * *vertices [# of vertices]
    + * 1 "a"
    + * 2 "b"
    + * 3 "c"
    + * *arcslist
    + * 1 2 3
    + * 2 3
      * 
    * - * Here is an example format for an undirected graph with edge weights - * and edges specified in non-list form:
    + * Here is an example format for an undirected graph with edge weights and edges specified in + * non-list form:
    + * *
    - * *vertices [# of vertices] 
    - * 1 "a" 
    - * 2 "b" 
    - * 3 "c" 
    - * *edges 
    - * 1 2 0.1 
    - * 1 3 0.9 
    - * 2 3 1.0 
    - * 
    - * + * *vertices [# of vertices] + * 1 "a" + * 2 "b" + * 3 "c" + * *edges + * 1 2 0.1 + * 1 3 0.9 + * 2 3 1.0 + * + * * @author Joshua O'Madadhain - * @see "'Pajek - Program for Analysis and Visualization of Large Networks', Vladimir Batagelj and Andrej Mrvar, http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/pajekman.pdf" + * @see "'Pajek - Program for Analysis and Visualization of Large Networks', Vladimir Batagelj and + * Andrej Mrvar, http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/pajekman.pdf" * @author Tom Nelson - converted to jung2 */ -public class PajekNetReader,V,E> -{ - protected Supplier vertex_factory; - protected Supplier edge_factory; - - /** - * The map for vertex labels (if any) created by this class. - */ - protected SettableTransformer vertex_labels = new MapSettableTransformer(new HashMap()); - - /** - * The map for vertex locations (if any) defined by this class. - */ - protected SettableTransformer vertex_locations = new MapSettableTransformer(new HashMap()); - - protected SettableTransformer edge_weights = - new MapSettableTransformer(new HashMap()); - - /** - * Used to specify whether the most recently read line is a - * Pajek-specific tag. - */ - private static final Predicate v_pred = new StartsWithPredicate("*vertices"); - private static final Predicate a_pred = new StartsWithPredicate("*arcs"); - private static final Predicate e_pred = new StartsWithPredicate("*edges"); - private static final Predicate t_pred = new StartsWithPredicate("*"); - private static final Predicate c_pred = Predicates.or(a_pred, e_pred); - protected static final Predicate l_pred = ListTagPred.getInstance(); - - /** - * Creates a PajekNetReader instance with the specified vertex and edge factories. - * @param vertex_factory the Supplier to use to create vertex objects - * @param edge_factory the Supplier to use to create edge objects - */ - public PajekNetReader(Supplier vertex_factory, Supplier edge_factory) - { - this.vertex_factory = vertex_factory; - this.edge_factory = edge_factory; +public class PajekNetReader, V, E> { + protected Supplier vertex_factory; + protected Supplier edge_factory; + + /** The map for vertex labels (if any) created by this class. */ + protected SettableTransformer vertex_labels = + new MapSettableTransformer(new HashMap()); + + /** The map for vertex locations (if any) defined by this class. */ + protected SettableTransformer vertex_locations = + new MapSettableTransformer(new HashMap()); + + protected SettableTransformer edge_weights = + new MapSettableTransformer(new HashMap()); + + /** Used to specify whether the most recently read line is a Pajek-specific tag. */ + private static final Predicate v_pred = new StartsWithPredicate("*vertices"); + + private static final Predicate a_pred = new StartsWithPredicate("*arcs"); + private static final Predicate e_pred = new StartsWithPredicate("*edges"); + private static final Predicate t_pred = new StartsWithPredicate("*"); + private static final Predicate c_pred = Predicates.or(a_pred, e_pred); + protected static final Predicate l_pred = ListTagPred.getInstance(); + + /** + * Creates a PajekNetReader instance with the specified vertex and edge factories. + * + * @param vertex_factory the Supplier to use to create vertex objects + * @param edge_factory the Supplier to use to create edge objects + */ + public PajekNetReader(Supplier vertex_factory, Supplier edge_factory) { + this.vertex_factory = vertex_factory; + this.edge_factory = edge_factory; + } + + /** + * Creates a PajekNetReader instance with the specified edge Supplier, and whose vertex objects + * correspond to the integer IDs assigned in the file. Note that this requires V to + * be assignment-compatible with an Integer value. + * + * @param edge_factory the Supplier to use to create edge objects + */ + public PajekNetReader(Supplier edge_factory) { + this(null, edge_factory); + } + + /** + * Returns the graph created by parsing the specified file, as created by the specified Supplier. + * + * @param filename the file from which the graph is to be read + * @param graph_factory used to provide a graph instance + * @return a graph parsed from the specified file + * @throws IOException if the graph cannot be loaded + */ + public G load(String filename, Supplier graph_factory) throws IOException { + return load(new FileReader(filename), graph_factory.get()); + } + + /** + * Returns the graph created by parsing the specified reader, as created by the specified + * Supplier. + * + * @param reader the reader instance from which the graph is to be read + * @param graph_factory used to provide a graph instance + * @return a graph parsed from the specified reader + * @throws IOException if the graph cannot be loaded + */ + public G load(Reader reader, Supplier graph_factory) throws IOException { + return load(reader, graph_factory.get()); + } + + /** + * Returns the graph created by parsing the specified file, by populating the specified graph. + * + * @param filename the file from which the graph is to be read + * @param g the graph instance to populate + * @return a graph parsed from the specified file + * @throws IOException if the graph cannot be loaded + */ + public G load(String filename, G g) throws IOException { + if (g == null) throw new IllegalArgumentException("Graph provided must be non-null"); + return load(new FileReader(filename), g); + } + + /** + * Populates the graph g with the graph represented by the Pajek-format data supplied + * by reader. Stores edge weights, if any, according to nev (if + * non-null). + * + *

    Any existing vertices/edges of g, if any, are unaffected. + * + *

    The edge data are filtered according to g's constraints, if any; thus, if + * g only accepts directed edges, any undirected edges in the input are ignored. + * + * @param reader the reader from which the graph is to be read + * @param g the graph instance to populate + * @return a graph parsed from the specified reader + * @throws IOException if the graph cannot be loaded + */ + public G load(Reader reader, G g) throws IOException { + BufferedReader br = new BufferedReader(reader); + + // ignore everything until we see '*Vertices' + String curLine = skip(br, v_pred); + + if (curLine == null) // no vertices in the graph; return empty graph + return g; + + // create appropriate number of vertices + StringTokenizer st = new StringTokenizer(curLine); + st.nextToken(); // skip past "*vertices"; + int num_vertices = Integer.parseInt(st.nextToken()); + List id = null; + // TODO: under what circumstances (if any) is it reasonable for vertex_factory to be null? + if (vertex_factory != null) { + for (int i = 1; i <= num_vertices; i++) g.addNode(vertex_factory.get()); + id = new ArrayList(g.nodes()); } - /** - * Creates a PajekNetReader instance with the specified edge Supplier, - * and whose vertex objects correspond to the integer IDs assigned in the file. - * Note that this requires V to be assignment-compatible with - * an Integer value. - * @param edge_factory the Supplier to use to create edge objects - */ - public PajekNetReader(Supplier edge_factory) - { - this(null, edge_factory); + // read vertices until we see any Pajek format tag ('*...') + curLine = null; + while (br.ready()) { + curLine = br.readLine(); + if (curLine == null || t_pred.apply(curLine)) break; + if (curLine == "") // skip blank lines + continue; + + try { + readVertex(curLine, id, num_vertices); + } catch (IllegalArgumentException iae) { + br.close(); + reader.close(); + throw iae; + } } - - /** - * Returns the graph created by parsing the specified file, as created - * by the specified Supplier. - * @param filename the file from which the graph is to be read - * @param graph_factory used to provide a graph instance - * @return a graph parsed from the specified file - * @throws IOException if the graph cannot be loaded - */ - public G load(String filename, Supplier graph_factory) throws IOException + + // skip over the intermediate stuff (if any) + // and read the next arcs/edges section that we find + curLine = readArcsOrEdges(curLine, br, g, id, edge_factory); + + // ditto + readArcsOrEdges(curLine, br, g, id, edge_factory); + + br.close(); + reader.close(); + + return g; + } + + /** + * Parses curLine as a reference to a vertex, and optionally assigns label and + * location information. + */ + @SuppressWarnings("unchecked") + private void readVertex(String curLine, List id, int num_vertices) { + V v; + String[] parts = null; + int coord_idx = -1; // index of first coordinate in parts; -1 indicates no coordinates found + String index; + String label = null; + // if there are quote marks on this line, split on them; label is surrounded by them + if (curLine.indexOf('"') != -1) { + String[] initial_split = curLine.trim().split("\""); + // if there are any quote marks, there should be exactly 2 + if (initial_split.length < 2 || initial_split.length > 3) + throw new IllegalArgumentException( + "Unbalanced (or too many) " + "quote marks in " + curLine); + index = initial_split[0].trim(); + label = initial_split[1].trim(); + if (initial_split.length == 3) parts = initial_split[2].trim().split("\\s+", -1); + coord_idx = 0; + } else // no quote marks, but are there coordinates? { - return load(new FileReader(filename), graph_factory.get()); + parts = curLine.trim().split("\\s+", -1); + index = parts[0]; + switch (parts.length) { + case 1: // just the ID; nothing to do, continue + break; + case 2: // just the ID and a label + label = parts[1]; + break; + case 3: // ID, no label, coordinates + coord_idx = 1; + break; + default: // ID, label, (x,y) coordinates, maybe some other stuff + coord_idx = 2; + break; + } } - - /** - * Returns the graph created by parsing the specified reader, as created - * by the specified Supplier. - * @param reader the reader instance from which the graph is to be read - * @param graph_factory used to provide a graph instance - * @return a graph parsed from the specified reader - * @throws IOException if the graph cannot be loaded - */ - public G load(Reader reader, Supplier graph_factory) throws IOException - { - return load(reader, graph_factory.get()); + int v_id = Integer.parseInt(index) - 1; // go from 1-based to 0-based index + if (v_id >= num_vertices || v_id < 0) + throw new IllegalArgumentException( + "Vertex number " + v_id + "is not in the range [1," + num_vertices + "]"); + if (id != null) v = id.get(v_id); + else v = (V) (new Integer(v_id)); + // only attach the label if there's one to attach + if (label != null && label.length() > 0 && vertex_labels != null) vertex_labels.set(v, label); + + // parse the rest of the line + if (coord_idx != -1 + && parts != null + && parts.length >= coord_idx + 2 + && vertex_locations != null) { + double x = Double.parseDouble(parts[coord_idx]); + double y = Double.parseDouble(parts[coord_idx + 1]); + vertex_locations.set(v, new Point2D.Double(x, y)); } + } - /** - * Returns the graph created by parsing the specified file, by populating the - * specified graph. - * @param filename the file from which the graph is to be read - * @param g the graph instance to populate - * @return a graph parsed from the specified file - * @throws IOException if the graph cannot be loaded - */ - public G load(String filename, G g) throws IOException - { - if (g == null) - throw new IllegalArgumentException("Graph provided must be non-null"); - return load(new FileReader(filename), g); + @SuppressWarnings("unchecked") + private String readArcsOrEdges( + String curLine, + BufferedReader br, + MutableNetwork g, + List id, + Supplier edge_factory) + throws IOException { + String nextLine = curLine; + + // in case we're not there yet (i.e., format tag isn't arcs or edges) + if (!c_pred.apply(curLine)) nextLine = skip(br, c_pred); + + boolean reading_arcs = false; + boolean reading_edges = false; + if (a_pred.apply(nextLine)) { + Preconditions.checkState( + g.isDirected(), "Supplied undirected-only graph cannot be populated with directed edges"); + reading_arcs = true; } - - /** - * Populates the graph g with the graph represented by the - * Pajek-format data supplied by reader. Stores edge weights, - * if any, according to nev (if non-null). - * - *

    Any existing vertices/edges of g, if any, are unaffected. - * - *

    The edge data are filtered according to g's constraints, if any; thus, if - * g only accepts directed edges, any undirected edges in the - * input are ignored. - * - * @param reader the reader from which the graph is to be read - * @param g the graph instance to populate - * @return a graph parsed from the specified reader - * @throws IOException if the graph cannot be loaded - */ - public G load(Reader reader, G g) throws IOException - { - BufferedReader br = new BufferedReader(reader); - - // ignore everything until we see '*Vertices' - String curLine = skip(br, v_pred); - - if (curLine == null) // no vertices in the graph; return empty graph - return g; - - // create appropriate number of vertices - StringTokenizer st = new StringTokenizer(curLine); - st.nextToken(); // skip past "*vertices"; - int num_vertices = Integer.parseInt(st.nextToken()); - List id = null; - // TODO: under what circumstances (if any) is it reasonable for vertex_factory to be null? - if (vertex_factory != null) - { - for (int i = 1; i <= num_vertices; i++) - g.addNode(vertex_factory.get()); - id = new ArrayList(g.nodes()); - } - - // read vertices until we see any Pajek format tag ('*...') - curLine = null; - while (br.ready()) - { - curLine = br.readLine(); - if (curLine == null || t_pred.apply(curLine)) - break; - if (curLine == "") // skip blank lines - continue; - - try - { - readVertex(curLine, id, num_vertices); - } - catch (IllegalArgumentException iae) - { - br.close(); - reader.close(); - throw iae; - } - } - - // skip over the intermediate stuff (if any) - // and read the next arcs/edges section that we find - curLine = readArcsOrEdges(curLine, br, g, id, edge_factory); - - // ditto - readArcsOrEdges(curLine, br, g, id, edge_factory); - - br.close(); - reader.close(); - - return g; + if (e_pred.apply(nextLine)) { + Preconditions.checkState( + !g.isDirected(), + "Supplied directed-only graph cannot be populated with undirected edges"); + reading_edges = true; } - /** - * Parses curLine as a reference to a vertex, and optionally assigns - * label and location information. - */ - @SuppressWarnings("unchecked") - private void readVertex(String curLine, List id, int num_vertices) - { - V v; - String[] parts = null; - int coord_idx = -1; // index of first coordinate in parts; -1 indicates no coordinates found - String index; - String label = null; - // if there are quote marks on this line, split on them; label is surrounded by them - if (curLine.indexOf('"') != -1) - { - String[] initial_split = curLine.trim().split("\""); - // if there are any quote marks, there should be exactly 2 - if (initial_split.length < 2 || initial_split.length > 3) - throw new IllegalArgumentException("Unbalanced (or too many) " + - "quote marks in " + curLine); - index = initial_split[0].trim(); - label = initial_split[1].trim(); - if (initial_split.length == 3) - parts = initial_split[2].trim().split("\\s+", -1); - coord_idx = 0; - } - else // no quote marks, but are there coordinates? - { - parts = curLine.trim().split("\\s+", -1); - index = parts[0]; - switch (parts.length) - { - case 1: // just the ID; nothing to do, continue - break; - case 2: // just the ID and a label - label = parts[1]; - break; - case 3: // ID, no label, coordinates - coord_idx = 1; - break; - default: // ID, label, (x,y) coordinates, maybe some other stuff - coord_idx = 2; - break; - } - } - int v_id = Integer.parseInt(index) - 1; // go from 1-based to 0-based index - if (v_id >= num_vertices || v_id < 0) - throw new IllegalArgumentException("Vertex number " + v_id + - "is not in the range [1," + num_vertices + "]"); - if (id != null) - v = id.get(v_id); - else - v = (V)(new Integer(v_id)); - // only attach the label if there's one to attach - if (label != null && label.length() > 0 && vertex_labels != null) - vertex_labels.set(v, label); - - // parse the rest of the line - if (coord_idx != -1 && parts != null && parts.length >= coord_idx+2 && vertex_locations != null) - { - double x = Double.parseDouble(parts[coord_idx]); - double y = Double.parseDouble(parts[coord_idx+1]); - vertex_locations.set(v, new Point2D.Double(x,y)); - } + if (!(reading_arcs || reading_edges)) return nextLine; + + boolean is_list = l_pred.apply(nextLine); + + while (br.ready()) { + nextLine = br.readLine(); + if (nextLine == null || t_pred.apply(nextLine)) break; + if (curLine == "") // skip blank lines + continue; + + StringTokenizer st = new StringTokenizer(nextLine.trim()); + + int vid1 = Integer.parseInt(st.nextToken()) - 1; + // FIXME: check for vid < 0 + V v1; + if (id != null) { + v1 = id.get(vid1); + } else + // TODO: wat (look for other (V) casts also) + v1 = (V) new Integer(vid1); + + if (is_list) // one source, multiple destinations + { + do { + createAddEdge(st, v1, g, id, edge_factory); + } while (st.hasMoreTokens()); + } else // one source, one destination, at most one weight + { + E e = createAddEdge(st, v1, g, id, edge_factory); + // get the edge weight if we care + if (edge_weights != null && st.hasMoreTokens()) + edge_weights.set(e, new Float(st.nextToken())); + } } + return nextLine; + } - - - @SuppressWarnings("unchecked") - private String readArcsOrEdges(String curLine, BufferedReader br, MutableNetwork g, - List id, Supplier edge_factory) - throws IOException - { - String nextLine = curLine; - - // in case we're not there yet (i.e., format tag isn't arcs or edges) - if (! c_pred.apply(curLine)) - nextLine = skip(br, c_pred); - - boolean reading_arcs = false; - boolean reading_edges = false; - if (a_pred.apply(nextLine)) - { - Preconditions.checkState(g.isDirected(), - "Supplied undirected-only graph cannot be populated with directed edges"); - reading_arcs = true; - } - if (e_pred.apply(nextLine)) - { - Preconditions.checkState(!g.isDirected(), - "Supplied directed-only graph cannot be populated with undirected edges"); - reading_edges = true; - } - - if (!(reading_arcs || reading_edges)) - return nextLine; - - boolean is_list = l_pred.apply(nextLine); - - while (br.ready()) - { - nextLine = br.readLine(); - if (nextLine == null || t_pred.apply(nextLine)) - break; - if (curLine == "") // skip blank lines - continue; - - StringTokenizer st = new StringTokenizer(nextLine.trim()); - - int vid1 = Integer.parseInt(st.nextToken()) - 1; - // FIXME: check for vid < 0 - V v1; - if (id != null) { - v1 = id.get(vid1); - } - else - // TODO: wat (look for other (V) casts also) - v1 = (V)new Integer(vid1); - - if (is_list) // one source, multiple destinations - { - do - { - createAddEdge(st, v1, g, id, edge_factory); - } while (st.hasMoreTokens()); - } - else // one source, one destination, at most one weight - { - E e = createAddEdge(st, v1, g, id, edge_factory); - // get the edge weight if we care - if (edge_weights != null && st.hasMoreTokens()) - edge_weights.set(e, new Float(st.nextToken())); - } - } - return nextLine; + @SuppressWarnings("unchecked") + protected E createAddEdge( + StringTokenizer st, V v1, MutableNetwork g, List id, Supplier edge_factory) { + int vid2 = Integer.parseInt(st.nextToken()) - 1; + V v2; + if (id != null) v2 = id.get(vid2); + else v2 = (V) new Integer(vid2); + E e = edge_factory.get(); + + // don't error-check this: let the graph implementation do whatever it's going to do + // (add the edge, replace the existing edge, throw an exception--depends on the graph implementation) + g.addEdge(v1, v2, e); + return e; + } + + /** + * Returns the first line read from br for which p returns true + * , or null if there is no such line. + * + * @param br the reader from which the graph is being read + * @param p predicate specifying what line to accept + * @return the first line from {@code br} that matches {@code p}, or null + * @throws IOException if an error is encountered while reading from {@code br} + */ + protected String skip(BufferedReader br, Predicate p) throws IOException { + while (br.ready()) { + String curLine = br.readLine(); + if (curLine == null) break; + curLine = curLine.trim(); + if (p.apply(curLine)) return curLine; } + return null; + } - @SuppressWarnings("unchecked") - protected E createAddEdge(StringTokenizer st, V v1, MutableNetwork g, - List id, Supplier edge_factory) - { - int vid2 = Integer.parseInt(st.nextToken()) - 1; - V v2; - if (id != null) - v2 = id.get(vid2); - else - v2 = (V)new Integer(vid2); - E e = edge_factory.get(); - - // don't error-check this: let the graph implementation do whatever it's going to do - // (add the edge, replace the existing edge, throw an exception--depends on the graph implementation) - g.addEdge(v1, v2, e); - return e; + /** + * A Predicate which evaluates to true if the argument starts with the + * constructor-specified String. + * + * @author Joshua O'Madadhain + */ + protected static class StartsWithPredicate implements Predicate { + private String tag; + + protected StartsWithPredicate(String s) { + this.tag = s; } - - /** - * Returns the first line read from br for which p - * returns true, or null if there is no - * such line. - * @param br the reader from which the graph is being read - * @param p predicate specifying what line to accept - * @return the first line from {@code br} that matches {@code p}, or null - * @throws IOException if an error is encountered while reading from {@code br} - */ - protected String skip(BufferedReader br, Predicate p) throws IOException - { - while (br.ready()) - { - String curLine = br.readLine(); - if (curLine == null) - break; - curLine = curLine.trim(); - if (p.apply(curLine)) - return curLine; - } - return null; + + public boolean apply(String str) { + return (str != null && str.toLowerCase().startsWith(tag)); } - - /** - * A Predicate which evaluates to true if the - * argument starts with the constructor-specified String. - * - * @author Joshua O'Madadhain - */ - protected static class StartsWithPredicate implements Predicate { - private String tag; - - protected StartsWithPredicate(String s) { - this.tag = s; - } - - public boolean apply(String str) { - return (str != null && str.toLowerCase().startsWith(tag)); - } + } + + /** + * A Predicate which evaluates to true if the argument ends with the string "list". + * + * @author Joshua O'Madadhain + */ + protected static class ListTagPred implements Predicate { + protected static ListTagPred instance; + + protected ListTagPred() {} + + protected static ListTagPred getInstance() { + if (instance == null) instance = new ListTagPred(); + return instance; } - - - /** - * A Predicate which evaluates to true if the - * argument ends with the string "list". - * - * @author Joshua O'Madadhain - */ - protected static class ListTagPred implements Predicate - { - protected static ListTagPred instance; - - protected ListTagPred() {} - - protected static ListTagPred getInstance() - { - if (instance == null) - instance = new ListTagPred(); - return instance; - } - - public boolean apply(String s) - { - return (s != null && s.toLowerCase().endsWith("list")); - } + + public boolean apply(String s) { + return (s != null && s.toLowerCase().endsWith("list")); } + } + + /** @return the vertexLocationTransformer */ + public SettableTransformer getVertexLocationTransformer() { + return vertex_locations; + } + + /** + * Provides a Function which will be used to write out the vertex locations. + * + * @param vertex_locations a container for the vertex locations + */ + public void setVertexLocationTransformer(SettableTransformer vertex_locations) { + this.vertex_locations = vertex_locations; + } + + /** @return a mapping from vertices to their labels */ + public SettableTransformer getVertexLabeller() { + return vertex_labels; + } + + /** + * Provides a Function which will be used to write out the vertex labels. + * + * @param vertex_labels a container for the vertex labels + */ + public void setVertexLabeller(SettableTransformer vertex_labels) { + this.vertex_labels = vertex_labels; + } - /** - * @return the vertexLocationTransformer - */ - public SettableTransformer getVertexLocationTransformer() { - return vertex_locations; - } - - /** - * Provides a Function which will be used to write out the vertex locations. - * @param vertex_locations a container for the vertex locations - */ - public void setVertexLocationTransformer(SettableTransformer vertex_locations) - { - this.vertex_locations = vertex_locations; - } - - /** - * @return a mapping from vertices to their labels - */ - public SettableTransformer getVertexLabeller() { - return vertex_labels; - } - - /** - * Provides a Function which will be used to write out the vertex labels. - * @param vertex_labels a container for the vertex labels - */ - public void setVertexLabeller(SettableTransformer vertex_labels) - { - this.vertex_labels = vertex_labels; - } - - /** - * @return a mapping from edges to their weights - */ - public SettableTransformer getEdgeWeightTransformer() - { - return edge_weights; - } - - /** - * Provides a Function which will be used to write out edge weights. - * @param edge_weights a container for the edge weights - */ - public void setEdgeWeightTransformer(SettableTransformer edge_weights) - { - this.edge_weights = edge_weights; - } + /** @return a mapping from edges to their weights */ + public SettableTransformer getEdgeWeightTransformer() { + return edge_weights; + } + /** + * Provides a Function which will be used to write out edge weights. + * + * @param edge_weights a container for the edge weights + */ + public void setEdgeWeightTransformer(SettableTransformer edge_weights) { + this.edge_weights = edge_weights; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/PajekNetWriter.java b/jung-io/src/main/java/edu/uci/ics/jung/io/PajekNetWriter.java index de8be408..30784afc 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/PajekNetWriter.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/PajekNetWriter.java @@ -1,7 +1,7 @@ /* * Created on May 4, 2004 * - * Copyright (c) 2004, The JUNG Authors + * Copyright (c) 2004, The JUNG Authors * * All rights reserved. * @@ -11,6 +11,9 @@ */ package edu.uci.ics.jung.io; +import com.google.common.base.Function; +import com.google.common.graph.EndpointPair; +import com.google.common.graph.Network; import java.awt.geom.Point2D; import java.io.BufferedWriter; import java.io.FileWriter; @@ -19,153 +22,154 @@ import java.util.ArrayList; import java.util.List; -import com.google.common.base.Function; -import com.google.common.graph.EndpointPair; -import com.google.common.graph.Network; - /** * Writes graphs in the Pajek NET format. - * - *

    Labels for vertices, edge weights, and vertex locations may each optionally - * be specified. Note that vertex location coordinates - * must be normalized to the interval [0, 1] on each axis in order to conform to the - * Pajek specification. - * + * + *

    Labels for vertices, edge weights, and vertex locations may each optionally be specified. Note + * that vertex location coordinates must be normalized to the interval [0, 1] on each axis in order + * to conform to the Pajek specification. + * * @author Joshua O'Madadhain * @author Tom Nelson - converted to jung2 */ -public class PajekNetWriter -{ - /** - * Creates a new instance. - */ - public PajekNetWriter() - { - } +public class PajekNetWriter { + /** Creates a new instance. */ + public PajekNetWriter() {} - /** - * Saves the graph to the specified file. - * @param g the graph to be saved - * @param filename the filename of the file to write the graph to - * @param vs mapping from vertices to labels - * @param nev mapping from edges to weights - * @param vld mapping from vertices to locations - * @throws IOException if the graph cannot be saved - */ - public void save(Network g, String filename, Function vs, - Function nev, Function vld) throws IOException - { - save(g, new FileWriter(filename), vs, nev, vld); - } - - /** - * Saves the graph to the specified file. - * @param g the graph to be saved - * @param filename the filename of the file to write the graph to - * @param vs mapping from vertices to labels - * @param nev mapping from edges to weights - * @throws IOException if the graph cannot be saved - */ - public void save(Network g, String filename, Function vs, - Function nev) throws IOException - { - save(g, new FileWriter(filename), vs, nev, null); - } - - /** - * Saves the graph to the specified file. No vertex labels are written, and the - * edge weights are written as 1.0. - * @param g the graph to be saved - * @param filename the filename of the file to write the graph to - * @throws IOException if the graph cannot be saved - */ - public void save(Network g, String filename) throws IOException - { - save(g, filename, null, null, null); - } + /** + * Saves the graph to the specified file. + * + * @param g the graph to be saved + * @param filename the filename of the file to write the graph to + * @param vs mapping from vertices to labels + * @param nev mapping from edges to weights + * @param vld mapping from vertices to locations + * @throws IOException if the graph cannot be saved + */ + public void save( + Network g, + String filename, + Function vs, + Function nev, + Function vld) + throws IOException { + save(g, new FileWriter(filename), vs, nev, vld); + } - /** - * Saves the graph to the specified writer. No vertex labels are written, and the - * edge weights are written as 1.0. - * @param g the graph to be saved - * @param w the writer instance to write the graph to - * @throws IOException if the graph cannot be saved - */ - public void save(Network g, Writer w) throws IOException - { - save(g, w, null, null, null); - } + /** + * Saves the graph to the specified file. + * + * @param g the graph to be saved + * @param filename the filename of the file to write the graph to + * @param vs mapping from vertices to labels + * @param nev mapping from edges to weights + * @throws IOException if the graph cannot be saved + */ + public void save( + Network g, String filename, Function vs, Function nev) + throws IOException { + save(g, new FileWriter(filename), vs, nev, null); + } - /** - * Saves the graph to the specified writer. - * @param g the graph to be saved - * @param w the writer instance to write the graph to - * @param vs mapping from vertices to labels - * @param nev mapping from edges to weights - * @throws IOException if the graph cannot be saved - */ - public void save(Network g, Writer w, Function vs, - Function nev) throws IOException - { - save(g, w, vs, nev, null); - } - - /** - * Saves the graph to the specified writer. - * @param graph the graph to be saved - * @param w the writer instance to write the graph to - * @param vs mapping from vertices to labels (no labels are written if null) - * @param nev mapping from edges to weights (defaults to weights of 1.0 if null) - * @param vld mapping from vertices to locations (no locations are written if null) - * @throws IOException if the graph cannot be saved + /** + * Saves the graph to the specified file. No vertex labels are written, and the edge weights are + * written as 1.0. + * + * @param g the graph to be saved + * @param filename the filename of the file to write the graph to + * @throws IOException if the graph cannot be saved + */ + public void save(Network g, String filename) throws IOException { + save(g, filename, null, null, null); + } + + /** + * Saves the graph to the specified writer. No vertex labels are written, and the edge weights are + * written as 1.0. + * + * @param g the graph to be saved + * @param w the writer instance to write the graph to + * @throws IOException if the graph cannot be saved + */ + public void save(Network g, Writer w) throws IOException { + save(g, w, null, null, null); + } + + /** + * Saves the graph to the specified writer. + * + * @param g the graph to be saved + * @param w the writer instance to write the graph to + * @param vs mapping from vertices to labels + * @param nev mapping from edges to weights + * @throws IOException if the graph cannot be saved + */ + public void save(Network g, Writer w, Function vs, Function nev) + throws IOException { + save(g, w, vs, nev, null); + } + + /** + * Saves the graph to the specified writer. + * + * @param graph the graph to be saved + * @param w the writer instance to write the graph to + * @param vs mapping from vertices to labels (no labels are written if null) + * @param nev mapping from edges to weights (defaults to weights of 1.0 if null) + * @param vld mapping from vertices to locations (no locations are written if null) + * @throws IOException if the graph cannot be saved + */ + public void save( + Network graph, + Writer w, + Function vs, + Function nev, + Function vld) + throws IOException { + /* + * TODO: Changes we might want to make: + * - optionally writing out in list form */ - public void save(Network graph, Writer w, Function vs, - Function nev, Function vld) throws IOException - { - /* - * TODO: Changes we might want to make: - * - optionally writing out in list form - */ - - BufferedWriter writer = new BufferedWriter(w); - if (nev == null) - nev = new Function() { public Number apply(E e) { return 1; } }; - writer.write("*Vertices " + graph.nodes().size()); - writer.newLine(); - - List id = new ArrayList(graph.nodes()); - for (V currentVertex : graph.nodes()) - { - // convert from 0-based to 1-based index - int v_id = id.indexOf(currentVertex) + 1; - writer.write(""+v_id); - if (vs != null) - { - String label = vs.apply(currentVertex); - if (label != null) - writer.write (" \"" + label + "\""); - } - if (vld != null) - { - Point2D location = vld.apply(currentVertex); - if (location != null) - writer.write (" " + location.getX() + " " + location.getY() + " 0.0"); + + BufferedWriter writer = new BufferedWriter(w); + if (nev == null) + nev = + new Function() { + public Number apply(E e) { + return 1; } - writer.newLine(); - } + }; + writer.write("*Vertices " + graph.nodes().size()); + writer.newLine(); + + List id = new ArrayList(graph.nodes()); + for (V currentVertex : graph.nodes()) { + // convert from 0-based to 1-based index + int v_id = id.indexOf(currentVertex) + 1; + writer.write("" + v_id); + if (vs != null) { + String label = vs.apply(currentVertex); + if (label != null) writer.write(" \"" + label + "\""); + } + if (vld != null) { + Point2D location = vld.apply(currentVertex); + if (location != null) writer.write(" " + location.getX() + " " + location.getY() + " 0.0"); + } + writer.newLine(); + } - writer.write(graph.isDirected() ? "*Arcs" : "*Edges"); - writer.newLine(); + writer.write(graph.isDirected() ? "*Arcs" : "*Edges"); + writer.newLine(); - for (E e : graph.edges()) { - EndpointPair endpoints = graph.incidentNodes(e); - // convert from 0-based to 1-based index - int nodeU_id = id.indexOf(endpoints.nodeU()) + 1; - int nodeV_id = id.indexOf(endpoints.nodeV()) + 1; - float weight = nev.apply(e).floatValue(); - writer.write(nodeU_id + " " + nodeV_id + " " + weight); - writer.newLine(); - } - writer.close(); + for (E e : graph.edges()) { + EndpointPair endpoints = graph.incidentNodes(e); + // convert from 0-based to 1-based index + int nodeU_id = id.indexOf(endpoints.nodeU()) + 1; + int nodeV_id = id.indexOf(endpoints.nodeV()) + 1; + float weight = nev.apply(e).floatValue(); + writer.write(nodeU_id + " " + nodeV_id + " " + weight); + writer.newLine(); } + writer.close(); + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/AbstractMetadata.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/AbstractMetadata.java index e087c778..86407d03 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/AbstractMetadata.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/AbstractMetadata.java @@ -15,26 +15,26 @@ /** * Abstract base class for metadata - implements the property functionality - * + * * @author Nathan Mittler - nathan.mittler@gmail.com */ public abstract class AbstractMetadata implements Metadata { - final private Map properties = new HashMap(); - - public Map getProperties() { - return properties; - } - - public String getProperty(String key) { - return properties.get(key); - } - - public String setProperty(String key, String value) { - return properties.put(key, value); - } - - public void addData(DataMetadata data) { - properties.put(data.getKey(), data.getValue()); - } + private final Map properties = new HashMap(); + + public Map getProperties() { + return properties; + } + + public String getProperty(String key) { + return properties.get(key); + } + + public String setProperty(String key, String value) { + return properties.put(key, value); + } + + public void addData(DataMetadata data) { + properties.put(data.getKey(), data.getValue()); + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/DataMetadata.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/DataMetadata.java index fede529c..d166d996 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/DataMetadata.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/DataMetadata.java @@ -14,28 +14,26 @@ * Metadata structure for the 'data' GraphML element. * * @author Nathan Mittler - nathan.mittler@gmail.com - * * @see "http://graphml.graphdrawing.org/specification.html" */ public class DataMetadata { - private String key; - private String value; - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } + private String key; + private String value; + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/EdgeMetadata.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/EdgeMetadata.java index 8034b064..72d278d8 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/EdgeMetadata.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/EdgeMetadata.java @@ -14,101 +14,84 @@ * Metadata structure for the 'edge' GraphML element. * * @author Nathan Mittler - nathan.mittler@gmail.com - * * @see "http://graphml.graphdrawing.org/specification.html" */ public class EdgeMetadata extends AbstractMetadata { - private String id; - private Boolean directed; - private String source; - private String target; - private String sourcePort; - private String targetPort; - private String description; - private Object edge; - - public String getId() { - return id; - } - - - public void setId(String id) { - this.id = id; - } - - - public Boolean isDirected() { - return directed; - } - - - public void setDirected(Boolean directed) { - this.directed = directed; - } - - - public String getSource() { - return source; - } - - - public void setSource(String source) { - this.source = source; - } - - - public String getTarget() { - return target; - } - - - public void setTarget(String target) { - this.target = target; - } - - - public String getSourcePort() { - return sourcePort; - } - - - public void setSourcePort(String sourcePort) { - this.sourcePort = sourcePort; - } - - - public String getTargetPort() { - return targetPort; - } - - - public void setTargetPort(String targetPort) { - this.targetPort = targetPort; - } - - - public String getDescription() { - return description; - } - - - public void setDescription(String description) { - this.description = description; - } - - public Object getEdge() { - return edge; - } - - - public void setEdge(Object edge) { - this.edge = edge; - } - - - public MetadataType getMetadataType() { - return MetadataType.EDGE; - } - + private String id; + private Boolean directed; + private String source; + private String target; + private String sourcePort; + private String targetPort; + private String description; + private Object edge; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Boolean isDirected() { + return directed; + } + + public void setDirected(Boolean directed) { + this.directed = directed; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } + + public String getSourcePort() { + return sourcePort; + } + + public void setSourcePort(String sourcePort) { + this.sourcePort = sourcePort; + } + + public String getTargetPort() { + return targetPort; + } + + public void setTargetPort(String targetPort) { + this.targetPort = targetPort; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Object getEdge() { + return edge; + } + + public void setEdge(Object edge) { + this.edge = edge; + } + + public MetadataType getMetadataType() { + return MetadataType.EDGE; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/EndpointMetadata.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/EndpointMetadata.java index 60a28da0..6d91d2a5 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/EndpointMetadata.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/EndpointMetadata.java @@ -14,55 +14,63 @@ * Metadata structure for the 'endpoint' GraphML element. * * @author Nathan Mittler - nathan.mittler@gmail.com - * * @see "http://graphml.graphdrawing.org/specification.html" */ public class EndpointMetadata extends AbstractMetadata { - public enum EndpointType { - IN, - OUT, - UNDIR - } - - private String id; - private String port; - private String node; - private String description; - private EndpointType endpointType = EndpointType.UNDIR; - - public String getId() { - return id; - } - public void setId(String id) { - this.id = id; - } - public String getPort() { - return port; - } - public void setPort(String port) { - this.port = port; - } - public String getNode() { - return node; - } - public void setNode(String node) { - this.node = node; - } - public EndpointType getEndpointType() { - return endpointType; - } - public void setEndpointType(EndpointType endpointType) { - this.endpointType = endpointType; - } - public String getDescription() { - return description; - } - public void setDescription(String description) { - this.description = description; - } - public MetadataType getMetadataType() { - return MetadataType.ENDPOINT; - } - + public enum EndpointType { + IN, + OUT, + UNDIR + } + + private String id; + private String port; + private String node; + private String description; + private EndpointType endpointType = EndpointType.UNDIR; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getPort() { + return port; + } + + public void setPort(String port) { + this.port = port; + } + + public String getNode() { + return node; + } + + public void setNode(String node) { + this.node = node; + } + + public EndpointType getEndpointType() { + return endpointType; + } + + public void setEndpointType(EndpointType endpointType) { + this.endpointType = endpointType; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public MetadataType getMetadataType() { + return MetadataType.ENDPOINT; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/ExceptionConverter.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/ExceptionConverter.java index 9de9db11..546196c5 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/ExceptionConverter.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/ExceptionConverter.java @@ -11,44 +11,41 @@ package edu.uci.ics.jung.io.graphml; import edu.uci.ics.jung.io.GraphIOException; - import javax.xml.stream.XMLStreamException; /** - * Converts an exception to the a GraphIOException. Runtime exceptions - * are checked for the cause. If the cause is an XMLStreamException, it is - * converted to a GraphIOException. Otherwise, the RuntimeException is - * rethrown. + * Converts an exception to the a GraphIOException. Runtime exceptions are checked for the cause. If + * the cause is an XMLStreamException, it is converted to a GraphIOException. Otherwise, the + * RuntimeException is rethrown. * * @author Nathan Mittler - nathan.mittler@gmail.com */ public class ExceptionConverter { - /** - * Converts an exception to the a GraphIOException. Runtime exceptions - * are checked for the cause. If the cause is an XMLStreamException, it is - * converted to a GraphReaderException. Otherwise, the RuntimeException is - * rethrown. - * - * @param e the exception to be converted - * @throws GraphIOException the converted exception - */ - static public void convert(Exception e) throws GraphIOException { - - if (e instanceof GraphIOException) { - throw (GraphIOException) e; - } - - if (e instanceof RuntimeException) { + /** + * Converts an exception to the a GraphIOException. Runtime exceptions are checked for the cause. + * If the cause is an XMLStreamException, it is converted to a GraphReaderException. Otherwise, + * the RuntimeException is rethrown. + * + * @param e the exception to be converted + * @throws GraphIOException the converted exception + */ + public static void convert(Exception e) throws GraphIOException { + + if (e instanceof GraphIOException) { + throw (GraphIOException) e; + } - // If the cause was an XMLStreamException, throw a GraphReaderException - if (e.getCause() instanceof XMLStreamException) { - throw new GraphIOException(e.getCause()); - } + if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } + // If the cause was an XMLStreamException, throw a GraphReaderException + if (e.getCause() instanceof XMLStreamException) { + throw new GraphIOException(e.getCause()); + } - throw new GraphIOException(e); + throw (RuntimeException) e; } + + throw new GraphIOException(e); + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMLConstants.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMLConstants.java index 8567b6ee..fa6ce52e 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMLConstants.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMLConstants.java @@ -12,37 +12,37 @@ /** * Provides some constants for element/attribute names in GraphML - * + * * @author Nathan Mittler - nathan.mittler@gmail.com */ public class GraphMLConstants { - public static final String GRAPHML_NAME = "graphml"; - public static final String GRAPH_NAME = "graph"; - public static final String NODE_NAME = "node"; - public static final String EDGE_NAME = "edge"; - public static final String ENDPOINT_NAME = "endpoint"; - public static final String HYPEREDGE_NAME = "hyperedge"; - public static final String PORT_NAME = "port"; - public static final String KEY_NAME = "key"; - public static final String DATA_NAME = "data"; - public static final String ALL_NAME = "all"; - public static final String ID_NAME = "id"; - public static final String FOR_NAME = "for"; - public static final String DESC_NAME = "desc"; - public static final String DEFAULT_NAME = "default"; - public static final String ATTRNAME_NAME = "attr.name"; - public static final String ATTRTYPE_NAME = "attr.type"; - public static final String NAME_NAME = "name"; - public static final String EDGEDEFAULT_NAME = "edgedefault"; - public static final String TYPE_NAME = "type"; - public static final String IN_NAME = "in"; - public static final String OUT_NAME = "out"; - public static final String UNDIR_NAME = "undir"; - public static final String DIRECTED_NAME = "directed"; - public static final String UNDIRECTED_NAME = "undirected"; - public static final String SOURCE_NAME = "source"; - public static final String TARGET_NAME = "target"; - public static final String SOURCEPORT_NAME = "sourceport"; - public static final String TARGETPORT_NAME = "targetport"; + public static final String GRAPHML_NAME = "graphml"; + public static final String GRAPH_NAME = "graph"; + public static final String NODE_NAME = "node"; + public static final String EDGE_NAME = "edge"; + public static final String ENDPOINT_NAME = "endpoint"; + public static final String HYPEREDGE_NAME = "hyperedge"; + public static final String PORT_NAME = "port"; + public static final String KEY_NAME = "key"; + public static final String DATA_NAME = "data"; + public static final String ALL_NAME = "all"; + public static final String ID_NAME = "id"; + public static final String FOR_NAME = "for"; + public static final String DESC_NAME = "desc"; + public static final String DEFAULT_NAME = "default"; + public static final String ATTRNAME_NAME = "attr.name"; + public static final String ATTRTYPE_NAME = "attr.type"; + public static final String NAME_NAME = "name"; + public static final String EDGEDEFAULT_NAME = "edgedefault"; + public static final String TYPE_NAME = "type"; + public static final String IN_NAME = "in"; + public static final String OUT_NAME = "out"; + public static final String UNDIR_NAME = "undir"; + public static final String DIRECTED_NAME = "directed"; + public static final String UNDIRECTED_NAME = "undirected"; + public static final String SOURCE_NAME = "source"; + public static final String TARGET_NAME = "target"; + public static final String SOURCEPORT_NAME = "sourceport"; + public static final String TARGETPORT_NAME = "targetport"; } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMLDocument.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMLDocument.java index e2ac1533..b4bf7447 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMLDocument.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMLDocument.java @@ -13,24 +13,22 @@ import java.util.ArrayList; import java.util.List; -/** - * Maintains all the metadata read in from a single GraphML XML document. - */ +/** Maintains all the metadata read in from a single GraphML XML document. */ public class GraphMLDocument { - final private KeyMap keyMap = new KeyMap(); - final private List graphMetadata = new ArrayList(); + private final KeyMap keyMap = new KeyMap(); + private final List graphMetadata = new ArrayList(); - public KeyMap getKeyMap() { - return keyMap; - } + public KeyMap getKeyMap() { + return keyMap; + } - public List getGraphMetadata() { - return graphMetadata; - } + public List getGraphMetadata() { + return graphMetadata; + } - public void clear() { - graphMetadata.clear(); - keyMap.clear(); - } + public void clear() { + graphMetadata.clear(); + keyMap.clear(); + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMLReader2.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMLReader2.java index bbfa02b7..ddb95ea7 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMLReader2.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMLReader2.java @@ -10,353 +10,338 @@ package edu.uci.ics.jung.io.graphml; +import com.google.common.base.Function; +import com.google.common.graph.MutableNetwork; +import edu.uci.ics.jung.io.GraphIOException; +import edu.uci.ics.jung.io.GraphReader; +import edu.uci.ics.jung.io.graphml.parser.ElementParserRegistry; +import edu.uci.ics.jung.io.graphml.parser.GraphMLEventFilter; import java.io.IOException; import java.io.InputStream; import java.io.Reader; - import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; -import com.google.common.base.Function; -import com.google.common.graph.MutableNetwork; - -import edu.uci.ics.jung.io.GraphIOException; -import edu.uci.ics.jung.io.GraphReader; -import edu.uci.ics.jung.io.graphml.parser.ElementParserRegistry; -import edu.uci.ics.jung.io.graphml.parser.GraphMLEventFilter; - /** - * Reads in data from a GraphML-formatted file and generates graphs based on - * that data. Does not currently support nested graphs. - * - *

    Note that the user is responsible for supplying a graph - * Transformer that will create graphs capable of supporting the - * edge types in the supplied GraphML file. If the graph generated by the - * Factory is not compatible (for example: if the graph does not - * accept directed edges, and the GraphML file contains a directed edge) then - * the results are graph-implementation-dependent. + * Reads in data from a GraphML-formatted file and generates graphs based on that data. Does not + * currently support nested graphs. + * + *

    Note that the user is responsible for supplying a graph Transformer that will + * create graphs capable of supporting the edge types in the supplied GraphML file. If the graph + * generated by the Factory is not compatible (for example: if the graph does not + * accept directed edges, and the GraphML file contains a directed edge) then the results are + * graph-implementation-dependent. * * @author Nathan Mittler - nathan.mittler@gmail.com - * - * @param - * The graph type to be read from the GraphML file - * @param the vertex type - * The vertex type used by the graph - * @param the edge type - * The edge type used by the graph + * @param The graph type to be read from the GraphML file + * @param the vertex type The vertex type used by the graph + * @param the edge type The edge type used by the graph * @see "http://graphml.graphdrawing.org/specification.html" */ // TODO: do we actually need the G type, or can we just have it be "Network"? // TODO: provide Graph/ValueGraph support? // TODO: don't bother taking in a graph factory -public class GraphMLReader2, V, E> implements - GraphReader { - - protected XMLEventReader xmlEventReader; - protected Reader fileReader; - protected Function graphTransformer; - protected Function vertexTransformer; - protected Function edgeTransformer; -// protected Function hyperEdgeTransformer; - protected boolean initialized; - final protected GraphMLDocument document = new GraphMLDocument(); - final protected ElementParserRegistry parserRegistry; - private InputStream inputStream ; - - /** - * Constructs a GraphML reader around the given reader. This constructor - * requires the user to supply transformation functions to convert from the - * GraphML metadata to Graph, Vertex, Edge instances. These Function - * functions can be used as purely factories (i.e. the metadata is - * disregarded) or can use the metadata to set particular fields in the - * objects. - * - * @param fileReader the reader for the input GraphML document. - * @param graphTransformer Transformation function to convert from GraphML GraphMetadata - * to graph objects. This must be non-null. - * @param vertexTransformer Transformation function to convert from GraphML NodeMetadata - * to vertex objects. This must be non-null. - * @param edgeTransformer Transformation function to convert from GraphML EdgeMetadata - * to edge objects. This must be non-null. - * @throws IllegalArgumentException thrown if any of the arguments are null. - */ - public GraphMLReader2(Reader fileReader, - Function graphTransformer, - Function vertexTransformer, - Function edgeTransformer) { - - if (fileReader == null) { - throw new IllegalArgumentException( - "Argument fileReader must be non-null"); - } - - if (graphTransformer == null) { - throw new IllegalArgumentException( - "Argument graphTransformer must be non-null"); - } - - if (vertexTransformer == null) { - throw new IllegalArgumentException( - "Argument vertexTransformer must be non-null"); - } - - if (edgeTransformer == null) { - throw new IllegalArgumentException( - "Argument edgeTransformer must be non-null"); - } - - this.fileReader = fileReader; - this.graphTransformer = graphTransformer; - this.vertexTransformer = vertexTransformer; - this.edgeTransformer = edgeTransformer; - - // Create the parser registry. - this.parserRegistry = new ElementParserRegistry(document.getKeyMap(), - graphTransformer, vertexTransformer, edgeTransformer); // , hyperEdgeTransformer); +public class GraphMLReader2, V, E> implements GraphReader { + + protected XMLEventReader xmlEventReader; + protected Reader fileReader; + protected Function graphTransformer; + protected Function vertexTransformer; + protected Function edgeTransformer; + // protected Function hyperEdgeTransformer; + protected boolean initialized; + protected final GraphMLDocument document = new GraphMLDocument(); + protected final ElementParserRegistry parserRegistry; + private InputStream inputStream; + + /** + * Constructs a GraphML reader around the given reader. This constructor requires the user to + * supply transformation functions to convert from the GraphML metadata to Graph, Vertex, Edge + * instances. These Function functions can be used as purely factories (i.e. the metadata is + * disregarded) or can use the metadata to set particular fields in the objects. + * + * @param fileReader the reader for the input GraphML document. + * @param graphTransformer Transformation function to convert from GraphML GraphMetadata to graph + * objects. This must be non-null. + * @param vertexTransformer Transformation function to convert from GraphML NodeMetadata to vertex + * objects. This must be non-null. + * @param edgeTransformer Transformation function to convert from GraphML EdgeMetadata to edge + * objects. This must be non-null. + * @throws IllegalArgumentException thrown if any of the arguments are null. + */ + public GraphMLReader2( + Reader fileReader, + Function graphTransformer, + Function vertexTransformer, + Function edgeTransformer) { + + if (fileReader == null) { + throw new IllegalArgumentException("Argument fileReader must be non-null"); } - /** - * Constructs a GraphML reader around the given reader. This constructor - * requires the user to supply transformation functions to convert from the - * GraphML metadata to Graph, Vertex, Edge instances. These Function - * functions can be used as purely factories (i.e. the metadata is - * disregarded) or can use the metadata to set particular fields in the - * objects. - * - * @param inputStream the inputstream for the input GraphML document. - * @param graphTransformer Transformation function to convert from GraphML GraphMetadata - * to graph objects. This must be non-null. - * @param vertexTransformer Transformation function to convert from GraphML NodeMetadata - * to vertex objects. This must be non-null. - * @param edgeTransformer Transformation function to convert from GraphML EdgeMetadata - * to edge objects. This must be non-null. - * @param hyperEdgeTransformer Transformation function to convert from GraphML - * HyperEdgeMetadata to edge objects. This must be non-null. - * @throws IllegalArgumentException thrown if any of the arguments are null. - */ - public GraphMLReader2(InputStream inputStream, - Function graphTransformer, - Function vertexTransformer, - Function edgeTransformer) { // -// Function hyperEdgeTransformer) { - - if (inputStream == null) { - throw new IllegalArgumentException( - "Argument inputStream must be non-null"); - } - - if (graphTransformer == null) { - throw new IllegalArgumentException( - "Argument graphTransformer must be non-null"); - } - - if (vertexTransformer == null) { - throw new IllegalArgumentException( - "Argument vertexTransformer must be non-null"); - } - - if (edgeTransformer == null) { - throw new IllegalArgumentException( - "Argument edgeTransformer must be non-null"); - } - -// if (hyperEdgeTransformer == null) { -// throw new IllegalArgumentException( -// "Argument hyperEdgeTransformer must be non-null"); -// } - - this.inputStream = inputStream; - this.graphTransformer = graphTransformer; - this.vertexTransformer = vertexTransformer; - this.edgeTransformer = edgeTransformer; -// this.hyperEdgeTransformer = hyperEdgeTransformer; - - // Create the parser registry. - this.parserRegistry = new ElementParserRegistry(document.getKeyMap(), - graphTransformer, vertexTransformer, edgeTransformer); // , hyperEdgeTransformer); + if (graphTransformer == null) { + throw new IllegalArgumentException("Argument graphTransformer must be non-null"); } - /** - * Gets the current Function that is being used for graph objects. - * - * @return the current Function. - */ - public Function getGraphTransformer() { - return graphTransformer; + if (vertexTransformer == null) { + throw new IllegalArgumentException("Argument vertexTransformer must be non-null"); } - /** - * Gets the current Function that is being used for vertex objects. - * - * @return the current Function. - */ - public Function getVertexTransformer() { - return vertexTransformer; + if (edgeTransformer == null) { + throw new IllegalArgumentException("Argument edgeTransformer must be non-null"); } - /** - * Gets the current Function that is being used for edge objects. - * - * @return the current Function. - */ - public Function getEdgeTransformer() { - return edgeTransformer; + this.fileReader = fileReader; + this.graphTransformer = graphTransformer; + this.vertexTransformer = vertexTransformer; + this.edgeTransformer = edgeTransformer; + + // Create the parser registry. + this.parserRegistry = + new ElementParserRegistry( + document.getKeyMap(), + graphTransformer, + vertexTransformer, + edgeTransformer); // , hyperEdgeTransformer); + } + + /** + * Constructs a GraphML reader around the given reader. This constructor requires the user to + * supply transformation functions to convert from the GraphML metadata to Graph, Vertex, Edge + * instances. These Function functions can be used as purely factories (i.e. the metadata is + * disregarded) or can use the metadata to set particular fields in the objects. + * + * @param inputStream the inputstream for the input GraphML document. + * @param graphTransformer Transformation function to convert from GraphML GraphMetadata to graph + * objects. This must be non-null. + * @param vertexTransformer Transformation function to convert from GraphML NodeMetadata to vertex + * objects. This must be non-null. + * @param edgeTransformer Transformation function to convert from GraphML EdgeMetadata to edge + * objects. This must be non-null. + * @param hyperEdgeTransformer Transformation function to convert from GraphML HyperEdgeMetadata + * to edge objects. This must be non-null. + * @throws IllegalArgumentException thrown if any of the arguments are null. + */ + public GraphMLReader2( + InputStream inputStream, + Function graphTransformer, + Function vertexTransformer, + Function edgeTransformer) { // + // Function hyperEdgeTransformer) { + + if (inputStream == null) { + throw new IllegalArgumentException("Argument inputStream must be non-null"); } -// /** -// * Gets the current Function that is being used for hyperedge objects. -// * -// * @return the current Function. -// */ -// public Function getHyperEdgeTransformer() { -// return hyperEdgeTransformer; -// } - - /** - * Verifies the object state and initializes this reader. All Function - * properties must be set and be non-null or a GraphReaderException - * will be thrown. This method may be called more than once. - * Successive calls will have no effect. - * - * @throws edu.uci.ics.jung.io.GraphIOException thrown if an error occurred. - */ - public void init() throws GraphIOException { - - try { - - if (!initialized) { - - // Create the event reader. - XMLInputFactory Supplier = XMLInputFactory.newInstance(); - if(fileReader==null && inputStream != null) { - xmlEventReader = Supplier.createXMLEventReader(inputStream); - } else { - xmlEventReader = Supplier.createXMLEventReader(fileReader); - } - xmlEventReader = Supplier.createFilteredReader(xmlEventReader, - new GraphMLEventFilter()); - - initialized = true; - } - - } catch( Exception e ) { - ExceptionConverter.convert(e); - } + if (graphTransformer == null) { + throw new IllegalArgumentException("Argument graphTransformer must be non-null"); } - /** - * Closes the GraphML reader and disposes of any resources. - * - * @throws edu.uci.ics.jung.io.GraphIOException thrown if an error occurs. - */ - public void close() throws GraphIOException { - try { - - // Clear the contents of the document. - document.clear(); - - if (xmlEventReader != null) { - xmlEventReader.close(); - } - - if (fileReader != null) { - fileReader.close(); - } - - if (inputStream != null) { - inputStream.close(); - } - - } catch (IOException e) { - throw new GraphIOException(e); - } catch (XMLStreamException e) { - throw new GraphIOException(e); - } finally { - fileReader = null; - inputStream = null; - xmlEventReader = null; - graphTransformer = null; - vertexTransformer = null; - edgeTransformer = null; -// hyperEdgeTransformer = null; - } + if (vertexTransformer == null) { + throw new IllegalArgumentException("Argument vertexTransformer must be non-null"); } - /** - * Returns the object that contains the metadata read in from the GraphML - * document - * - * @return the GraphML document - */ - public GraphMLDocument getGraphMLDocument() { - return document; + if (edgeTransformer == null) { + throw new IllegalArgumentException("Argument edgeTransformer must be non-null"); } - /** - * Reads a single graph object from the GraphML document. Automatically - * calls init to initialize the state of the reader. - * - * @return the graph that was read if one was found, otherwise null. - */ - @SuppressWarnings("unchecked") - public G readGraph() throws GraphIOException { + // if (hyperEdgeTransformer == null) { + // throw new IllegalArgumentException( + // "Argument hyperEdgeTransformer must be non-null"); + // } + + this.inputStream = inputStream; + this.graphTransformer = graphTransformer; + this.vertexTransformer = vertexTransformer; + this.edgeTransformer = edgeTransformer; + // this.hyperEdgeTransformer = hyperEdgeTransformer; + + // Create the parser registry. + this.parserRegistry = + new ElementParserRegistry( + document.getKeyMap(), + graphTransformer, + vertexTransformer, + edgeTransformer); // , hyperEdgeTransformer); + } + + /** + * Gets the current Function that is being used for graph objects. + * + * @return the current Function. + */ + public Function getGraphTransformer() { + return graphTransformer; + } + + /** + * Gets the current Function that is being used for vertex objects. + * + * @return the current Function. + */ + public Function getVertexTransformer() { + return vertexTransformer; + } + + /** + * Gets the current Function that is being used for edge objects. + * + * @return the current Function. + */ + public Function getEdgeTransformer() { + return edgeTransformer; + } + + // /** + // * Gets the current Function that is being used for hyperedge objects. + // * + // * @return the current Function. + // */ + // public Function getHyperEdgeTransformer() { + // return hyperEdgeTransformer; + // } + + /** + * Verifies the object state and initializes this reader. All Function properties must be set and + * be non-null or a GraphReaderException + * will be thrown. This method may be called more than once. Successive calls will have no + * effect. + * + * @throws edu.uci.ics.jung.io.GraphIOException thrown if an error occurred. + */ + public void init() throws GraphIOException { + + try { + + if (!initialized) { + + // Create the event reader. + XMLInputFactory Supplier = XMLInputFactory.newInstance(); + if (fileReader == null && inputStream != null) { + xmlEventReader = Supplier.createXMLEventReader(inputStream); + } else { + xmlEventReader = Supplier.createXMLEventReader(fileReader); + } + xmlEventReader = Supplier.createFilteredReader(xmlEventReader, new GraphMLEventFilter()); - try { + initialized = true; + } - // Initialize if not already. - init(); + } catch (Exception e) { + ExceptionConverter.convert(e); + } + } + + /** + * Closes the GraphML reader and disposes of any resources. + * + * @throws edu.uci.ics.jung.io.GraphIOException thrown if an error occurs. + */ + public void close() throws GraphIOException { + try { + + // Clear the contents of the document. + document.clear(); + + if (xmlEventReader != null) { + xmlEventReader.close(); + } + + if (fileReader != null) { + fileReader.close(); + } + + if (inputStream != null) { + inputStream.close(); + } + + } catch (IOException e) { + throw new GraphIOException(e); + } catch (XMLStreamException e) { + throw new GraphIOException(e); + } finally { + fileReader = null; + inputStream = null; + xmlEventReader = null; + graphTransformer = null; + vertexTransformer = null; + edgeTransformer = null; + // hyperEdgeTransformer = null; + } + } - while (xmlEventReader.hasNext()) { + /** + * Returns the object that contains the metadata read in from the GraphML document + * + * @return the GraphML document + */ + public GraphMLDocument getGraphMLDocument() { + return document; + } - XMLEvent event = xmlEventReader.nextEvent(); - if (event.isStartElement()) { - StartElement element = (StartElement) event; - String name = element.getName().getLocalPart(); + /** + * Reads a single graph object from the GraphML document. Automatically calls init to + * initialize the state of the reader. + * + * @return the graph that was read if one was found, otherwise null. + */ + @SuppressWarnings("unchecked") + public G readGraph() throws GraphIOException { - // The element should be one of: key, graph, graphml - if (GraphMLConstants.KEY_NAME.equals(name)) { + try { - // Parse the key object. - Key key = (Key) parserRegistry.getParser(name).parse( - xmlEventReader, element); + // Initialize if not already. + init(); - // Add the key to the key map. - document.getKeyMap().addKey(key); + while (xmlEventReader.hasNext()) { - } else if (GraphMLConstants.GRAPH_NAME.equals(name)) { + XMLEvent event = xmlEventReader.nextEvent(); + if (event.isStartElement()) { + StartElement element = (StartElement) event; + String name = element.getName().getLocalPart(); - // Parse the graph. - GraphMetadata graph = (GraphMetadata) parserRegistry - .getParser(name).parse(xmlEventReader, element); + // The element should be one of: key, graph, graphml + if (GraphMLConstants.KEY_NAME.equals(name)) { - // Add it to the graph metadata list. - document.getGraphMetadata().add(graph); - - // Return the graph object. - return (G)graph.getGraph(); + // Parse the key object. + Key key = (Key) parserRegistry.getParser(name).parse(xmlEventReader, element); - } else if (GraphMLConstants.GRAPHML_NAME.equals(name)) { - // Ignore the graphML object. - } else { + // Add the key to the key map. + document.getKeyMap().addKey(key); - // Encounted an unknown element - just skip by it. - parserRegistry.getUnknownElementParser().parse( - xmlEventReader, element); - } + } else if (GraphMLConstants.GRAPH_NAME.equals(name)) { - } else if (event.isEndDocument()) { - break; - } - } + // Parse the graph. + GraphMetadata graph = + (GraphMetadata) parserRegistry.getParser(name).parse(xmlEventReader, element); - } catch (Exception e) { - ExceptionConverter.convert(e); + // Add it to the graph metadata list. + document.getGraphMetadata().add(graph); + + // Return the graph object. + return (G) graph.getGraph(); + + } else if (GraphMLConstants.GRAPHML_NAME.equals(name)) { + // Ignore the graphML object. + } else { + + // Encounted an unknown element - just skip by it. + parserRegistry.getUnknownElementParser().parse(xmlEventReader, element); + } + + } else if (event.isEndDocument()) { + break; } + } + + } catch (Exception e) { + ExceptionConverter.convert(e); + } - // We didn't read anything from the document. - throw new GraphIOException("Unable to read Graph from document - the document could be empty"); - } + // We didn't read anything from the document. + throw new GraphIOException("Unable to read Graph from document - the document could be empty"); + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMetadata.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMetadata.java index 8ef0a8f6..e9f041fb 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMetadata.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/GraphMetadata.java @@ -15,150 +15,140 @@ /** * Metadata structure for the 'graph' GraphML element. - * + * * @author Nathan Mittler - nathan.mittler@gmail.com - * * @see "http://graphml.graphdrawing.org/specification.html" */ public class GraphMetadata extends AbstractMetadata { - public enum EdgeDefault { - DIRECTED, UNDIRECTED - } - - private String id; - private EdgeDefault edgeDefault; - private String description; - private Object graph; - final private Map nodes = new HashMap(); - final private Map edges = new HashMap(); - final private Map hyperEdges = new HashMap(); - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public EdgeDefault getEdgeDefault() { - return edgeDefault; - } - - public void setEdgeDefault(EdgeDefault edgeDefault) { - this.edgeDefault = edgeDefault; - } - - public String getDescription() { - return description; - } - - public void setDescription(String desc) { - this.description = desc; - } - - public void addNodeMetadata(Object vertex, NodeMetadata metadata) { - nodes.put(vertex, metadata); - } - - public NodeMetadata getNodeMetadata(Object vertex) { - return nodes.get(vertex); - } - - public Map getNodeMap() { - return nodes; - } - - public void addEdgeMetadata(Object edge, EdgeMetadata metadata) { - edges.put(edge, metadata); - } - - public EdgeMetadata getEdgeMetadata(Object edge) { - return edges.get(edge); - } - - public Map getEdgeMap() { - return edges; - } - - public void addHyperEdgeMetadata(Object edge, HyperEdgeMetadata metadata) { - hyperEdges.put(edge, metadata); - } - - public HyperEdgeMetadata getHyperEdgeMetadata(Object edge) { - return hyperEdges.get(edge); - } - - public Map getHyperEdgeMap() { - return hyperEdges; - } - - public Object getGraph() { - return graph; - } - - public void setGraph(Object graph) { - this.graph = graph; - } - - public MetadataType getMetadataType() { - return MetadataType.GRAPH; - } - - /** - * Gets the property for the given vertex object. - * - * @param vertex - * the subject vertex - * @param key - * the property key - * @return the property value - * @throws IllegalArgumentException - * thrown if there is no metadata associated with the provided - * vertex object. - */ - public String getVertexProperty(Object vertex, String key) - throws IllegalArgumentException { - NodeMetadata metadata = getNodeMetadata(vertex); - if (metadata == null) { - throw new IllegalArgumentException( - "Metadata does not exist for provided vertex"); - } - - return metadata.getProperty(key); - } - - /** - * Gets the property for the given edge object. - * - * @param edge - * the subject edge. - * @param key - * the property key - * @return the property value - * @throws IllegalArgumentException - * thrown if there is no metadata associated with the provided - * edge object. - */ - public String getEdgeProperty(Object edge, String key) - throws IllegalArgumentException { - - // First, try standard edges. - EdgeMetadata em = getEdgeMetadata(edge); - if (em != null) { - return em.getProperty(key); - } - - // Next, try hyperedges. - HyperEdgeMetadata hem = getHyperEdgeMetadata(edge); - if (hem != null) { - return hem.getProperty(key); - } - - // Couldn't find the edge. - throw new IllegalArgumentException( - "Metadata does not exist for provided edge"); - } - + public enum EdgeDefault { + DIRECTED, + UNDIRECTED + } + + private String id; + private EdgeDefault edgeDefault; + private String description; + private Object graph; + private final Map nodes = new HashMap(); + private final Map edges = new HashMap(); + private final Map hyperEdges = + new HashMap(); + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public EdgeDefault getEdgeDefault() { + return edgeDefault; + } + + public void setEdgeDefault(EdgeDefault edgeDefault) { + this.edgeDefault = edgeDefault; + } + + public String getDescription() { + return description; + } + + public void setDescription(String desc) { + this.description = desc; + } + + public void addNodeMetadata(Object vertex, NodeMetadata metadata) { + nodes.put(vertex, metadata); + } + + public NodeMetadata getNodeMetadata(Object vertex) { + return nodes.get(vertex); + } + + public Map getNodeMap() { + return nodes; + } + + public void addEdgeMetadata(Object edge, EdgeMetadata metadata) { + edges.put(edge, metadata); + } + + public EdgeMetadata getEdgeMetadata(Object edge) { + return edges.get(edge); + } + + public Map getEdgeMap() { + return edges; + } + + public void addHyperEdgeMetadata(Object edge, HyperEdgeMetadata metadata) { + hyperEdges.put(edge, metadata); + } + + public HyperEdgeMetadata getHyperEdgeMetadata(Object edge) { + return hyperEdges.get(edge); + } + + public Map getHyperEdgeMap() { + return hyperEdges; + } + + public Object getGraph() { + return graph; + } + + public void setGraph(Object graph) { + this.graph = graph; + } + + public MetadataType getMetadataType() { + return MetadataType.GRAPH; + } + + /** + * Gets the property for the given vertex object. + * + * @param vertex the subject vertex + * @param key the property key + * @return the property value + * @throws IllegalArgumentException thrown if there is no metadata associated with the provided + * vertex object. + */ + public String getVertexProperty(Object vertex, String key) throws IllegalArgumentException { + NodeMetadata metadata = getNodeMetadata(vertex); + if (metadata == null) { + throw new IllegalArgumentException("Metadata does not exist for provided vertex"); + } + + return metadata.getProperty(key); + } + + /** + * Gets the property for the given edge object. + * + * @param edge the subject edge. + * @param key the property key + * @return the property value + * @throws IllegalArgumentException thrown if there is no metadata associated with the provided + * edge object. + */ + public String getEdgeProperty(Object edge, String key) throws IllegalArgumentException { + + // First, try standard edges. + EdgeMetadata em = getEdgeMetadata(edge); + if (em != null) { + return em.getProperty(key); + } + + // Next, try hyperedges. + HyperEdgeMetadata hem = getHyperEdgeMetadata(edge); + if (hem != null) { + return hem.getProperty(key); + } + + // Couldn't find the edge. + throw new IllegalArgumentException("Metadata does not exist for provided edge"); + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/HyperEdgeMetadata.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/HyperEdgeMetadata.java index e0db5276..c49b3312 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/HyperEdgeMetadata.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/HyperEdgeMetadata.java @@ -17,50 +17,48 @@ * Metadata structure for the 'hyperedge' GraphML element. * * @author Nathan Mittler - nathan.mittler@gmail.com - * * @see "http://graphml.graphdrawing.org/specification.html" */ public class HyperEdgeMetadata extends AbstractMetadata { - private String id; - private String description; - private Object edge; - final private List endpoints = new ArrayList(); - - public String getId() { - return id; - } + private String id; + private String description; + private Object edge; + private final List endpoints = new ArrayList(); + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } - public void setId(String id) { - this.id = id; - } + public String getDescription() { + return description; + } - public String getDescription() { - return description; - } + public void setDescription(String description) { + this.description = description; + } - public void setDescription(String description) { - this.description = description; - } + public void addEndpoint(EndpointMetadata endpoint) { + endpoints.add(endpoint); + } - public void addEndpoint( EndpointMetadata endpoint ) { - endpoints.add(endpoint); - } - - public List getEndpoints() { - return endpoints; - } + public List getEndpoints() { + return endpoints; + } - public Object getEdge() { - return edge; - } + public Object getEdge() { + return edge; + } - public void setEdge(Object edge) { - this.edge = edge; - } + public void setEdge(Object edge) { + this.edge = edge; + } - public MetadataType getMetadataType() { - return MetadataType.HYPEREDGE; - } - + public MetadataType getMetadataType() { + return MetadataType.HYPEREDGE; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/Key.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/Key.java index fbd7ae0d..0c750bd2 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/Key.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/Key.java @@ -19,77 +19,83 @@ */ public class Key { - /** - * Enumeration for the 'for' type of this key. The for property indicates - * which elements (e.g. graph, node, edge) this key applies to. - */ - public enum ForType { - ALL, GRAPH, NODE, EDGE, HYPEREDGE, PORT, ENDPOINT - } - - private String id; - private String description; - private String attributeName; - private String attributeType; - private String defaultValue; - private ForType forType = ForType.ALL; - - public String getDescription() { - return description; - } + /** + * Enumeration for the 'for' type of this key. The for property indicates which elements (e.g. + * graph, node, edge) this key applies to. + */ + public enum ForType { + ALL, + GRAPH, + NODE, + EDGE, + HYPEREDGE, + PORT, + ENDPOINT + } - public void setDescription(String description) { - this.description = description; - } + private String id; + private String description; + private String attributeName; + private String attributeType; + private String defaultValue; + private ForType forType = ForType.ALL; - public String getAttributeName() { - return attributeName; - } + public String getDescription() { + return description; + } - public void setAttributeName(String attributeName) { - this.attributeName = attributeName; - } + public void setDescription(String description) { + this.description = description; + } - public String getAttributeType() { - return attributeType; - } + public String getAttributeName() { + return attributeName; + } - public void setAttributeType(String attributeType) { - this.attributeType = attributeType; - } + public void setAttributeName(String attributeName) { + this.attributeName = attributeName; + } - public String getDefaultValue() { - return defaultValue; - } + public String getAttributeType() { + return attributeType; + } - public void setDefaultValue(String defaultValue) { - this.defaultValue = defaultValue; - } + public void setAttributeType(String attributeType) { + this.attributeType = attributeType; + } - public void setId(String id) { - this.id = id; - } + public String getDefaultValue() { + return defaultValue; + } - public void setForType(ForType forType) { - this.forType = forType; - } + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } - public String getId() { - return this.id; - } - - public String defaultValue() { - return this.defaultValue; - } - - public ForType getForType() { - return this.forType; - } + public void setId(String id) { + this.id = id; + } + + public void setForType(ForType forType) { + this.forType = forType; + } + + public String getId() { + return this.id; + } + + public String defaultValue() { + return this.defaultValue; + } + + public ForType getForType() { + return this.forType; + } - public void applyKey( Metadata metadata ) { - Map props = metadata.getProperties(); - if( defaultValue != null && !props.containsKey(id) ) { - props.put(id, defaultValue); - } + public void applyKey(Metadata metadata) { + Map props = metadata.getProperties(); + if (defaultValue != null && !props.containsKey(id)) { + props.put(id, defaultValue); } + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/KeyMap.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/KeyMap.java index 2739fa6f..84cfc1f1 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/KeyMap.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/KeyMap.java @@ -17,107 +17,111 @@ import java.util.Set; /** - * A KeyMap is a storage mechanism for the keys read from the GraphML file. It - * stores the keys indexed by the type of GraphML metadata (node, edge, etc) - * that the key applies to. The applyKeys method will obtain the - * list of keys that apply to the given metadata type and apply the keys - * one-by-one to the metadata. + * A KeyMap is a storage mechanism for the keys read from the GraphML file. It stores the keys + * indexed by the type of GraphML metadata (node, edge, etc) that the key applies to. The + * applyKeys method will obtain the list of keys that apply to the given metadata type and + * apply the keys one-by-one to the metadata. * * @author Nathan Mittler - nathan.mittler@gmail.com */ public class KeyMap { - final private Map> map = new HashMap>(); + private final Map> map = + new HashMap>(); - /** - * Adds the given key to the map. - * - * @param key the key to be added. - */ - public void addKey(Key key) { + /** + * Adds the given key to the map. + * + * @param key the key to be added. + */ + public void addKey(Key key) { - switch (key.getForType()) { - case EDGE: { - getKeyList(Metadata.MetadataType.EDGE).add(key); - break; - } - case ENDPOINT: { - getKeyList(Metadata.MetadataType.ENDPOINT).add(key); - break; - } - case GRAPH: { - getKeyList(Metadata.MetadataType.GRAPH).add(key); - break; - } - case HYPEREDGE: { - getKeyList(Metadata.MetadataType.HYPEREDGE).add(key); - break; - } - case NODE: { - getKeyList(Metadata.MetadataType.NODE).add(key); - break; - } - case PORT: { - getKeyList(Metadata.MetadataType.PORT).add(key); - break; - } - default: { - - // Default = ALL - getKeyList(Metadata.MetadataType.EDGE).add(key); - getKeyList(Metadata.MetadataType.ENDPOINT).add(key); - getKeyList(Metadata.MetadataType.GRAPH).add(key); - getKeyList(Metadata.MetadataType.HYPEREDGE).add(key); - getKeyList(Metadata.MetadataType.NODE).add(key); - getKeyList(Metadata.MetadataType.PORT).add(key); - } + switch (key.getForType()) { + case EDGE: + { + getKeyList(Metadata.MetadataType.EDGE).add(key); + break; } - } - - /** - * Applies all keys that are applicable to the given metadata. - * - * @param metadata the target metadata. - */ - public void applyKeys(Metadata metadata) { + case ENDPOINT: + { + getKeyList(Metadata.MetadataType.ENDPOINT).add(key); + break; + } + case GRAPH: + { + getKeyList(Metadata.MetadataType.GRAPH).add(key); + break; + } + case HYPEREDGE: + { + getKeyList(Metadata.MetadataType.HYPEREDGE).add(key); + break; + } + case NODE: + { + getKeyList(Metadata.MetadataType.NODE).add(key); + break; + } + case PORT: + { + getKeyList(Metadata.MetadataType.PORT).add(key); + break; + } + default: + { - List keys = getKeyList(metadata.getMetadataType()); - for (Key key : keys) { - key.applyKey(metadata); + // Default = ALL + getKeyList(Metadata.MetadataType.EDGE).add(key); + getKeyList(Metadata.MetadataType.ENDPOINT).add(key); + getKeyList(Metadata.MetadataType.GRAPH).add(key); + getKeyList(Metadata.MetadataType.HYPEREDGE).add(key); + getKeyList(Metadata.MetadataType.NODE).add(key); + getKeyList(Metadata.MetadataType.PORT).add(key); } } + } - /** - * Clears this map. - */ - public void clear() { - map.clear(); - } + /** + * Applies all keys that are applicable to the given metadata. + * + * @param metadata the target metadata. + */ + public void applyKeys(Metadata metadata) { - /** - * Retrieves the set of entries contained in this map. - * - * @return all of the entries in this map. - */ - public Set>> entrySet() { - return map.entrySet(); + List keys = getKeyList(metadata.getMetadataType()); + for (Key key : keys) { + key.applyKey(metadata); } + } - /** - * Gets the list for the given metadata type. If doesn't exist, the list is - * created. - * - * @param type the metadata type. - * @return the list for the metadata type. - */ - private List getKeyList(Metadata.MetadataType type) { + /** Clears this map. */ + public void clear() { + map.clear(); + } - List keys = map.get(type); - if (keys == null) { - keys = new ArrayList(); - map.put(type, keys); - } + /** + * Retrieves the set of entries contained in this map. + * + * @return all of the entries in this map. + */ + public Set>> entrySet() { + return map.entrySet(); + } + + /** + * Gets the list for the given metadata type. If doesn't exist, the list is created. + * + * @param type the metadata type. + * @return the list for the metadata type. + */ + private List getKeyList(Metadata.MetadataType type) { - return keys; + List keys = map.get(type); + if (keys == null) { + keys = new ArrayList(); + map.put(type, keys); } + + return keys; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/Metadata.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/Metadata.java index bb8faa14..d17a31ca 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/Metadata.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/Metadata.java @@ -19,25 +19,27 @@ */ public interface Metadata { - /** - * Metadata type enumeration - */ - enum MetadataType { - GRAPH, NODE, EDGE, HYPEREDGE, PORT, ENDPOINT - } + /** Metadata type enumeration */ + enum MetadataType { + GRAPH, + NODE, + EDGE, + HYPEREDGE, + PORT, + ENDPOINT + } - /** - * Gets the metadata type of this object. - * - * @return the metadata type - */ - MetadataType getMetadataType(); + /** + * Gets the metadata type of this object. + * + * @return the metadata type + */ + MetadataType getMetadataType(); - /** - * Gets any properties that were associated with this metadata in the - * GraphML - * - * @return GraphML properties - */ - Map getProperties(); -} \ No newline at end of file + /** + * Gets any properties that were associated with this metadata in the GraphML + * + * @return GraphML properties + */ + Map getProperties(); +} diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/NodeMetadata.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/NodeMetadata.java index 69be3919..0b103aab 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/NodeMetadata.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/NodeMetadata.java @@ -17,50 +17,48 @@ * Metadata structure for the 'node' GraphML element. * * @author Nathan Mittler - nathan.mittler@gmail.com - * * @see "http://graphml.graphdrawing.org/specification.html" */ public class NodeMetadata extends AbstractMetadata { - private String id; - private String description; - private Object vertex; - final private List ports = new ArrayList(); - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getDescription() { - return description; - } - - public void setDescription(String desc) { - this.description = desc; - } - - public void addPort(PortMetadata port) { - ports.add(port); - } - - public List getPorts() { - return ports; - } - - public Object getVertex() { - return vertex; - } + private String id; + private String description; + private Object vertex; + private final List ports = new ArrayList(); - public void setVertex(Object vertex) { - this.vertex = vertex; - } + public String getId() { + return id; + } - public MetadataType getMetadataType() { - return MetadataType.NODE; - } + public void setId(String id) { + this.id = id; + } + public String getDescription() { + return description; + } + + public void setDescription(String desc) { + this.description = desc; + } + + public void addPort(PortMetadata port) { + ports.add(port); + } + + public List getPorts() { + return ports; + } + + public Object getVertex() { + return vertex; + } + + public void setVertex(Object vertex) { + this.vertex = vertex; + } + + public MetadataType getMetadataType() { + return MetadataType.NODE; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/PortMetadata.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/PortMetadata.java index b85e298b..9c403338 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/PortMetadata.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/PortMetadata.java @@ -14,32 +14,30 @@ * Metadata structure for the 'port' GraphML element. * * @author Nathan Mittler - nathan.mittler@gmail.com - * * @see "http://graphml.graphdrawing.org/specification.html" */ public class PortMetadata extends AbstractMetadata { - private String name; - private String description; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String desc) { - this.description = desc; - } - - public MetadataType getMetadataType() { - return MetadataType.PORT; - } + private String name; + private String description; + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String desc) { + this.description = desc; + } + + public MetadataType getMetadataType() { + return MetadataType.PORT; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/AbstractElementParser.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/AbstractElementParser.java index 76bd3243..d396552c 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/AbstractElementParser.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/AbstractElementParser.java @@ -10,52 +10,53 @@ package edu.uci.ics.jung.io.graphml.parser; -import javax.xml.stream.events.EndElement; -import javax.xml.stream.events.StartElement; - import com.google.common.graph.MutableNetwork; - import edu.uci.ics.jung.io.GraphIOException; import edu.uci.ics.jung.io.graphml.Metadata; +import javax.xml.stream.events.EndElement; +import javax.xml.stream.events.StartElement; /** * Base class for element parsers - provides some minimal functionality. - * + * * @author Nathan Mittler - nathan.mittler@gmail.com */ -public abstract class AbstractElementParser,V,E> implements ElementParser { +public abstract class AbstractElementParser, V, E> + implements ElementParser { - final private ParserContext parserContext; - protected AbstractElementParser(ParserContext parserContext) { - this.parserContext = parserContext; - } - - public ParserContext getParserContext() { - return this.parserContext; - } - - public ElementParser getParser(String localName) { - return parserContext.getElementParserRegistry().getParser(localName); - } - - public void applyKeys(Metadata metadata) { - getParserContext().getKeyMap().applyKeys(metadata); - } - - public ElementParser getUnknownParser() { - return parserContext.getElementParserRegistry().getUnknownElementParser(); - } - - protected void verifyMatch(StartElement start, EndElement end) - throws GraphIOException { - - String startName = start.getName().getLocalPart(); - String endName = end.getName().getLocalPart(); - if (!startName.equals(endName)) { - throw new GraphIOException( - "Failed parsing document: Start/end tag mismatch! " - + "StartTag:" + startName + ", EndTag: " - + endName); - } + private final ParserContext parserContext; + + protected AbstractElementParser(ParserContext parserContext) { + this.parserContext = parserContext; + } + + public ParserContext getParserContext() { + return this.parserContext; + } + + public ElementParser getParser(String localName) { + return parserContext.getElementParserRegistry().getParser(localName); + } + + public void applyKeys(Metadata metadata) { + getParserContext().getKeyMap().applyKeys(metadata); + } + + public ElementParser getUnknownParser() { + return parserContext.getElementParserRegistry().getUnknownElementParser(); + } + + protected void verifyMatch(StartElement start, EndElement end) throws GraphIOException { + + String startName = start.getName().getLocalPart(); + String endName = end.getName().getLocalPart(); + if (!startName.equals(endName)) { + throw new GraphIOException( + "Failed parsing document: Start/end tag mismatch! " + + "StartTag:" + + startName + + ", EndTag: " + + endName); } + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/DataElementParser.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/DataElementParser.java index 7cce6552..7407b620 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/DataElementParser.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/DataElementParser.java @@ -10,8 +10,12 @@ package edu.uci.ics.jung.io.graphml.parser; +import com.google.common.graph.MutableNetwork; +import edu.uci.ics.jung.io.GraphIOException; +import edu.uci.ics.jung.io.graphml.DataMetadata; +import edu.uci.ics.jung.io.graphml.ExceptionConverter; +import edu.uci.ics.jung.io.graphml.GraphMLConstants; import java.util.Iterator; - import javax.xml.stream.XMLEventReader; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.Characters; @@ -19,76 +23,68 @@ import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; -import com.google.common.graph.MutableNetwork; - -import edu.uci.ics.jung.io.GraphIOException; -import edu.uci.ics.jung.io.graphml.DataMetadata; -import edu.uci.ics.jung.io.graphml.ExceptionConverter; -import edu.uci.ics.jung.io.graphml.GraphMLConstants; - /** * Parses the data element. * * @author Nathan Mittler - nathan.mittler@gmail.com */ -public class DataElementParser,V,E> extends AbstractElementParser { +public class DataElementParser, V, E> + extends AbstractElementParser { - public DataElementParser(ParserContext parserContext) { - super(parserContext); - } - - public DataMetadata parse(XMLEventReader xmlEventReader, StartElement start) - throws GraphIOException { + public DataElementParser(ParserContext parserContext) { + super(parserContext); + } - try { - // Create the new port. - DataMetadata data = new DataMetadata(); + public DataMetadata parse(XMLEventReader xmlEventReader, StartElement start) + throws GraphIOException { - // Parse the attributes. - @SuppressWarnings("unchecked") - Iterator iterator = start.getAttributes(); - while (iterator.hasNext()) { - Attribute attribute = (Attribute) iterator.next(); - String name = attribute.getName().getLocalPart(); - String value = attribute.getValue(); - if (data.getKey() == null && GraphMLConstants.KEY_NAME.equals(name)) { - data.setKey(value); - } - } + try { + // Create the new port. + DataMetadata data = new DataMetadata(); + + // Parse the attributes. + @SuppressWarnings("unchecked") + Iterator iterator = start.getAttributes(); + while (iterator.hasNext()) { + Attribute attribute = (Attribute) iterator.next(); + String name = attribute.getName().getLocalPart(); + String value = attribute.getValue(); + if (data.getKey() == null && GraphMLConstants.KEY_NAME.equals(name)) { + data.setKey(value); + } + } - // Make sure the key has been set. - if (data.getKey() == null) { - throw new GraphIOException( - "Element 'data' is missing attribute 'key'"); - } + // Make sure the key has been set. + if (data.getKey() == null) { + throw new GraphIOException("Element 'data' is missing attribute 'key'"); + } - while (xmlEventReader.hasNext()) { + while (xmlEventReader.hasNext()) { - XMLEvent event = xmlEventReader.nextEvent(); - if (event.isStartElement()) { - StartElement element = (StartElement) event; - - // Treat any child elements as unknown - getUnknownParser().parse(xmlEventReader, element); - } - if (event.isCharacters()) { - Characters characters = (Characters) event; - data.setValue(characters.getData()); - } - if (event.isEndElement()) { - EndElement end = (EndElement) event; - verifyMatch(start, end); - break; - } - } + XMLEvent event = xmlEventReader.nextEvent(); + if (event.isStartElement()) { + StartElement element = (StartElement) event; - return data; - - } catch (Exception e) { - ExceptionConverter.convert(e); + // Treat any child elements as unknown + getUnknownParser().parse(xmlEventReader, element); } + if (event.isCharacters()) { + Characters characters = (Characters) event; + data.setValue(characters.getData()); + } + if (event.isEndElement()) { + EndElement end = (EndElement) event; + verifyMatch(start, end); + break; + } + } + + return data; - return null; + } catch (Exception e) { + ExceptionConverter.convert(e); } -} + return null; + } +} diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/EdgeElementParser.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/EdgeElementParser.java index 5d92a6d7..de25a013 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/EdgeElementParser.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/EdgeElementParser.java @@ -10,106 +10,102 @@ package edu.uci.ics.jung.io.graphml.parser; -import java.util.Iterator; - -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.EndElement; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; - import com.google.common.graph.MutableNetwork; - import edu.uci.ics.jung.io.GraphIOException; import edu.uci.ics.jung.io.graphml.DataMetadata; import edu.uci.ics.jung.io.graphml.EdgeMetadata; import edu.uci.ics.jung.io.graphml.ExceptionConverter; import edu.uci.ics.jung.io.graphml.GraphMLConstants; +import java.util.Iterator; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.EndElement; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; /** * Parses an edge element. * * @author Nathan Mittler - nathan.mittler@gmail.com */ -public class EdgeElementParser,V,E> extends AbstractElementParser { - - public EdgeElementParser(ParserContext parserContext) { - super(parserContext); - } - - public EdgeMetadata parse(XMLEventReader xmlEventReader, StartElement start) - throws GraphIOException { - - try { - // Create the new edge. - EdgeMetadata edge = new EdgeMetadata(); - - // Parse the attributes. - @SuppressWarnings("unchecked") - Iterator iterator = start.getAttributes(); - while (iterator.hasNext()) { - Attribute attribute = iterator.next(); - String name = attribute.getName().getLocalPart(); - String value = attribute.getValue(); - if (edge.getId() == null && GraphMLConstants.ID_NAME.equals(name)) { - edge.setId(value); - } else if (edge.isDirected() == null && GraphMLConstants.DIRECTED_NAME.equals(name)) { - edge.setDirected(("true".equals(value))); - } else if (edge.getSource() == null && GraphMLConstants.SOURCE_NAME.equals(name)) { - edge.setSource(value); - } else if (edge.getTarget() == null && GraphMLConstants.TARGET_NAME.equals(name)) { - edge.setTarget(value); - } else if (edge.getSourcePort() == null && GraphMLConstants.SOURCEPORT_NAME.equals(name)) { - edge.setSourcePort(value); - } else if (edge.getTargetPort() == null && GraphMLConstants.TARGETPORT_NAME.equals(name)) { - edge.setTargetPort(value); - } else { - edge.setProperty(name, value); - } - } - - // Make sure the source and target have been been set. - if (edge.getSource() == null || edge.getTarget() == null) { - throw new GraphIOException( - "Element 'edge' is missing attribute 'source' or 'target'"); - } - - while (xmlEventReader.hasNext()) { - - XMLEvent event = xmlEventReader.nextEvent(); - if (event.isStartElement()) { - StartElement element = (StartElement) event; - - String name = element.getName().getLocalPart(); - if(GraphMLConstants.DESC_NAME.equals(name)) { - String desc = (String)getParser(name).parse(xmlEventReader, element); - edge.setDescription(desc); - } else if(GraphMLConstants.DATA_NAME.equals(name)) { - DataMetadata data = (DataMetadata)getParser(name).parse(xmlEventReader, element); - edge.addData(data); - } else { - - // Treat anything else as unknown - getUnknownParser().parse(xmlEventReader, element); - } - - } - if (event.isEndElement()) { - EndElement end = (EndElement) event; - verifyMatch(start, end); - break; - } - } - - // Apply the keys to this object. - applyKeys(edge); - - return edge; - - } catch (Exception e) { - ExceptionConverter.convert(e); +public class EdgeElementParser, V, E> + extends AbstractElementParser { + + public EdgeElementParser(ParserContext parserContext) { + super(parserContext); + } + + public EdgeMetadata parse(XMLEventReader xmlEventReader, StartElement start) + throws GraphIOException { + + try { + // Create the new edge. + EdgeMetadata edge = new EdgeMetadata(); + + // Parse the attributes. + @SuppressWarnings("unchecked") + Iterator iterator = start.getAttributes(); + while (iterator.hasNext()) { + Attribute attribute = iterator.next(); + String name = attribute.getName().getLocalPart(); + String value = attribute.getValue(); + if (edge.getId() == null && GraphMLConstants.ID_NAME.equals(name)) { + edge.setId(value); + } else if (edge.isDirected() == null && GraphMLConstants.DIRECTED_NAME.equals(name)) { + edge.setDirected(("true".equals(value))); + } else if (edge.getSource() == null && GraphMLConstants.SOURCE_NAME.equals(name)) { + edge.setSource(value); + } else if (edge.getTarget() == null && GraphMLConstants.TARGET_NAME.equals(name)) { + edge.setTarget(value); + } else if (edge.getSourcePort() == null && GraphMLConstants.SOURCEPORT_NAME.equals(name)) { + edge.setSourcePort(value); + } else if (edge.getTargetPort() == null && GraphMLConstants.TARGETPORT_NAME.equals(name)) { + edge.setTargetPort(value); + } else { + edge.setProperty(name, value); } + } + + // Make sure the source and target have been been set. + if (edge.getSource() == null || edge.getTarget() == null) { + throw new GraphIOException("Element 'edge' is missing attribute 'source' or 'target'"); + } + + while (xmlEventReader.hasNext()) { + + XMLEvent event = xmlEventReader.nextEvent(); + if (event.isStartElement()) { + StartElement element = (StartElement) event; - return null; + String name = element.getName().getLocalPart(); + if (GraphMLConstants.DESC_NAME.equals(name)) { + String desc = (String) getParser(name).parse(xmlEventReader, element); + edge.setDescription(desc); + } else if (GraphMLConstants.DATA_NAME.equals(name)) { + DataMetadata data = (DataMetadata) getParser(name).parse(xmlEventReader, element); + edge.addData(data); + } else { + + // Treat anything else as unknown + getUnknownParser().parse(xmlEventReader, element); + } + } + if (event.isEndElement()) { + EndElement end = (EndElement) event; + verifyMatch(start, end); + break; + } + } + + // Apply the keys to this object. + applyKeys(edge); + + return edge; + + } catch (Exception e) { + ExceptionConverter.convert(e); } + + return null; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/ElementParser.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/ElementParser.java index fecbfc5f..b69f32b8 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/ElementParser.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/ElementParser.java @@ -10,19 +10,16 @@ package edu.uci.ics.jung.io.graphml.parser; +import edu.uci.ics.jung.io.GraphIOException; import javax.xml.stream.XMLEventReader; import javax.xml.stream.events.StartElement; -import edu.uci.ics.jung.io.GraphIOException; - /** - * Interface for all element parsers. All parsers will be registered with the registry. + * Interface for all element parsers. All parsers will be registered with the registry. * * @author Nathan Mittler - nathan.mittler@gmail.com - * * @see ElementParserRegistry */ public interface ElementParser { - Object parse(XMLEventReader xmlEventReader, StartElement start) - throws GraphIOException; + Object parse(XMLEventReader xmlEventReader, StartElement start) throws GraphIOException; } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/ElementParserRegistry.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/ElementParserRegistry.java index c49e81e2..5bdd4106 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/ElementParserRegistry.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/ElementParserRegistry.java @@ -10,62 +10,62 @@ package edu.uci.ics.jung.io.graphml.parser; -import java.util.HashMap; -import java.util.Map; - import com.google.common.base.Function; import com.google.common.graph.MutableNetwork; - import edu.uci.ics.jung.io.graphml.EdgeMetadata; import edu.uci.ics.jung.io.graphml.GraphMLConstants; import edu.uci.ics.jung.io.graphml.GraphMetadata; import edu.uci.ics.jung.io.graphml.KeyMap; import edu.uci.ics.jung.io.graphml.NodeMetadata; +import java.util.HashMap; +import java.util.Map; /** * Registry for all element parsers. - * + * * @author Nathan Mittler - nathan.mittler@gmail.com */ public class ElementParserRegistry, V, E> { - final private Map parserMap = new HashMap(); + private final Map parserMap = new HashMap(); - final private ElementParser unknownElementParser = new UnknownElementParser(); + private final ElementParser unknownElementParser = new UnknownElementParser(); - public ElementParserRegistry(KeyMap keyMap, - Function graphTransformer, - Function vertexTransformer, - Function edgeTransformer) { -// Function hyperEdgeTransformer) { - - // Create the parser context. - ParserContext context = new ParserContext(this, keyMap, graphTransformer, - vertexTransformer, edgeTransformer); - - parserMap.put(GraphMLConstants.DEFAULT_NAME, new StringElementParser(context)); - parserMap.put(GraphMLConstants.DESC_NAME, new StringElementParser(context)); - parserMap.put(GraphMLConstants.KEY_NAME, new KeyElementParser(context)); - parserMap.put(GraphMLConstants.DATA_NAME, new DataElementParser(context)); - parserMap.put(GraphMLConstants.PORT_NAME, new PortElementParser(context)); - parserMap.put(GraphMLConstants.NODE_NAME, new NodeElementParser(context)); - parserMap.put(GraphMLConstants.GRAPH_NAME, new GraphElementParser(context)); - parserMap.put(GraphMLConstants.ENDPOINT_NAME, new EndpointElementParser(context)); - parserMap.put(GraphMLConstants.EDGE_NAME, new EdgeElementParser(context)); - // TODO: restore this once we have a Hypergraph type again -// parserMap.put(GraphMLConstants.HYPEREDGE_NAME, new HyperEdgeElementParser(context)); - } + public ElementParserRegistry( + KeyMap keyMap, + Function graphTransformer, + Function vertexTransformer, + Function edgeTransformer) { + // Function hyperEdgeTransformer) { - public ElementParser getUnknownElementParser() { - return unknownElementParser; - } + // Create the parser context. + ParserContext context = + new ParserContext( + this, keyMap, graphTransformer, vertexTransformer, edgeTransformer); - public ElementParser getParser(String localName) { - ElementParser parser = parserMap.get(localName); - if (parser == null) { - parser = unknownElementParser; - } + parserMap.put(GraphMLConstants.DEFAULT_NAME, new StringElementParser(context)); + parserMap.put(GraphMLConstants.DESC_NAME, new StringElementParser(context)); + parserMap.put(GraphMLConstants.KEY_NAME, new KeyElementParser(context)); + parserMap.put(GraphMLConstants.DATA_NAME, new DataElementParser(context)); + parserMap.put(GraphMLConstants.PORT_NAME, new PortElementParser(context)); + parserMap.put(GraphMLConstants.NODE_NAME, new NodeElementParser(context)); + parserMap.put(GraphMLConstants.GRAPH_NAME, new GraphElementParser(context)); + parserMap.put(GraphMLConstants.ENDPOINT_NAME, new EndpointElementParser(context)); + parserMap.put(GraphMLConstants.EDGE_NAME, new EdgeElementParser(context)); + // TODO: restore this once we have a Hypergraph type again + // parserMap.put(GraphMLConstants.HYPEREDGE_NAME, new HyperEdgeElementParser(context)); + } - return parser; + public ElementParser getUnknownElementParser() { + return unknownElementParser; + } + + public ElementParser getParser(String localName) { + ElementParser parser = parserMap.get(localName); + if (parser == null) { + parser = unknownElementParser; } + + return parser; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/EndpointElementParser.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/EndpointElementParser.java index 2f769d52..4cad3444 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/EndpointElementParser.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/EndpointElementParser.java @@ -10,112 +10,113 @@ package edu.uci.ics.jung.io.graphml.parser; +import com.google.common.graph.MutableNetwork; +import edu.uci.ics.jung.io.GraphIOException; +import edu.uci.ics.jung.io.graphml.EndpointMetadata; +import edu.uci.ics.jung.io.graphml.EndpointMetadata.EndpointType; +import edu.uci.ics.jung.io.graphml.ExceptionConverter; +import edu.uci.ics.jung.io.graphml.GraphMLConstants; import java.util.HashMap; import java.util.Iterator; import java.util.Map; - import javax.xml.stream.XMLEventReader; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; -import com.google.common.graph.MutableNetwork; - -import edu.uci.ics.jung.io.GraphIOException; -import edu.uci.ics.jung.io.graphml.EndpointMetadata; -import edu.uci.ics.jung.io.graphml.EndpointMetadata.EndpointType; -import edu.uci.ics.jung.io.graphml.ExceptionConverter; -import edu.uci.ics.jung.io.graphml.GraphMLConstants; - /** * Parses endpoint elements. - * + * * @author Nathan Mittler - nathan.mittler@gmail.com */ -public class EndpointElementParser,V,E> extends AbstractElementParser { - - final static private Map endpointTypeMap = new HashMap(); - static { - endpointTypeMap.put(GraphMLConstants.IN_NAME, EndpointType.IN); - endpointTypeMap.put(GraphMLConstants.OUT_NAME, EndpointType.OUT); - endpointTypeMap.put(GraphMLConstants.UNDIR_NAME, EndpointType.UNDIR); - } - - public EndpointElementParser(ParserContext parserContext) { - super(parserContext); - } - - public EndpointMetadata parse(XMLEventReader xmlEventReader, StartElement start) - throws GraphIOException { - - try { - // Create the new endpoint. - EndpointMetadata endpoint = new EndpointMetadata(); - - // Parse the attributes. - @SuppressWarnings("unchecked") - Iterator iterator = start.getAttributes(); - while (iterator.hasNext()) { - Attribute attribute = iterator.next(); - String name = attribute.getName().getLocalPart(); - String value = attribute.getValue(); - if (endpoint.getId() == null && GraphMLConstants.ID_NAME.equals(name)) { - endpoint.setId(value); - } if (endpoint.getPort() == null && GraphMLConstants.PORT_NAME.equals(name)) { - endpoint.setPort(value); - } if (endpoint.getNode() == null && GraphMLConstants.NODE_NAME.equals(name)) { - endpoint.setNode(value); - } if (GraphMLConstants.TYPE_NAME.equals(name)) { - EndpointType t = endpointTypeMap.get(value); - if( t == null ) { - t = EndpointType.UNDIR; - } - endpoint.setEndpointType(t); - } else { - endpoint.setProperty(name, value); - } - } - - // Make sure the node has been set. - if (endpoint.getNode() == null) { - throw new GraphIOException( - "Element 'endpoint' is missing attribute 'node'"); - } - - while (xmlEventReader.hasNext()) { - - XMLEvent event = xmlEventReader.nextEvent(); - if (event.isStartElement()) { - StartElement element = (StartElement) event; - - String name = element.getName().getLocalPart(); - if(GraphMLConstants.DESC_NAME.equals(name)) { - String desc = (String)getParser(name).parse(xmlEventReader, element); - endpoint.setDescription(desc); - } else { - - // Treat anything else as unknown - getUnknownParser().parse(xmlEventReader, element); - } - - } - if (event.isEndElement()) { - EndElement end = (EndElement) event; - verifyMatch(start, end); - break; - } - } - - // Apply the keys to this object. - applyKeys(endpoint); - - return endpoint; - - } catch (Exception e) { - ExceptionConverter.convert(e); +public class EndpointElementParser, V, E> + extends AbstractElementParser { + + private static final Map endpointTypeMap = + new HashMap(); + + static { + endpointTypeMap.put(GraphMLConstants.IN_NAME, EndpointType.IN); + endpointTypeMap.put(GraphMLConstants.OUT_NAME, EndpointType.OUT); + endpointTypeMap.put(GraphMLConstants.UNDIR_NAME, EndpointType.UNDIR); + } + + public EndpointElementParser(ParserContext parserContext) { + super(parserContext); + } + + public EndpointMetadata parse(XMLEventReader xmlEventReader, StartElement start) + throws GraphIOException { + + try { + // Create the new endpoint. + EndpointMetadata endpoint = new EndpointMetadata(); + + // Parse the attributes. + @SuppressWarnings("unchecked") + Iterator iterator = start.getAttributes(); + while (iterator.hasNext()) { + Attribute attribute = iterator.next(); + String name = attribute.getName().getLocalPart(); + String value = attribute.getValue(); + if (endpoint.getId() == null && GraphMLConstants.ID_NAME.equals(name)) { + endpoint.setId(value); + } + if (endpoint.getPort() == null && GraphMLConstants.PORT_NAME.equals(name)) { + endpoint.setPort(value); + } + if (endpoint.getNode() == null && GraphMLConstants.NODE_NAME.equals(name)) { + endpoint.setNode(value); + } + if (GraphMLConstants.TYPE_NAME.equals(name)) { + EndpointType t = endpointTypeMap.get(value); + if (t == null) { + t = EndpointType.UNDIR; + } + endpoint.setEndpointType(t); + } else { + endpoint.setProperty(name, value); + } + } + + // Make sure the node has been set. + if (endpoint.getNode() == null) { + throw new GraphIOException("Element 'endpoint' is missing attribute 'node'"); + } + + while (xmlEventReader.hasNext()) { + + XMLEvent event = xmlEventReader.nextEvent(); + if (event.isStartElement()) { + StartElement element = (StartElement) event; + + String name = element.getName().getLocalPart(); + if (GraphMLConstants.DESC_NAME.equals(name)) { + String desc = (String) getParser(name).parse(xmlEventReader, element); + endpoint.setDescription(desc); + } else { + + // Treat anything else as unknown + getUnknownParser().parse(xmlEventReader, element); + } } + if (event.isEndElement()) { + EndElement end = (EndElement) event; + verifyMatch(start, end); + break; + } + } + + // Apply the keys to this object. + applyKeys(endpoint); - return null; + return endpoint; + + } catch (Exception e) { + ExceptionConverter.convert(e); } + + return null; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/GraphElementParser.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/GraphElementParser.java index 110055a0..c7222a49 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/GraphElementParser.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/GraphElementParser.java @@ -10,240 +10,235 @@ package edu.uci.ics.jung.io.graphml.parser; +import com.google.common.graph.MutableNetwork; +import edu.uci.ics.jung.io.GraphIOException; +import edu.uci.ics.jung.io.graphml.DataMetadata; +import edu.uci.ics.jung.io.graphml.EdgeMetadata; +import edu.uci.ics.jung.io.graphml.ExceptionConverter; +import edu.uci.ics.jung.io.graphml.GraphMLConstants; +import edu.uci.ics.jung.io.graphml.GraphMetadata; +import edu.uci.ics.jung.io.graphml.GraphMetadata.EdgeDefault; +import edu.uci.ics.jung.io.graphml.NodeMetadata; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; - import javax.xml.stream.XMLEventReader; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; -import com.google.common.graph.MutableNetwork; - -import edu.uci.ics.jung.io.GraphIOException; -import edu.uci.ics.jung.io.graphml.DataMetadata; -import edu.uci.ics.jung.io.graphml.EdgeMetadata; -import edu.uci.ics.jung.io.graphml.ExceptionConverter; -import edu.uci.ics.jung.io.graphml.GraphMLConstants; -import edu.uci.ics.jung.io.graphml.GraphMetadata; -import edu.uci.ics.jung.io.graphml.GraphMetadata.EdgeDefault; -import edu.uci.ics.jung.io.graphml.NodeMetadata; - /** * Parses graph elements. * * @author Nathan Mittler - nathan.mittler@gmail.com */ -public class GraphElementParser,V,E> extends AbstractElementParser { +public class GraphElementParser, V, E> + extends AbstractElementParser { + + public GraphElementParser(ParserContext parserContext) { + super(parserContext); + } + + public GraphMetadata parse(XMLEventReader xmlEventReader, StartElement start) + throws GraphIOException { + + try { + // Create the new graph. + GraphMetadata graphMetadata = new GraphMetadata(); + + // Parse the attributes. + @SuppressWarnings("unchecked") + Iterator iterator = start.getAttributes(); + while (iterator.hasNext()) { + Attribute attribute = iterator.next(); + String name = attribute.getName().getLocalPart(); + String value = attribute.getValue(); + if (graphMetadata.getId() == null && GraphMLConstants.ID_NAME.equals(name)) { + + graphMetadata.setId(value); + } else if (graphMetadata.getEdgeDefault() == null + && GraphMLConstants.EDGEDEFAULT_NAME.equals(name)) { + + graphMetadata.setEdgeDefault( + GraphMLConstants.DIRECTED_NAME.equals(value) + ? EdgeDefault.DIRECTED + : EdgeDefault.UNDIRECTED); + } else { + graphMetadata.setProperty(name, value); + } + } - public GraphElementParser(ParserContext parserContext) { - super(parserContext); - } + // Make sure the graphdefault has been set. + if (graphMetadata.getEdgeDefault() == null) { + throw new GraphIOException("Element 'graph' is missing attribute 'edgedefault'"); + } - public GraphMetadata parse(XMLEventReader xmlEventReader, StartElement start) - throws GraphIOException { - - try { - // Create the new graph. - GraphMetadata graphMetadata = new GraphMetadata(); - - // Parse the attributes. - @SuppressWarnings("unchecked") - Iterator iterator = start.getAttributes(); - while (iterator.hasNext()) { - Attribute attribute = iterator.next(); - String name = attribute.getName().getLocalPart(); - String value = attribute.getValue(); - if (graphMetadata.getId() == null - && GraphMLConstants.ID_NAME.equals(name)) { - - graphMetadata.setId(value); - } else if (graphMetadata.getEdgeDefault() == null - && GraphMLConstants.EDGEDEFAULT_NAME.equals(name)) { - - graphMetadata.setEdgeDefault(GraphMLConstants.DIRECTED_NAME - .equals(value) ? EdgeDefault.DIRECTED - : EdgeDefault.UNDIRECTED); - } else { - graphMetadata.setProperty(name, value); - } - } + Map idToVertexMap = new HashMap(); + Collection edgeMetadata = new LinkedList(); + // Collection hyperEdgeMetadata = new LinkedList(); - // Make sure the graphdefault has been set. - if (graphMetadata.getEdgeDefault() == null) { - throw new GraphIOException( - "Element 'graph' is missing attribute 'edgedefault'"); - } - - Map idToVertexMap = new HashMap(); - Collection edgeMetadata = new LinkedList(); -// Collection hyperEdgeMetadata = new LinkedList(); - - while (xmlEventReader.hasNext()) { - - XMLEvent event = xmlEventReader.nextEvent(); - if (event.isStartElement()) { - StartElement element = (StartElement) event; - - String name = element.getName().getLocalPart(); - if (GraphMLConstants.DESC_NAME.equals(name)) { - - // Parse the description and set it in the graph. - String desc = (String) getParser(name).parse( - xmlEventReader, element); - graphMetadata.setDescription(desc); - - } else if (GraphMLConstants.DATA_NAME.equals(name)) { - - // Parse the data element and store the property in the graph. - DataMetadata data = (DataMetadata) getParser(name).parse( - xmlEventReader, element); - graphMetadata.addData(data); - - } else if (GraphMLConstants.NODE_NAME.equals(name)) { - - // Parse the node metadata - NodeMetadata metadata = (NodeMetadata) getParser(name).parse( - xmlEventReader, element); - - // Create the vertex object and store it in the metadata - V vertex = getParserContext().createVertex(metadata); - metadata.setVertex(vertex); - idToVertexMap.put(metadata.getId(), vertex); - - // Add it to the graph - graphMetadata.addNodeMetadata(vertex, metadata); - - } else if (GraphMLConstants.EDGE_NAME.equals(name)) { - - // Parse the edge metadata - EdgeMetadata metadata = (EdgeMetadata) getParser(name).parse( - xmlEventReader, element); - - // Set the directed property if not overridden. - if (metadata.isDirected() == null) { - metadata.setDirected(graphMetadata.getEdgeDefault() == EdgeDefault.DIRECTED); - } - - // Create the edge object and store it in the metadata - E edge = getParserContext().createEdge(metadata); - edgeMetadata.add(metadata); - metadata.setEdge(edge); - - // Add it to the graph. - graphMetadata.addEdgeMetadata(edge, metadata); - -// } else if (GraphMLConstants.HYPEREDGE_NAME.equals(name)) { -// -// // Parse the edge metadata -// HyperEdgeMetadata metadata = (HyperEdgeMetadata) getParser(name).parse( -// xmlEventReader, element); -// -// // Create the edge object and store it in the metadata -// E edge = getParserContext().createHyperEdge(metadata); -// hyperEdgeMetadata.add(metadata); -// metadata.setEdge(edge); -// -// // Add it to the graph -// graphMetadata.addHyperEdgeMetadata(edge, metadata); - - } else { - - // Treat anything else as unknown - getUnknownParser().parse(xmlEventReader, element); - } - - } - if (event.isEndElement()) { - EndElement end = (EndElement) event; - verifyMatch(start, end); - break; - } - } - - // Apply the keys to this object. - applyKeys(graphMetadata); - - // Create the graph object and store it in the metadata - G graph = getParserContext().createGraph(graphMetadata); - graphMetadata.setGraph(graph); - - // Add all of the vertices to the graph object. - addVerticesToGraph(graph, idToVertexMap.values()); - - // Add the edges to the graph object. - addEdgesToGraph(graph, edgeMetadata, idToVertexMap); -// addHyperEdgesToGraph(graph, hyperEdgeMetadata, idToVertexMap); - - return graphMetadata; - - } catch (Exception e) { - ExceptionConverter.convert(e); - } + while (xmlEventReader.hasNext()) { - return null; - } - - private void addVerticesToGraph(G graph, Collection vertices) { - - for (V vertex : vertices) { - graph.addNode(vertex); - } - } - - @SuppressWarnings("unchecked") - private void addEdgesToGraph(G graph, Collection metadata, - Map idToVertexMap) throws GraphIOException { - - for (EdgeMetadata emd : metadata) { - - // Get the edge out of the metadata - E edge = (E)emd.getEdge(); - - // Get the vertices. - V source = idToVertexMap.get(emd.getSource()); - V target = idToVertexMap.get(emd.getTarget()); - if (source == null || target == null) { - throw new GraphIOException( - "edge references undefined source or target vertex. " - + "Source: " + emd.getSource() - + ", Target: " + emd.getTarget()); + XMLEvent event = xmlEventReader.nextEvent(); + if (event.isStartElement()) { + StartElement element = (StartElement) event; + + String name = element.getName().getLocalPart(); + if (GraphMLConstants.DESC_NAME.equals(name)) { + + // Parse the description and set it in the graph. + String desc = (String) getParser(name).parse(xmlEventReader, element); + graphMetadata.setDescription(desc); + + } else if (GraphMLConstants.DATA_NAME.equals(name)) { + + // Parse the data element and store the property in the graph. + DataMetadata data = (DataMetadata) getParser(name).parse(xmlEventReader, element); + graphMetadata.addData(data); + + } else if (GraphMLConstants.NODE_NAME.equals(name)) { + + // Parse the node metadata + NodeMetadata metadata = (NodeMetadata) getParser(name).parse(xmlEventReader, element); + + // Create the vertex object and store it in the metadata + V vertex = getParserContext().createVertex(metadata); + metadata.setVertex(vertex); + idToVertexMap.put(metadata.getId(), vertex); + + // Add it to the graph + graphMetadata.addNodeMetadata(vertex, metadata); + + } else if (GraphMLConstants.EDGE_NAME.equals(name)) { + + // Parse the edge metadata + EdgeMetadata metadata = (EdgeMetadata) getParser(name).parse(xmlEventReader, element); + + // Set the directed property if not overridden. + if (metadata.isDirected() == null) { + metadata.setDirected(graphMetadata.getEdgeDefault() == EdgeDefault.DIRECTED); } + // Create the edge object and store it in the metadata + E edge = getParserContext().createEdge(metadata); + edgeMetadata.add(metadata); + metadata.setEdge(edge); + // Add it to the graph. - graph.addEdge(source, target, edge); + graphMetadata.addEdgeMetadata(edge, metadata); + + // } else if (GraphMLConstants.HYPEREDGE_NAME.equals(name)) { + // + // // Parse the edge metadata + // HyperEdgeMetadata metadata = (HyperEdgeMetadata) getParser(name).parse( + // xmlEventReader, element); + // + // // Create the edge object and store it in the metadata + // E edge = getParserContext().createHyperEdge(metadata); + // hyperEdgeMetadata.add(metadata); + // metadata.setEdge(edge); + // + // // Add it to the graph + // graphMetadata.addHyperEdgeMetadata(edge, metadata); + + } else { + + // Treat anything else as unknown + getUnknownParser().parse(xmlEventReader, element); + } + } + if (event.isEndElement()) { + EndElement end = (EndElement) event; + verifyMatch(start, end); + break; } + } + + // Apply the keys to this object. + applyKeys(graphMetadata); + + // Create the graph object and store it in the metadata + G graph = getParserContext().createGraph(graphMetadata); + graphMetadata.setGraph(graph); + + // Add all of the vertices to the graph object. + addVerticesToGraph(graph, idToVertexMap.values()); + + // Add the edges to the graph object. + addEdgesToGraph(graph, edgeMetadata, idToVertexMap); + // addHyperEdgesToGraph(graph, hyperEdgeMetadata, idToVertexMap); + + return graphMetadata; + + } catch (Exception e) { + ExceptionConverter.convert(e); + } + + return null; + } + + private void addVerticesToGraph(G graph, Collection vertices) { + + for (V vertex : vertices) { + graph.addNode(vertex); + } + } + + @SuppressWarnings("unchecked") + private void addEdgesToGraph( + G graph, Collection metadata, Map idToVertexMap) + throws GraphIOException { + + for (EdgeMetadata emd : metadata) { + + // Get the edge out of the metadata + E edge = (E) emd.getEdge(); + + // Get the vertices. + V source = idToVertexMap.get(emd.getSource()); + V target = idToVertexMap.get(emd.getTarget()); + if (source == null || target == null) { + throw new GraphIOException( + "edge references undefined source or target vertex. " + + "Source: " + + emd.getSource() + + ", Target: " + + emd.getTarget()); + } + + // Add it to the graph. + graph.addEdge(source, target, edge); } - - // TODO: hypergraph support -// @SuppressWarnings("unchecked") -// private void addHyperEdgesToGraph(G graph, Collection metadata, -// Map idToVertexMap) throws GraphIOException { -// -// for (HyperEdgeMetadata emd : metadata) { -// -// // Get the edge out of the metadata -// E edge = (E)emd.getEdge(); -// -// // Add the verticies to a list. -// List verticies = new ArrayList(); -// List endpoints = emd.getEndpoints(); -// for (EndpointMetadata ep : endpoints) { -// V v = idToVertexMap.get(ep.getNode()); -// if (v == null) { -// throw new GraphIOException( -// "hyperedge references undefined vertex: " -// + ep.getNode()); -// } -// verticies.add(v); -// } -// -// // Add it to the graph. -// graph.addEdge(edge, verticies); -// } -// } + } + + // TODO: hypergraph support + // @SuppressWarnings("unchecked") + // private void addHyperEdgesToGraph(G graph, Collection metadata, + // Map idToVertexMap) throws GraphIOException { + // + // for (HyperEdgeMetadata emd : metadata) { + // + // // Get the edge out of the metadata + // E edge = (E)emd.getEdge(); + // + // // Add the verticies to a list. + // List verticies = new ArrayList(); + // List endpoints = emd.getEndpoints(); + // for (EndpointMetadata ep : endpoints) { + // V v = idToVertexMap.get(ep.getNode()); + // if (v == null) { + // throw new GraphIOException( + // "hyperedge references undefined vertex: " + // + ep.getNode()); + // } + // verticies.add(v); + // } + // + // // Add it to the graph. + // graph.addEdge(edge, verticies); + // } + // } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/GraphMLEventFilter.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/GraphMLEventFilter.java index e1b57037..13143203 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/GraphMLEventFilter.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/GraphMLEventFilter.java @@ -19,23 +19,24 @@ * * @author Nathan Mittler - nathan.mittler@gmail.com */ -public class GraphMLEventFilter implements EventFilter { +public class GraphMLEventFilter implements EventFilter { - public boolean accept(XMLEvent event) { - switch( event.getEventType() ) { - case XMLStreamConstants.START_ELEMENT: - case XMLStreamConstants.END_ELEMENT: - case XMLStreamConstants.CHARACTERS: - case XMLStreamConstants.ATTRIBUTE: - case XMLStreamConstants.NAMESPACE: - case XMLStreamConstants.START_DOCUMENT: - case XMLStreamConstants.END_DOCUMENT: { - return true; - } - default: { - return false; + public boolean accept(XMLEvent event) { + switch (event.getEventType()) { + case XMLStreamConstants.START_ELEMENT: + case XMLStreamConstants.END_ELEMENT: + case XMLStreamConstants.CHARACTERS: + case XMLStreamConstants.ATTRIBUTE: + case XMLStreamConstants.NAMESPACE: + case XMLStreamConstants.START_DOCUMENT: + case XMLStreamConstants.END_DOCUMENT: + { + return true; } + default: + { + return false; } } - + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/KeyElementParser.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/KeyElementParser.java index bc1d4a33..d62d99b7 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/KeyElementParser.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/KeyElementParser.java @@ -10,125 +10,118 @@ package edu.uci.ics.jung.io.graphml.parser; +import com.google.common.graph.MutableNetwork; +import edu.uci.ics.jung.io.GraphIOException; +import edu.uci.ics.jung.io.graphml.ExceptionConverter; +import edu.uci.ics.jung.io.graphml.GraphMLConstants; +import edu.uci.ics.jung.io.graphml.Key; import java.util.Iterator; - import javax.xml.stream.XMLEventReader; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; -import com.google.common.graph.MutableNetwork; - -import edu.uci.ics.jung.io.GraphIOException; -import edu.uci.ics.jung.io.graphml.ExceptionConverter; -import edu.uci.ics.jung.io.graphml.GraphMLConstants; -import edu.uci.ics.jung.io.graphml.Key; - /** * Parses key elements. * * @author Nathan Mittler - nathan.mittler@gmail.com */ -public class KeyElementParser,V,E> extends AbstractElementParser { - - public KeyElementParser(ParserContext parserContext) { - super(parserContext); - } - - public Key parse(XMLEventReader xmlEventReader, StartElement start) - throws GraphIOException { - - try { - // Create the new key. ForType defaults to ALL. - Key key = new Key(); - - // Parse the attributes. - @SuppressWarnings("unchecked") - Iterator iterator = start.getAttributes(); - while (iterator.hasNext()) { - Attribute attribute = iterator.next(); - String name = attribute.getName().getLocalPart(); - String value = attribute.getValue(); - if (key.getId() == null && GraphMLConstants.ID_NAME.equals(name)) { - key.setId(value); - } else if (key.getAttributeName() == null - && GraphMLConstants.ATTRNAME_NAME.equals(name)) { - key.setAttributeName(value); - } else if (key.getAttributeType() == null - && GraphMLConstants.ATTRTYPE_NAME.equals(name)) { - key.setAttributeType(value); - } else if (GraphMLConstants.FOR_NAME.equals(name)) { - key.setForType(convertFor(value)); - } - } - - // Make sure the id has been set. - if (key.getId() == null) { - throw new GraphIOException( - "Element 'key' is missing attribute 'id'"); - } - - while (xmlEventReader.hasNext()) { - - XMLEvent event = xmlEventReader.nextEvent(); - if (event.isStartElement()) { - StartElement element = (StartElement) event; - - String name = element.getName().getLocalPart(); - if(GraphMLConstants.DESC_NAME.equals(name)) { - String desc = (String)getParser(name).parse(xmlEventReader, element); - key.setDescription(desc); - } else if(GraphMLConstants.DEFAULT_NAME.equals(name)) { - String defaultValue = (String)getParser(name).parse(xmlEventReader, element); - key.setDefaultValue(defaultValue); - } else { - - // Treat anything else as unknown - getUnknownParser().parse(xmlEventReader, element); - } - - } - if (event.isEndElement()) { - EndElement end = (EndElement) event; - verifyMatch(start, end); - break; - } - } - - return key; - - } catch (Exception e) { - ExceptionConverter.convert(e); +public class KeyElementParser, V, E> + extends AbstractElementParser { + + public KeyElementParser(ParserContext parserContext) { + super(parserContext); + } + + public Key parse(XMLEventReader xmlEventReader, StartElement start) throws GraphIOException { + + try { + // Create the new key. ForType defaults to ALL. + Key key = new Key(); + + // Parse the attributes. + @SuppressWarnings("unchecked") + Iterator iterator = start.getAttributes(); + while (iterator.hasNext()) { + Attribute attribute = iterator.next(); + String name = attribute.getName().getLocalPart(); + String value = attribute.getValue(); + if (key.getId() == null && GraphMLConstants.ID_NAME.equals(name)) { + key.setId(value); + } else if (key.getAttributeName() == null && GraphMLConstants.ATTRNAME_NAME.equals(name)) { + key.setAttributeName(value); + } else if (key.getAttributeType() == null && GraphMLConstants.ATTRTYPE_NAME.equals(name)) { + key.setAttributeType(value); + } else if (GraphMLConstants.FOR_NAME.equals(name)) { + key.setForType(convertFor(value)); + } + } + + // Make sure the id has been set. + if (key.getId() == null) { + throw new GraphIOException("Element 'key' is missing attribute 'id'"); + } + + while (xmlEventReader.hasNext()) { + + XMLEvent event = xmlEventReader.nextEvent(); + if (event.isStartElement()) { + StartElement element = (StartElement) event; + + String name = element.getName().getLocalPart(); + if (GraphMLConstants.DESC_NAME.equals(name)) { + String desc = (String) getParser(name).parse(xmlEventReader, element); + key.setDescription(desc); + } else if (GraphMLConstants.DEFAULT_NAME.equals(name)) { + String defaultValue = (String) getParser(name).parse(xmlEventReader, element); + key.setDefaultValue(defaultValue); + } else { + + // Treat anything else as unknown + getUnknownParser().parse(xmlEventReader, element); + } } + if (event.isEndElement()) { + EndElement end = (EndElement) event; + verifyMatch(start, end); + break; + } + } - return null; - } + return key; - static public Key.ForType convertFor(String value) { - - if (value != null) { - - if (GraphMLConstants.GRAPH_NAME.equals(value)) { - return Key.ForType.GRAPH; - } - if (GraphMLConstants.EDGE_NAME.equals(value)) { - return Key.ForType.EDGE; - } - if (GraphMLConstants.ENDPOINT_NAME.equals(value)) { - return Key.ForType.ENDPOINT; - } - if (GraphMLConstants.HYPEREDGE_NAME.equals(value)) { - return Key.ForType.HYPEREDGE; - } - if (GraphMLConstants.NODE_NAME.equals(value)) { - return Key.ForType.NODE; - } - if (GraphMLConstants.PORT_NAME.equals(value)) { - return Key.ForType.PORT; - } - } + } catch (Exception e) { + ExceptionConverter.convert(e); + } - return Key.ForType.ALL; + return null; + } + + public static Key.ForType convertFor(String value) { + + if (value != null) { + + if (GraphMLConstants.GRAPH_NAME.equals(value)) { + return Key.ForType.GRAPH; + } + if (GraphMLConstants.EDGE_NAME.equals(value)) { + return Key.ForType.EDGE; + } + if (GraphMLConstants.ENDPOINT_NAME.equals(value)) { + return Key.ForType.ENDPOINT; + } + if (GraphMLConstants.HYPEREDGE_NAME.equals(value)) { + return Key.ForType.HYPEREDGE; + } + if (GraphMLConstants.NODE_NAME.equals(value)) { + return Key.ForType.NODE; + } + if (GraphMLConstants.PORT_NAME.equals(value)) { + return Key.ForType.PORT; + } } + + return Key.ForType.ALL; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/NodeElementParser.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/NodeElementParser.java index 1e539cfb..3404162f 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/NodeElementParser.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/NodeElementParser.java @@ -10,99 +10,96 @@ package edu.uci.ics.jung.io.graphml.parser; -import java.util.Iterator; - -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.EndElement; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; - import com.google.common.graph.MutableNetwork; - import edu.uci.ics.jung.io.GraphIOException; import edu.uci.ics.jung.io.graphml.DataMetadata; import edu.uci.ics.jung.io.graphml.ExceptionConverter; import edu.uci.ics.jung.io.graphml.GraphMLConstants; import edu.uci.ics.jung.io.graphml.NodeMetadata; import edu.uci.ics.jung.io.graphml.PortMetadata; +import java.util.Iterator; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.EndElement; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; /** * Parses node elements. * * @author Nathan Mittler - nathan.mittler@gmail.com */ -public class NodeElementParser,V,E> extends AbstractElementParser { - - public NodeElementParser(ParserContext parserContext) { - super(parserContext); - } - - @SuppressWarnings("unchecked") - public NodeMetadata parse(XMLEventReader xmlEventReader, StartElement start) - throws GraphIOException { - - try { - // Create the new node. - NodeMetadata node = new NodeMetadata(); - - // Parse the attributes. - Iterator iterator = start.getAttributes(); - while (iterator.hasNext()) { - Attribute attribute = iterator.next(); - String name = attribute.getName().getLocalPart(); - String value = attribute.getValue(); - if (node.getId() == null && GraphMLConstants.ID_NAME.equals(name)) { - node.setId(value); - } else { - node.setProperty(name, value); - } - } - - // Make sure the name has been set. - if (node.getId() == null) { - throw new GraphIOException( - "Element 'node' is missing attribute 'id'"); - } - - while (xmlEventReader.hasNext()) { - - XMLEvent event = xmlEventReader.nextEvent(); - if (event.isStartElement()) { - StartElement element = (StartElement) event; - - String name = element.getName().getLocalPart(); - if(GraphMLConstants.DESC_NAME.equals(name)) { - String desc = (String)getParser(name).parse(xmlEventReader, element); - node.setDescription(desc); - } else if(GraphMLConstants.DATA_NAME.equals(name)) { - DataMetadata data = (DataMetadata)getParser(name).parse(xmlEventReader, element); - node.addData(data); - } else if(GraphMLConstants.PORT_NAME.equals(name)) { - PortMetadata port = (PortMetadata)getParser(name).parse(xmlEventReader, element); - node.addPort(port); - } else { - - // Treat anything else as unknown - getUnknownParser().parse(xmlEventReader, element); - } - - } else if (event.isEndElement()) { - EndElement end = (EndElement) event; - verifyMatch(start, end); - break; - } - } - - // Apply the keys to this object. - applyKeys(node); - - return node; - - } catch (Exception e) { - ExceptionConverter.convert(e); +public class NodeElementParser, V, E> + extends AbstractElementParser { + + public NodeElementParser(ParserContext parserContext) { + super(parserContext); + } + + @SuppressWarnings("unchecked") + public NodeMetadata parse(XMLEventReader xmlEventReader, StartElement start) + throws GraphIOException { + + try { + // Create the new node. + NodeMetadata node = new NodeMetadata(); + + // Parse the attributes. + Iterator iterator = start.getAttributes(); + while (iterator.hasNext()) { + Attribute attribute = iterator.next(); + String name = attribute.getName().getLocalPart(); + String value = attribute.getValue(); + if (node.getId() == null && GraphMLConstants.ID_NAME.equals(name)) { + node.setId(value); + } else { + node.setProperty(name, value); + } + } + + // Make sure the name has been set. + if (node.getId() == null) { + throw new GraphIOException("Element 'node' is missing attribute 'id'"); + } + + while (xmlEventReader.hasNext()) { + + XMLEvent event = xmlEventReader.nextEvent(); + if (event.isStartElement()) { + StartElement element = (StartElement) event; + + String name = element.getName().getLocalPart(); + if (GraphMLConstants.DESC_NAME.equals(name)) { + String desc = (String) getParser(name).parse(xmlEventReader, element); + node.setDescription(desc); + } else if (GraphMLConstants.DATA_NAME.equals(name)) { + DataMetadata data = (DataMetadata) getParser(name).parse(xmlEventReader, element); + node.addData(data); + } else if (GraphMLConstants.PORT_NAME.equals(name)) { + PortMetadata port = (PortMetadata) getParser(name).parse(xmlEventReader, element); + node.addPort(port); + } else { + + // Treat anything else as unknown + getUnknownParser().parse(xmlEventReader, element); + } + + } else if (event.isEndElement()) { + EndElement end = (EndElement) event; + verifyMatch(start, end); + break; } + } + + // Apply the keys to this object. + applyKeys(node); - return null; + return node; + + } catch (Exception e) { + ExceptionConverter.convert(e); } + + return null; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/ParserContext.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/ParserContext.java index 64125013..9f71e006 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/ParserContext.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/ParserContext.java @@ -12,65 +12,64 @@ import com.google.common.base.Function; import com.google.common.graph.MutableNetwork; - import edu.uci.ics.jung.io.graphml.EdgeMetadata; import edu.uci.ics.jung.io.graphml.GraphMetadata; import edu.uci.ics.jung.io.graphml.KeyMap; import edu.uci.ics.jung.io.graphml.NodeMetadata; /** - * Provides resources related to the current parsing context. - * - * @author Nathan Mittler - nathan.mittler@gmail.com + * Provides resources related to the current parsing context. * + * @author Nathan Mittler - nathan.mittler@gmail.com * @param The graph type * @param The vertex type * @param The edge type */ public class ParserContext, V, E> { - private final KeyMap keyMap; - private final ElementParserRegistry elementParserRegistry; - private final Function graphTransformer; - private final Function vertexTransformer; - private final Function edgeTransformer; -// private final Function hyperEdgeTransformer; - - public ParserContext(ElementParserRegistry elementParserRegistry, - KeyMap keyMap, - Function graphTransformer, - Function vertexTransformer, - Function edgeTransformer) { -// Function hyperEdgeTransformer ) { - this.elementParserRegistry = elementParserRegistry; - this.keyMap = keyMap; - this.graphTransformer = graphTransformer; - this.vertexTransformer = vertexTransformer; - this.edgeTransformer = edgeTransformer; -// this.hyperEdgeTransformer = hyperEdgeTransformer; - } + private final KeyMap keyMap; + private final ElementParserRegistry elementParserRegistry; + private final Function graphTransformer; + private final Function vertexTransformer; + private final Function edgeTransformer; + // private final Function hyperEdgeTransformer; + + public ParserContext( + ElementParserRegistry elementParserRegistry, + KeyMap keyMap, + Function graphTransformer, + Function vertexTransformer, + Function edgeTransformer) { + // Function hyperEdgeTransformer ) { + this.elementParserRegistry = elementParserRegistry; + this.keyMap = keyMap; + this.graphTransformer = graphTransformer; + this.vertexTransformer = vertexTransformer; + this.edgeTransformer = edgeTransformer; + // this.hyperEdgeTransformer = hyperEdgeTransformer; + } + + public ElementParserRegistry getElementParserRegistry() { + return elementParserRegistry; + } + + public KeyMap getKeyMap() { + return keyMap; + } + + public G createGraph(GraphMetadata metadata) { + return graphTransformer.apply(metadata); + } + + public V createVertex(NodeMetadata metadata) { + return vertexTransformer.apply(metadata); + } + + public E createEdge(EdgeMetadata metadata) { + return edgeTransformer.apply(metadata); + } - public ElementParserRegistry getElementParserRegistry() { - return elementParserRegistry; - } - - public KeyMap getKeyMap() { - return keyMap; - } - - public G createGraph(GraphMetadata metadata) { - return graphTransformer.apply(metadata); - } - - public V createVertex(NodeMetadata metadata) { - return vertexTransformer.apply(metadata); - } - - public E createEdge(EdgeMetadata metadata) { - return edgeTransformer.apply(metadata); - } - -// public E createHyperEdge(HyperEdgeMetadata metadata) { -// return hyperEdgeTransformer.apply(metadata); -// } + // public E createHyperEdge(HyperEdgeMetadata metadata) { + // return hyperEdgeTransformer.apply(metadata); + // } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/PortElementParser.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/PortElementParser.java index c156d667..7e9bf2ad 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/PortElementParser.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/PortElementParser.java @@ -10,97 +10,93 @@ package edu.uci.ics.jung.io.graphml.parser; -import java.util.Iterator; - -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.EndElement; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; - import com.google.common.graph.MutableNetwork; - import edu.uci.ics.jung.io.GraphIOException; import edu.uci.ics.jung.io.graphml.DataMetadata; import edu.uci.ics.jung.io.graphml.ExceptionConverter; import edu.uci.ics.jung.io.graphml.GraphMLConstants; import edu.uci.ics.jung.io.graphml.PortMetadata; +import java.util.Iterator; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.EndElement; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; /** * Parses port elements. * * @author Nathan Mittler - nathan.mittler@gmail.com */ -public class PortElementParser,V,E> extends AbstractElementParser { - - public PortElementParser(ParserContext parserContext) { - super(parserContext); - } - - public PortMetadata parse(XMLEventReader xmlEventReader, StartElement start) - throws GraphIOException { - - try { - - // Create the new port. - PortMetadata port = new PortMetadata(); - - // Parse the attributes. - @SuppressWarnings("unchecked") - Iterator iterator = start.getAttributes(); - while (iterator.hasNext()) { - Attribute attribute = iterator.next(); - String name = attribute.getName().getLocalPart(); - String value = attribute.getValue(); - if (port.getName() == null && GraphMLConstants.NAME_NAME.equals(name)) { - port.setName(value); - } else { - port.setProperty(name, value); - } - } - - // Make sure the name has been set. - if (port.getName() == null) { - throw new GraphIOException( - "Element 'port' is missing attribute 'name'"); - } - - while (xmlEventReader.hasNext()) { - - XMLEvent event = xmlEventReader.nextEvent(); - if (event.isStartElement()) { - StartElement element = (StartElement) event; - - String name = element.getName().getLocalPart(); - if(GraphMLConstants.DESC_NAME.equals(name)) { - String desc = (String)getParser(name).parse(xmlEventReader, element); - port.setDescription(desc); - } else if(GraphMLConstants.DATA_NAME.equals(name)) { - DataMetadata data = (DataMetadata)getParser(name).parse(xmlEventReader, element); - port.addData(data); - } else { - - // Treat anything else as unknown - getUnknownParser().parse(xmlEventReader, element); - } - - } - if (event.isEndElement()) { - EndElement end = (EndElement) event; - verifyMatch(start, end); - break; - } - } - - // Apply the keys to this port. - applyKeys(port); - - return port; - - } catch (Exception e) { - ExceptionConverter.convert(e); +public class PortElementParser, V, E> + extends AbstractElementParser { + + public PortElementParser(ParserContext parserContext) { + super(parserContext); + } + + public PortMetadata parse(XMLEventReader xmlEventReader, StartElement start) + throws GraphIOException { + + try { + + // Create the new port. + PortMetadata port = new PortMetadata(); + + // Parse the attributes. + @SuppressWarnings("unchecked") + Iterator iterator = start.getAttributes(); + while (iterator.hasNext()) { + Attribute attribute = iterator.next(); + String name = attribute.getName().getLocalPart(); + String value = attribute.getValue(); + if (port.getName() == null && GraphMLConstants.NAME_NAME.equals(name)) { + port.setName(value); + } else { + port.setProperty(name, value); } + } + + // Make sure the name has been set. + if (port.getName() == null) { + throw new GraphIOException("Element 'port' is missing attribute 'name'"); + } + + while (xmlEventReader.hasNext()) { + + XMLEvent event = xmlEventReader.nextEvent(); + if (event.isStartElement()) { + StartElement element = (StartElement) event; + + String name = element.getName().getLocalPart(); + if (GraphMLConstants.DESC_NAME.equals(name)) { + String desc = (String) getParser(name).parse(xmlEventReader, element); + port.setDescription(desc); + } else if (GraphMLConstants.DATA_NAME.equals(name)) { + DataMetadata data = (DataMetadata) getParser(name).parse(xmlEventReader, element); + port.addData(data); + } else { + + // Treat anything else as unknown + getUnknownParser().parse(xmlEventReader, element); + } + } + if (event.isEndElement()) { + EndElement end = (EndElement) event; + verifyMatch(start, end); + break; + } + } + + // Apply the keys to this port. + applyKeys(port); - return null; + return port; + + } catch (Exception e) { + ExceptionConverter.convert(e); } + + return null; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/StringElementParser.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/StringElementParser.java index ebf54615..34ce63dd 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/StringElementParser.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/StringElementParser.java @@ -10,58 +10,55 @@ package edu.uci.ics.jung.io.graphml.parser; +import com.google.common.graph.MutableNetwork; +import edu.uci.ics.jung.io.GraphIOException; +import edu.uci.ics.jung.io.graphml.ExceptionConverter; import javax.xml.stream.XMLEventReader; import javax.xml.stream.events.Characters; import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; -import com.google.common.graph.MutableNetwork; - -import edu.uci.ics.jung.io.GraphIOException; -import edu.uci.ics.jung.io.graphml.ExceptionConverter; - /** * Parses an element that just contains text. * * @author Nathan Mittler - nathan.mittler@gmail.com */ -public class StringElementParser,V,E> extends AbstractElementParser { - - public StringElementParser(ParserContext parserContext) { - super(parserContext); - } - - public String parse(XMLEventReader xmlEventReader, StartElement start) - throws GraphIOException { +public class StringElementParser, V, E> + extends AbstractElementParser { - try { - String str = null; + public StringElementParser(ParserContext parserContext) { + super(parserContext); + } - while (xmlEventReader.hasNext()) { + public String parse(XMLEventReader xmlEventReader, StartElement start) throws GraphIOException { - XMLEvent event = xmlEventReader.nextEvent(); - if (event.isStartElement()) { + try { + String str = null; - // Parse the unknown element. - getUnknownParser().parse(xmlEventReader, event - .asStartElement()); - } else if (event.isEndElement()) { - EndElement end = (EndElement) event; - verifyMatch(start, end); - break; - } else if (event.isCharacters()) { - Characters characters = (Characters) event; - str = characters.getData(); - } - } + while (xmlEventReader.hasNext()) { - return str; + XMLEvent event = xmlEventReader.nextEvent(); + if (event.isStartElement()) { - } catch (Exception e) { - ExceptionConverter.convert(e); + // Parse the unknown element. + getUnknownParser().parse(xmlEventReader, event.asStartElement()); + } else if (event.isEndElement()) { + EndElement end = (EndElement) event; + verifyMatch(start, end); + break; + } else if (event.isCharacters()) { + Characters characters = (Characters) event; + str = characters.getData(); } + } + + return str; - return null; + } catch (Exception e) { + ExceptionConverter.convert(e); } + + return null; + } } diff --git a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/UnknownElementParser.java b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/UnknownElementParser.java index 42b2fc8e..66b6609c 100644 --- a/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/UnknownElementParser.java +++ b/jung-io/src/main/java/edu/uci/ics/jung/io/graphml/parser/UnknownElementParser.java @@ -10,15 +10,13 @@ package edu.uci.ics.jung.io.graphml.parser; +import edu.uci.ics.jung.io.GraphIOException; +import edu.uci.ics.jung.io.graphml.ExceptionConverter; import java.util.Stack; - import javax.xml.stream.XMLEventReader; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; -import edu.uci.ics.jung.io.GraphIOException; -import edu.uci.ics.jung.io.graphml.ExceptionConverter; - /** * Skips an entire unknown subtree of the XML * @@ -26,58 +24,52 @@ */ public class UnknownElementParser implements ElementParser { - /** - * Skips an entire subtree starting with the provided unknown element. - * - * @param xmlEventReader - * the event reader - * @param start - * the unknown element to be skipped. - * @return null - */ - public Object parse(XMLEventReader xmlEventReader, StartElement start) - throws GraphIOException { + /** + * Skips an entire subtree starting with the provided unknown element. + * + * @param xmlEventReader the event reader + * @param start the unknown element to be skipped. + * @return null + */ + public Object parse(XMLEventReader xmlEventReader, StartElement start) throws GraphIOException { - try { - Stack skippedElements = new Stack(); - skippedElements.add(start.getName().getLocalPart()); + try { + Stack skippedElements = new Stack(); + skippedElements.add(start.getName().getLocalPart()); - while (xmlEventReader.hasNext()) { + while (xmlEventReader.hasNext()) { - XMLEvent event = xmlEventReader.nextEvent(); - if (event.isStartElement()) { + XMLEvent event = xmlEventReader.nextEvent(); + if (event.isStartElement()) { - String name = event.asStartElement().getName() - .getLocalPart(); + String name = event.asStartElement().getName().getLocalPart(); - // Push the name of the unknown element. - skippedElements.push(name); - } - if (event.isEndElement()) { - - String name = event.asEndElement().getName() - .getLocalPart(); - - if (skippedElements.size() == 0 - || !skippedElements.peek().equals(name)) { - throw new GraphIOException( - "Failed parsing GraphML document - startTag/endTag mismatch"); - } + // Push the name of the unknown element. + skippedElements.push(name); + } + if (event.isEndElement()) { - // Pop the stack. - skippedElements.pop(); - if( skippedElements.isEmpty() ) { - break; - } - } - } + String name = event.asEndElement().getName().getLocalPart(); - return null; + if (skippedElements.size() == 0 || !skippedElements.peek().equals(name)) { + throw new GraphIOException( + "Failed parsing GraphML document - startTag/endTag mismatch"); + } - } catch (Exception e) { - ExceptionConverter.convert(e); + // Pop the stack. + skippedElements.pop(); + if (skippedElements.isEmpty()) { + break; + } } + } - return null; + return null; + + } catch (Exception e) { + ExceptionConverter.convert(e); } + + return null; + } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/PajekNetIOTest.java b/jung-io/src/test/java/edu/uci/ics/jung/io/PajekNetIOTest.java index fb135212..ffb13bff 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/PajekNetIOTest.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/PajekNetIOTest.java @@ -1,7 +1,7 @@ /* * Created on May 3, 2004 * - * Copyright (c) 2004, The JUNG Authors + * Copyright (c) 2004, The JUNG Authors * * All rights reserved. * @@ -11,6 +11,11 @@ */ package edu.uci.ics.jung.io; +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.Network; +import com.google.common.graph.NetworkBuilder; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -19,236 +24,213 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; - -import com.google.common.base.Function; -import com.google.common.base.Supplier; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.Network; -import com.google.common.graph.NetworkBuilder; - import junit.framework.Assert; import junit.framework.TestCase; - /** - * Needed tests: - * - edgeslist, arcslist - * - unit test to catch bug in readArcsOrEdges() [was skipping until e_pred, not c_pred] - * + * Needed tests: - edgeslist, arcslist - unit test to catch bug in readArcsOrEdges() [was skipping + * until e_pred, not c_pred] + * * @author Joshua O'Madadhain * @author Tom Nelson - converted to jung2 */ -public class PajekNetIOTest extends TestCase -{ - protected String[] vertex_labels = {"alpha", "beta", "gamma", "delta", "epsilon"}; - - Supplier> directedGraphFactory = - new Supplier>() { - public MutableNetwork get() { - return NetworkBuilder.directed().allowsSelfLoops(true).build(); - } - }; - Supplier> undirectedGraphFactory = - new Supplier>() { - public MutableNetwork get() { - return NetworkBuilder.undirected().allowsSelfLoops(true).build(); - } - }; - Supplier vertexFactory; - Supplier edgeFactory; - PajekNetReader, Number,Number> pnr; - - @Override - protected void setUp() { - vertexFactory = new Supplier() { - int n = 0; - public Number get() { return n++; } - }; - edgeFactory = new Supplier() { - int n = 0; - public Number get() { return n++; } - }; - pnr = new PajekNetReader, Number,Number>(vertexFactory, edgeFactory); +public class PajekNetIOTest extends TestCase { + protected String[] vertex_labels = {"alpha", "beta", "gamma", "delta", "epsilon"}; - } - - public void testNull() - { - - } - - - public void testFileNotFound() - { - try - { - pnr.load("/dev/null/foo", directedGraphFactory); - fail("File load did not fail on nonexistent file"); - } - catch (FileNotFoundException fnfe) - { + Supplier> directedGraphFactory = + new Supplier>() { + public MutableNetwork get() { + return NetworkBuilder.directed().allowsSelfLoops(true).build(); } - catch (IOException ioe) - { - fail("unexpected IOException"); + }; + Supplier> undirectedGraphFactory = + new Supplier>() { + public MutableNetwork get() { + return NetworkBuilder.undirected().allowsSelfLoops(true).build(); } + }; + Supplier vertexFactory; + Supplier edgeFactory; + PajekNetReader, Number, Number> pnr; + + @Override + protected void setUp() { + vertexFactory = + new Supplier() { + int n = 0; + + public Number get() { + return n++; + } + }; + edgeFactory = + new Supplier() { + int n = 0; + + public Number get() { + return n++; + } + }; + pnr = + new PajekNetReader, Number, Number>( + vertexFactory, edgeFactory); + } + + public void testNull() {} + + public void testFileNotFound() { + try { + pnr.load("/dev/null/foo", directedGraphFactory); + fail("File load did not fail on nonexistent file"); + } catch (FileNotFoundException fnfe) { + } catch (IOException ioe) { + fail("unexpected IOException"); } + } - public void testNoLabels() throws IOException - { - String test = "*Vertices 3\n1\n2\n3\n*Edges\n1 2\n2 2"; - Reader r = new StringReader(test); - - Network g = pnr.load(r, undirectedGraphFactory); - assertEquals(g.nodes().size(), 3); - assertEquals(g.edges().size(), 2); + public void testNoLabels() throws IOException { + String test = "*Vertices 3\n1\n2\n3\n*Edges\n1 2\n2 2"; + Reader r = new StringReader(test); + + Network g = pnr.load(r, undirectedGraphFactory); + assertEquals(g.nodes().size(), 3); + assertEquals(g.edges().size(), 2); + } + + public void testDirectedSaveLoadSave() throws IOException { + MutableNetwork graph1 = directedGraphFactory.get(); + for (int i = 1; i <= 5; i++) { + graph1.addNode(i); } - - public void testDirectedSaveLoadSave() throws IOException - { - MutableNetwork graph1 = directedGraphFactory.get(); - for(int i=1; i<=5; i++) { - graph1.addNode(i); - } - List id = new ArrayList(graph1.nodes()); - GreekLabels gl = new GreekLabels(id); - int j=0; - graph1.addEdge(1, 2, j++); - graph1.addEdge(1, 3, j++); - graph1.addEdge(2, 3, j++); - graph1.addEdge(2, 4, j++); - graph1.addEdge(2, 5, j++); - graph1.addEdge(5, 3, j++); - - assertEquals(graph1.edges().size(), 6); + List id = new ArrayList(graph1.nodes()); + GreekLabels gl = new GreekLabels(id); + int j = 0; + graph1.addEdge(1, 2, j++); + graph1.addEdge(1, 3, j++); + graph1.addEdge(2, 3, j++); + graph1.addEdge(2, 4, j++); + graph1.addEdge(2, 5, j++); + graph1.addEdge(5, 3, j++); + + assertEquals(graph1.edges().size(), 6); + + String testFilename = "dtest.net"; + String testFilename2 = testFilename + "2"; - String testFilename = "dtest.net"; - String testFilename2 = testFilename + "2"; + PajekNetWriter pnw = new PajekNetWriter(); + pnw.save(graph1, testFilename, gl, null, null); - PajekNetWriter pnw = new PajekNetWriter(); - pnw.save(graph1, testFilename, gl, null, null); + MutableNetwork graph2 = pnr.load(testFilename, directedGraphFactory); - MutableNetwork graph2 = pnr.load(testFilename, directedGraphFactory); + assertEquals(graph1.nodes().size(), graph2.nodes().size()); + assertEquals(graph1.edges().size(), graph2.edges().size()); - assertEquals(graph1.nodes().size(), graph2.nodes().size()); - assertEquals(graph1.edges().size(), graph2.edges().size()); + pnw.save(graph2, testFilename2, pnr.getVertexLabeller(), null, null); - pnw.save(graph2, testFilename2, pnr.getVertexLabeller(), null, null); + compareIndexedGraphs(graph1, graph2); - compareIndexedGraphs(graph1, graph2); + MutableNetwork graph3 = pnr.load(testFilename2, directedGraphFactory); - MutableNetwork graph3 = pnr.load(testFilename2, directedGraphFactory); + compareIndexedGraphs(graph2, graph3); - compareIndexedGraphs(graph2, graph3); + File file1 = new File(testFilename); + File file2 = new File(testFilename2); - File file1 = new File(testFilename); - File file2 = new File(testFilename2); + Assert.assertTrue(file1.length() == file2.length()); + file1.delete(); + file2.delete(); + } - Assert.assertTrue(file1.length() == file2.length()); - file1.delete(); - file2.delete(); + public void testUndirectedSaveLoadSave() throws IOException { + MutableNetwork graph1 = undirectedGraphFactory.get(); + for (int i = 1; i <= 5; i++) { + graph1.addNode(i); } - public void testUndirectedSaveLoadSave() throws IOException - { - MutableNetwork graph1 = - undirectedGraphFactory.get(); - for(int i=1; i<=5; i++) { - graph1.addNode(i); - } + List id = new ArrayList(graph1.nodes()); + int j = 0; + GreekLabels gl = new GreekLabels(id); + graph1.addEdge(1, 2, j++); + graph1.addEdge(1, 3, j++); + graph1.addEdge(2, 3, j++); + graph1.addEdge(2, 4, j++); + graph1.addEdge(2, 5, j++); + graph1.addEdge(5, 3, j++); + + assertEquals(graph1.edges().size(), 6); + + String testFilename = "utest.net"; + String testFilename2 = testFilename + "2"; + + PajekNetWriter pnw = new PajekNetWriter(); + pnw.save(graph1, testFilename, gl, null, null); - List id = new ArrayList(graph1.nodes()); - int j=0; - GreekLabels gl = new GreekLabels(id); - graph1.addEdge(1, 2, j++); - graph1.addEdge(1, 3, j++); - graph1.addEdge(2, 3, j++); - graph1.addEdge(2, 4, j++); - graph1.addEdge(2, 5, j++); - graph1.addEdge(5, 3, j++); + MutableNetwork graph2 = pnr.load(testFilename, undirectedGraphFactory); - assertEquals(graph1.edges().size(), 6); + assertEquals(graph1.nodes().size(), graph2.nodes().size()); + assertEquals(graph1.edges().size(), graph2.edges().size()); - String testFilename = "utest.net"; - String testFilename2 = testFilename + "2"; + pnw.save(graph2, testFilename2, pnr.getVertexLabeller(), null, null); + compareIndexedGraphs(graph1, graph2); - PajekNetWriter pnw = new PajekNetWriter(); - pnw.save(graph1, testFilename, gl, null, null); + MutableNetwork graph3 = pnr.load(testFilename2, undirectedGraphFactory); - MutableNetwork graph2 = pnr.load(testFilename, undirectedGraphFactory); - - assertEquals(graph1.nodes().size(), graph2.nodes().size()); - assertEquals(graph1.edges().size(), graph2.edges().size()); + compareIndexedGraphs(graph2, graph3); - pnw.save(graph2, testFilename2, pnr.getVertexLabeller(), null, null); - compareIndexedGraphs(graph1, graph2); + File file1 = new File(testFilename); + File file2 = new File(testFilename2); - MutableNetwork graph3 = pnr.load(testFilename2, undirectedGraphFactory); + Assert.assertTrue(file1.length() == file2.length()); + file1.delete(); + file2.delete(); + } - compareIndexedGraphs(graph2, graph3); + /** + * Tests to see whether these two graphs are structurally equivalent, based on the connectivity of + * the vertices with matching indices in each graph. Assumes a 0-based index. + * + * @param g1 + * @param g2 + */ + private void compareIndexedGraphs(Network g1, Network g2) { + int n1 = g1.nodes().size(); + int n2 = g2.nodes().size(); - File file1 = new File(testFilename); - File file2 = new File(testFilename2); + assertEquals(n1, n2); - Assert.assertTrue(file1.length() == file2.length()); - file1.delete(); - file2.delete(); + assertEquals(g1.edges().size(), g2.edges().size()); + + List id1 = new ArrayList(g1.nodes()); + List id2 = new ArrayList(g2.nodes()); + + for (int i = 0; i < n1; i++) { + Number v1 = id1.get(i); + Number v2 = id2.get(i); + assertNotNull(v1); + assertNotNull(v2); + + checkSets(g1.predecessors(v1), g2.predecessors(v2), id1, id2); + checkSets(g1.successors(v1), g2.successors(v2), id1, id2); } + } - /** - * Tests to see whether these two graphs are structurally equivalent, based - * on the connectivity of the vertices with matching indices in each graph. - * Assumes a 0-based index. - * - * @param g1 - * @param g2 - */ - private void compareIndexedGraphs(Network g1, Network g2) - { - int n1 = g1.nodes().size(); - int n2 = g2.nodes().size(); - - assertEquals(n1, n2); - - assertEquals(g1.edges().size(), g2.edges().size()); - - List id1 = new ArrayList(g1.nodes()); - List id2 = new ArrayList(g2.nodes()); - - for (int i = 0; i < n1; i++) - { - Number v1 = id1.get(i); - Number v2 = id2.get(i); - assertNotNull(v1); - assertNotNull(v2); - - checkSets(g1.predecessors(v1), g2.predecessors(v2), id1, id2); - checkSets(g1.successors(v1), g2.successors(v2), id1, id2); - } + private void checkSets( + Collection s1, Collection s2, List id1, List id2) { + for (Number u : s1) { + int j = id1.indexOf(u); + assertTrue(s2.contains(id2.get(j))); } + } - private void checkSets(Collection s1, Collection s2, List id1, List id2) - { - for (Number u : s1) - { - int j = id1.indexOf(u); - assertTrue(s2.contains(id2.get(j))); - } + private class GreekLabels implements Function { + private List id; + + public GreekLabels(List id) { + this.id = id; } - private class GreekLabels implements Function - { - private List id; - - public GreekLabels(List id) - { - this.id = id; - } - - public String apply(V v) - { - return vertex_labels[id.indexOf(v)]; - } - - } + public String apply(V v) { + return vertex_labels[id.indexOf(v)]; + } + } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/TestGraphMLReader.java b/jung-io/src/test/java/edu/uci/ics/jung/io/TestGraphMLReader.java index aa8a10f7..468b9424 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/TestGraphMLReader.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/TestGraphMLReader.java @@ -1,228 +1,201 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.io; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.Map; - -import javax.xml.parsers.ParserConfigurationException; - -import org.xml.sax.SAXException; - import com.google.common.base.Function; import com.google.common.base.Supplier; import com.google.common.collect.BiMap; import com.google.common.graph.MutableNetwork; import com.google.common.graph.Network; import com.google.common.graph.NetworkBuilder; - +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Map; +import javax.xml.parsers.ParserConfigurationException; import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; - +import org.xml.sax.SAXException; /** * @author Scott White * @author Tom Nelson - converted to jung2 */ -public class TestGraphMLReader extends TestCase -{ - - Supplier> graphFactory; - Supplier vertexFactory; - Supplier edgeFactory; - GraphMLReader, Number, Number> gmlreader; - - public static Test suite() - { - return new TestSuite(TestGraphMLReader.class); - } - - @Override - protected void setUp() throws ParserConfigurationException, SAXException - { - graphFactory = new Supplier>() - { - public MutableNetwork get() - { - return NetworkBuilder.directed() - .allowsSelfLoops(true) - .allowsParallelEdges(true) - .build(); - } +public class TestGraphMLReader extends TestCase { + + Supplier> graphFactory; + Supplier vertexFactory; + Supplier edgeFactory; + GraphMLReader, Number, Number> gmlreader; + + public static Test suite() { + return new TestSuite(TestGraphMLReader.class); + } + + @Override + protected void setUp() throws ParserConfigurationException, SAXException { + graphFactory = + new Supplier>() { + public MutableNetwork get() { + return NetworkBuilder.directed() + .allowsSelfLoops(true) + .allowsParallelEdges(true) + .build(); + } }; - vertexFactory = new Supplier() - { - int n = 0; - - public Number get() - { - return n++; - } - }; - edgeFactory = new Supplier() - { - int n = 0; - - public Number get() - { - return n++; - } - }; - gmlreader = new GraphMLReader, Number, Number>( - vertexFactory, edgeFactory); - } - - public void testLoad() throws IOException - { - String testFilename = "toy_graph.ml"; - - Network graph = loadGraph(testFilename); - - Assert.assertEquals(graph.nodes().size(), 3); - Assert.assertEquals(graph.edges().size(), 3); - - BiMap vertex_ids = gmlreader.getVertexIDs(); - - Number joe = vertex_ids.inverse().get("1"); - Number bob = vertex_ids.inverse().get("2"); - Number sue = vertex_ids.inverse().get("3"); - - Assert.assertNotNull(joe); - Assert.assertNotNull(bob); - Assert.assertNotNull(sue); - - Map> vertex_metadata = - gmlreader.getVertexMetadata(); - Function name = - vertex_metadata.get("name").transformer; - Assert.assertEquals(name.apply(joe), "Joe"); - Assert.assertEquals(name.apply(bob), "Bob"); - Assert.assertEquals(name.apply(sue), "Sue"); - - Assert.assertTrue(graph.predecessors(joe).contains(bob)); - Assert.assertTrue(graph.predecessors(bob).contains(joe)); - Assert.assertTrue(graph.predecessors(sue).contains(joe)); - Assert.assertFalse(graph.predecessors(joe).contains(sue)); - Assert.assertFalse(graph.predecessors(sue).contains(bob)); - Assert.assertFalse(graph.predecessors(bob).contains(sue)); - - File testFile = new File(testFilename); - testFile.delete(); - } + vertexFactory = + new Supplier() { + int n = 0; - public void testAttributes() throws IOException - { - MutableNetwork graph = NetworkBuilder.undirected().allowsSelfLoops(true).build(); - gmlreader.load("src/test/resources/edu/uci/ics/jung/io/graphml/attributes.graphml", graph); - - Assert.assertEquals(graph.nodes().size(), 6); - Assert.assertEquals(graph.edges().size(), 7); - - // test vertex IDs - BiMap vertex_ids = gmlreader.getVertexIDs(); - for (Map.Entry entry : vertex_ids.entrySet()) - { - Assert.assertEquals(entry.getValue().charAt(0), 'n'); - Assert.assertEquals( - Integer.parseInt(entry.getValue().substring(1)), entry - .getKey().intValue()); - } - - // test edge IDs - BiMap edge_ids = gmlreader.getEdgeIDs(); - for (Map.Entry entry : edge_ids.entrySet()) - { - Assert.assertEquals(entry.getValue().charAt(0), 'e'); - Assert.assertEquals( - Integer.parseInt(entry.getValue().substring(1)), entry - .getKey().intValue()); - } - - // test data -// Map> vertex_data = gmlreader -// .getVertexData(); -// Map> edge_data = gmlreader -// .getEdgeData(); - Map> vertex_metadata = - gmlreader.getVertexMetadata(); - Map> edge_metadata = - gmlreader.getEdgeMetadata(); - - - // test vertex colors -// Transformer vertex_color = vertex_data.get("d0"); - Function vertex_color = - vertex_metadata.get("d0").transformer; - Assert.assertEquals(vertex_color.apply(0), "green"); - Assert.assertEquals(vertex_color.apply(1), "yellow"); - Assert.assertEquals(vertex_color.apply(2), "blue"); - Assert.assertEquals(vertex_color.apply(3), "red"); - Assert.assertEquals(vertex_color.apply(4), "yellow"); - Assert.assertEquals(vertex_color.apply(5), "turquoise"); - - // test edge weights -// Transformer edge_weight = edge_data.get("d1"); - Function edge_weight = - edge_metadata.get("d1").transformer; - Assert.assertEquals(edge_weight.apply(0), "1.0"); - Assert.assertEquals(edge_weight.apply(1), "1.0"); - Assert.assertEquals(edge_weight.apply(2), "2.0"); - Assert.assertEquals(edge_weight.apply(3), null); - Assert.assertEquals(edge_weight.apply(4), null); - Assert.assertEquals(edge_weight.apply(5), null); - Assert.assertEquals(edge_weight.apply(6), "1.1"); + public Number get() { + return n++; + } + }; + edgeFactory = + new Supplier() { + int n = 0; + public Number get() { + return n++; + } + }; + gmlreader = + new GraphMLReader, Number, Number>( + vertexFactory, edgeFactory); + } + + public void testLoad() throws IOException { + String testFilename = "toy_graph.ml"; + + Network graph = loadGraph(testFilename); + + Assert.assertEquals(graph.nodes().size(), 3); + Assert.assertEquals(graph.edges().size(), 3); + + BiMap vertex_ids = gmlreader.getVertexIDs(); + + Number joe = vertex_ids.inverse().get("1"); + Number bob = vertex_ids.inverse().get("2"); + Number sue = vertex_ids.inverse().get("3"); + + Assert.assertNotNull(joe); + Assert.assertNotNull(bob); + Assert.assertNotNull(sue); + + Map> vertex_metadata = gmlreader.getVertexMetadata(); + Function name = vertex_metadata.get("name").transformer; + Assert.assertEquals(name.apply(joe), "Joe"); + Assert.assertEquals(name.apply(bob), "Bob"); + Assert.assertEquals(name.apply(sue), "Sue"); + + Assert.assertTrue(graph.predecessors(joe).contains(bob)); + Assert.assertTrue(graph.predecessors(bob).contains(joe)); + Assert.assertTrue(graph.predecessors(sue).contains(joe)); + Assert.assertFalse(graph.predecessors(joe).contains(sue)); + Assert.assertFalse(graph.predecessors(sue).contains(bob)); + Assert.assertFalse(graph.predecessors(bob).contains(sue)); + + File testFile = new File(testFilename); + testFile.delete(); + } + + public void testAttributes() throws IOException { + MutableNetwork graph = + NetworkBuilder.undirected().allowsSelfLoops(true).build(); + gmlreader.load("src/test/resources/edu/uci/ics/jung/io/graphml/attributes.graphml", graph); + + Assert.assertEquals(graph.nodes().size(), 6); + Assert.assertEquals(graph.edges().size(), 7); + + // test vertex IDs + BiMap vertex_ids = gmlreader.getVertexIDs(); + for (Map.Entry entry : vertex_ids.entrySet()) { + Assert.assertEquals(entry.getValue().charAt(0), 'n'); + Assert.assertEquals( + Integer.parseInt(entry.getValue().substring(1)), entry.getKey().intValue()); } - - private Network loadGraph(String testFilename) - throws IOException - { - BufferedWriter writer = new BufferedWriter(new FileWriter( - testFilename)); - writer.write("\n"); - writer.write("\n"); - writer.write("\n"); - writer.write("\n"); - writer.write("\n"); - writer.write("\n"); - writer.write("\n"); - writer.write("\n"); - writer.write("\n"); - writer.write("\n"); - writer.close(); - - MutableNetwork graph = graphFactory.get(); - gmlreader.load(testFilename, graph); - return graph; + // test edge IDs + BiMap edge_ids = gmlreader.getEdgeIDs(); + for (Map.Entry entry : edge_ids.entrySet()) { + Assert.assertEquals(entry.getValue().charAt(0), 'e'); + Assert.assertEquals( + Integer.parseInt(entry.getValue().substring(1)), entry.getKey().intValue()); } -// public void testSave() { -// String testFilename = "toy_graph.ml"; -// Network oldGraph = loadGraph(testFilename); -//// GraphMLFile graphmlFile = new GraphMLFile(); -// String newFilename = testFilename + "_save"; -// gmlreader.save(oldGraph,newFilename); -// Network newGraph = gmlreader.load(newFilename); -// Assert.assertEquals(oldGraph.nodes().size(),newGraph.nodes().size()); -// Assert.assertEquals(oldGraph.edges().size(),newGraph.edges().size()); -// File testFile = new File(testFilename); -// testFile.delete(); -// File newFile = new File(newFilename); -// newFile.delete(); -// -// -// } + // test data + // Map> vertex_data = gmlreader + // .getVertexData(); + // Map> edge_data = gmlreader + // .getEdgeData(); + Map> vertex_metadata = gmlreader.getVertexMetadata(); + Map> edge_metadata = gmlreader.getEdgeMetadata(); + + // test vertex colors + // Transformer vertex_color = vertex_data.get("d0"); + Function vertex_color = vertex_metadata.get("d0").transformer; + Assert.assertEquals(vertex_color.apply(0), "green"); + Assert.assertEquals(vertex_color.apply(1), "yellow"); + Assert.assertEquals(vertex_color.apply(2), "blue"); + Assert.assertEquals(vertex_color.apply(3), "red"); + Assert.assertEquals(vertex_color.apply(4), "yellow"); + Assert.assertEquals(vertex_color.apply(5), "turquoise"); + + // test edge weights + // Transformer edge_weight = edge_data.get("d1"); + Function edge_weight = edge_metadata.get("d1").transformer; + Assert.assertEquals(edge_weight.apply(0), "1.0"); + Assert.assertEquals(edge_weight.apply(1), "1.0"); + Assert.assertEquals(edge_weight.apply(2), "2.0"); + Assert.assertEquals(edge_weight.apply(3), null); + Assert.assertEquals(edge_weight.apply(4), null); + Assert.assertEquals(edge_weight.apply(5), null); + Assert.assertEquals(edge_weight.apply(6), "1.1"); + } + + private Network loadGraph(String testFilename) throws IOException { + BufferedWriter writer = new BufferedWriter(new FileWriter(testFilename)); + writer.write("\n"); + writer.write("\n"); + writer.write("\n"); + writer.write("\n"); + writer.write("\n"); + writer.write("\n"); + writer.write("\n"); + writer.write("\n"); + writer.write("\n"); + writer.write("\n"); + writer.close(); + + MutableNetwork graph = graphFactory.get(); + gmlreader.load(testFilename, graph); + return graph; + } + + // public void testSave() { + // String testFilename = "toy_graph.ml"; + // Network oldGraph = loadGraph(testFilename); + //// GraphMLFile graphmlFile = new GraphMLFile(); + // String newFilename = testFilename + "_save"; + // gmlreader.save(oldGraph,newFilename); + // Network newGraph = gmlreader.load(newFilename); + // Assert.assertEquals(oldGraph.nodes().size(),newGraph.nodes().size()); + // Assert.assertEquals(oldGraph.edges().size(),newGraph.edges().size()); + // File testFile = new File(testFilename); + // testFile.delete(); + // File newFile = new File(newFilename); + // newFile.delete(); + // + // + // } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/TestGraphMLWriter.java b/jung-io/src/test/java/edu/uci/ics/jung/io/TestGraphMLWriter.java index 0667aee8..58c61e42 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/TestGraphMLWriter.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/TestGraphMLWriter.java @@ -1,7 +1,7 @@ /* * Created on Jun 22, 2008 * - * Copyright (c) 2008, The JUNG Authors + * Copyright (c) 2008, The JUNG Authors * * All rights reserved. * @@ -11,6 +11,12 @@ */ package edu.uci.ics.jung.io; +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.Network; +import com.google.common.graph.NetworkBuilder; +import edu.uci.ics.jung.graph.util.TestGraphs; import java.io.File; import java.io.FileWriter; import java.io.IOException; @@ -20,97 +26,77 @@ import java.util.List; import java.util.Map; import java.util.Set; - import javax.xml.parsers.ParserConfigurationException; - +import junit.framework.Assert; +import junit.framework.TestCase; import org.xml.sax.SAXException; -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.Network; -import com.google.common.graph.NetworkBuilder; +public class TestGraphMLWriter extends TestCase { + public void testBasicWrite() throws IOException, ParserConfigurationException, SAXException { + Network g = TestGraphs.createTestGraph(true); + GraphMLWriter gmlw = new GraphMLWriter(); + Function edge_weight = + new Function() { + public String apply(Number n) { + return String.valueOf(n.intValue()); + } + }; -import edu.uci.ics.jung.graph.util.TestGraphs; -import junit.framework.Assert; -import junit.framework.TestCase; + Function vertex_name = Functions.identity(); + //TransformerUtils.nopTransformer(); + + gmlw.addEdgeData("weight", "integer value for the edge", Integer.toString(-1), edge_weight); + gmlw.addVertexData("name", "identifier for the vertex", null, vertex_name); + gmlw.setEdgeIDs(edge_weight); + gmlw.setVertexIDs(vertex_name); + gmlw.save(g, new FileWriter("src/test/resources/testbasicwrite.graphml")); + + // TODO: now read it back in and compare the graph connectivity + // and other metadata with what's in TestGraphs.pairs[], etc. + GraphMLReader, String, Object> gmlr = + new GraphMLReader, String, Object>(); + MutableNetwork g2 = NetworkBuilder.directed().allowsSelfLoops(true).build(); + gmlr.load("src/test/resources/testbasicwrite.graphml", g2); + Map> edge_metadata = gmlr.getEdgeMetadata(); + Function edge_weight2 = edge_metadata.get("weight").transformer; + validateTopology(g, g2, edge_weight, edge_weight2); + + File f = new File("src/test/resources/testbasicwrite.graphml"); + f.delete(); + } + + public > void validateTopology( + Network g, + Network g2, + Function edge_weight, + Function edge_weight2) { + Assert.assertEquals(g2.edges().size(), g.edges().size()); + List g_vertices = new ArrayList(g.nodes()); + List g2_vertices = new ArrayList(g2.nodes()); + Collections.sort(g_vertices); + Collections.sort(g2_vertices); + Assert.assertEquals(g_vertices, g2_vertices); -public class TestGraphMLWriter extends TestCase -{ - public void testBasicWrite() throws IOException, ParserConfigurationException, SAXException - { - Network g = TestGraphs.createTestGraph(true); - GraphMLWriter gmlw = new GraphMLWriter(); - Function edge_weight = new Function() - { - public String apply(Number n) - { - return String.valueOf(n.intValue()); - } - }; + Set g_edges = new HashSet(); + for (Number n : g.edges()) g_edges.add(String.valueOf(n)); + Set g2_edges = new HashSet(g2.edges()); + Assert.assertEquals(g_edges, g2_edges); - Function vertex_name = Functions.identity(); - //TransformerUtils.nopTransformer(); - - gmlw.addEdgeData("weight", "integer value for the edge", - Integer.toString(-1), edge_weight); - gmlw.addVertexData("name", "identifier for the vertex", null, vertex_name); - gmlw.setEdgeIDs(edge_weight); - gmlw.setVertexIDs(vertex_name); - gmlw.save(g, new FileWriter("src/test/resources/testbasicwrite.graphml")); - - // TODO: now read it back in and compare the graph connectivity - // and other metadata with what's in TestGraphs.pairs[], etc. - GraphMLReader, String, Object> gmlr = - new GraphMLReader, String, Object>(); - MutableNetwork g2 = NetworkBuilder.directed().allowsSelfLoops(true).build(); - gmlr.load("src/test/resources/testbasicwrite.graphml", g2); - Map> edge_metadata = - gmlr.getEdgeMetadata(); - Function edge_weight2 = - edge_metadata.get("weight").transformer; - validateTopology(g, g2, edge_weight, edge_weight2); - - File f = new File("src/test/resources/testbasicwrite.graphml"); - f.delete(); + for (T v : g2.nodes()) { + for (T w : g2.nodes()) { + Assert.assertEquals(g.adjacentNodes(v).contains(w), g2.adjacentNodes(v).contains(w)); + Set e = new HashSet(); + for (Number n : g.edgesConnecting(v, w)) e.add(String.valueOf(n)); + Set e2 = new HashSet(g2.edgesConnecting(v, w)); + Assert.assertEquals(e.size(), e2.size()); + Assert.assertEquals(e, e2); + } } - - public > void validateTopology(Network g, Network g2, - Function edge_weight, Function edge_weight2) - { - Assert.assertEquals(g2.edges().size(), g.edges().size()); - List g_vertices = new ArrayList(g.nodes()); - List g2_vertices = new ArrayList(g2.nodes()); - Collections.sort(g_vertices); - Collections.sort(g2_vertices); - Assert.assertEquals(g_vertices, g2_vertices); - Set g_edges = new HashSet(); - for (Number n : g.edges()) - g_edges.add(String.valueOf(n)); - Set g2_edges = new HashSet(g2.edges()); - Assert.assertEquals(g_edges, g2_edges); - - for (T v : g2.nodes()) - { - for (T w : g2.nodes()) - { - Assert.assertEquals(g.adjacentNodes(v).contains(w), - g2.adjacentNodes(v).contains(w)); - Set e = new HashSet(); - for (Number n : g.edgesConnecting(v, w)) - e.add(String.valueOf(n)); - Set e2 = new HashSet(g2.edgesConnecting(v, w)); - Assert.assertEquals(e.size(), e2.size()); - Assert.assertEquals(e, e2); - } - } - - for (Object o : g2.edges()) - { - String weight = edge_weight.apply(new Double((String)o)); - String weight2 = edge_weight2.apply(o); - Assert.assertEquals(weight2, weight); - } + for (Object o : g2.edges()) { + String weight = edge_weight.apply(new Double((String) o)); + String weight2 = edge_weight2.apply(o); + Assert.assertEquals(weight2, weight); } + } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/DummyEdge.java b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/DummyEdge.java index 35824127..3fd4e58c 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/DummyEdge.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/DummyEdge.java @@ -3,27 +3,26 @@ import com.google.common.base.Function; public class DummyEdge extends DummyGraphObjectBase { - - public static class EdgeFactory implements Function { - int n = 100; - public DummyEdge apply(EdgeMetadata md) { - return new DummyEdge(n++); - } - } - - public static class HyperEdgeFactory implements Function { - int n = 0; + public static class EdgeFactory implements Function { + int n = 100; - public DummyEdge apply(HyperEdgeMetadata md) { - return new DummyEdge(n++); - } - } - - public DummyEdge() { + public DummyEdge apply(EdgeMetadata md) { + return new DummyEdge(n++); } + } + + public static class HyperEdgeFactory implements Function { + int n = 0; - public DummyEdge(int v) { - super(v); + public DummyEdge apply(HyperEdgeMetadata md) { + return new DummyEdge(n++); } + } + + public DummyEdge() {} + + public DummyEdge(int v) { + super(v); + } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/DummyGraphObjectBase.java b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/DummyGraphObjectBase.java index b83adb0b..48309742 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/DummyGraphObjectBase.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/DummyGraphObjectBase.java @@ -6,53 +6,50 @@ // TODO: replace common.base.Function with java.util.Function public class DummyGraphObjectBase { - - // TODO: hopefully we can get rid of this; could just use lambdas if we need the factory at all - public static class UndirectedNetworkFactory implements Function> { - public MutableNetwork apply(GraphMetadata arg0) { - return NetworkBuilder.undirected().allowsParallelEdges(false).allowsSelfLoops(true).build(); - } - } -// public static class UndirectedSparseGraphFactory implements Function> { -// -// public Hypergraph apply(GraphMetadata arg0) { -// return new UndirectedSparseGraph(); -// } -// } -// -// public static class SetHypergraphFactory implements Function> { -// -// public Hypergraph apply(GraphMetadata arg0) { -// return new SetHypergraph(); -// } -// } - - public int myValue; - - public DummyGraphObjectBase() { - } - public DummyGraphObjectBase(int v) { - myValue = v; + // TODO: hopefully we can get rid of this; could just use lambdas if we need the factory at all + public static class UndirectedNetworkFactory + implements Function> { + public MutableNetwork apply(GraphMetadata arg0) { + return NetworkBuilder.undirected().allowsParallelEdges(false).allowsSelfLoops(true).build(); } + } + // public static class UndirectedSparseGraphFactory implements Function> { + // + // public Hypergraph apply(GraphMetadata arg0) { + // return new UndirectedSparseGraph(); + // } + // } + // + // public static class SetHypergraphFactory implements Function> { + // + // public Hypergraph apply(GraphMetadata arg0) { + // return new SetHypergraph(); + // } + // } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + myValue; - return result; - } + public int myValue; - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DummyGraphObjectBase other = (DummyGraphObjectBase) obj; - return myValue == other.myValue; - } + public DummyGraphObjectBase() {} + + public DummyGraphObjectBase(int v) { + myValue = v; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + myValue; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + DummyGraphObjectBase other = (DummyGraphObjectBase) obj; + return myValue == other.myValue; + } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/DummyVertex.java b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/DummyVertex.java index 22057df0..f145e431 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/DummyVertex.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/DummyVertex.java @@ -3,19 +3,18 @@ import com.google.common.base.Function; public class DummyVertex extends DummyGraphObjectBase { - - public static class Factory implements Function { - int n = 0; - public DummyVertex apply(NodeMetadata md) { - return new DummyVertex(n++); - } - } - - public DummyVertex() { - } + public static class Factory implements Function { + int n = 0; - public DummyVertex(int v) { - super(v); + public DummyVertex apply(NodeMetadata md) { + return new DummyVertex(n++); } + } + + public DummyVertex() {} + + public DummyVertex(int v) { + super(v); + } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/TestGraphMLReader2.java b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/TestGraphMLReader2.java index c41bb87c..760079ff 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/TestGraphMLReader2.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/TestGraphMLReader2.java @@ -10,6 +10,10 @@ package edu.uci.ics.jung.io.graphml; +import com.google.common.base.Function; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.Network; +import edu.uci.ics.jung.io.GraphIOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; @@ -18,425 +22,474 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; - import org.junit.After; import org.junit.Assert; import org.junit.Test; -import com.google.common.base.Function; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.Network; - -import edu.uci.ics.jung.io.GraphIOException; - public class TestGraphMLReader2 { - static final String graphMLDocStart = "" - + ""; - - private GraphMLReader2, DummyVertex, DummyEdge> reader; - - @After - public void tearDown() throws Exception { - if (reader != null) { - reader.close(); - } - reader = null; - } - + static final String graphMLDocStart = + "" + + ""; - @Test(expected = GraphIOException.class) - public void testEmptyFile() throws Exception { + private GraphMLReader2, DummyVertex, DummyEdge> reader; - String xml = ""; - readGraph(xml, new DummyGraphObjectBase.UndirectedNetworkFactory(), - new DummyVertex.Factory(), new DummyEdge.EdgeFactory()); //, new DummyEdge.HyperEdgeFactory()); + @After + public void tearDown() throws Exception { + if (reader != null) { + reader.close(); } - - @Test - public void testBasics() throws Exception { - - String xml = graphMLDocStart - + "" - + "yellow" - + "" - + "" - + "" - + "" + "green" - + "" + "" + "" - + "blue" + "" - + "" - + "1.0" + "" + "" + ""; - - // Read the graph object. - Network graph = readGraph(xml, new DummyGraphObjectBase.UndirectedNetworkFactory(), - new DummyVertex.Factory(), new DummyEdge.EdgeFactory()); //, new DummyEdge.HyperEdgeFactory()); - - // Check out the graph. - Assert.assertNotNull(graph); - Assert.assertEquals(3, graph.nodes().size()); - Assert.assertEquals(1, graph.edges().size()); - - // Check out metadata. - Assert.assertEquals(1, reader.getGraphMLDocument().getGraphMetadata().size()); - List edges = new ArrayList(reader.getGraphMLDocument().getGraphMetadata().get(0).getEdgeMap().values()); - Assert.assertEquals(1, edges.size()); - Assert.assertEquals("n0", edges.get(0).getSource()); - Assert.assertEquals("n2", edges.get(0).getTarget()); - } - - @Test - public void testData() throws Exception { - - String xml = - graphMLDocStart + - "" + - "yellow" + - "" + - "" + - "" + - "" + - "green" + - "" + - "" + - "" + - "blue" + - "" + - "" + - "1.0" + - "" + - "" + - ""; - - // Read the graph object. - readGraph(xml, new DummyGraphObjectBase.UndirectedNetworkFactory(), - new DummyVertex.Factory(), new DummyEdge.EdgeFactory()); //, new DummyEdge.HyperEdgeFactory()); - - // Check out metadata. - Assert.assertEquals(1, reader.getGraphMLDocument().getGraphMetadata().size()); - List edges = new ArrayList(reader.getGraphMLDocument().getGraphMetadata().get(0).getEdgeMap().values()); - List nodes = new ArrayList(reader.getGraphMLDocument().getGraphMetadata().get(0).getNodeMap().values()); - Collections.sort(nodes, new Comparator() { - public int compare(NodeMetadata o1, NodeMetadata o2) { - return o1.getId().compareTo(o2.getId()); - } - }); - Assert.assertEquals(1, edges.size()); - Assert.assertEquals("1.0", edges.get(0).getProperties().get("d1")); - Assert.assertEquals(3, nodes.size()); - Assert.assertEquals("green", nodes.get(0).getProperties().get("d0")); - Assert.assertEquals("yellow", nodes.get(1).getProperties().get("d0")); - Assert.assertEquals("blue", nodes.get(2).getProperties().get("d0")); - } - - @Test(expected = GraphIOException.class) - public void testEdgeWithInvalidNode() throws Exception { - - String xml = graphMLDocStart - + "" - + "yellow" - + "" - + "" - + "" - + "" + "green" - + "" + "" + "" - + "blue" + "" - + "" + // Invalid - // node: n3 - "1.0" + "" + ""; - - readGraph(xml, new DummyGraphObjectBase.UndirectedNetworkFactory(), new DummyVertex.Factory(), - new DummyEdge.EdgeFactory()); //, new DummyEdge.HyperEdgeFactory()); - } - -// @Test -// public void testHypergraph() throws Exception { -// -// String xml = graphMLDocStart -// + "" -// + "yellow" -// + "" -// + "" -// + "" -// + "" + "green" -// + "" + "" + "" -// + "blue" + "" -// + "" -// + "" + "" -// + "" + "" + "" + ""; -// -// // Read the graph object. -// Hypergraph graph = readGraph(xml, new DummyGraphObjectBase.SetHypergraphFactory(), -// new DummyVertex.Factory(), new DummyEdge.EdgeFactory(), new DummyEdge.HyperEdgeFactory()); -// -// // Check out the graph. -// Assert.assertNotNull(graph); -// Assert.assertEquals(3, graph.nodes().size()); -// Assert.assertEquals(1, graph.edges().size()); -// Assert.assertEquals(0, graph.edges().size(EdgeType.DIRECTED)); -// Assert.assertEquals(1, graph.edges().size(EdgeType.UNDIRECTED)); -// -// // Check out metadata. -// Assert.assertEquals(1, reader.getGraphMLDocument().getGraphMetadata().size()); -// List edges = new ArrayList(reader.getGraphMLDocument().getGraphMetadata().get(0).getHyperEdgeMap().values()); -// Assert.assertEquals(1, edges.size()); -// Assert.assertEquals(3, edges.get(0).getEndpoints().size()); -// Assert.assertEquals("n0", edges.get(0).getEndpoints().get(0).getNode()); -// Assert.assertEquals("n1", edges.get(0).getEndpoints().get(1).getNode()); -// Assert.assertEquals("n2", edges.get(0).getEndpoints().get(2).getNode()); -// } - -// @Test(expected = IllegalArgumentException.class) -// public void testInvalidGraphFactory() throws Exception { -// -// // Need a hypergraph -// String xml = graphMLDocStart -// + "" -// + "yellow" -// + "" -// + "" -// + "" -// + "" + "green" -// + "" + "" + "" -// + "blue" + "" -// + "" -// + "" + "" -// + "" + "" + ""; -// -// // This will attempt to add an edge with an invalid number of incident vertices (3) -// // for an UndirectedGraph, which should trigger an IllegalArgumentException. -// readGraph(xml, new DummyGraphObjectBase.UndirectedNetworkFactory(), -// new DummyVertex.Factory(), new DummyEdge.EdgeFactory()); //, new DummyEdge.HyperEdgeFactory()); -// } - - @Test - public void testAttributesFile() throws Exception { - - // Read the graph object. - Network graph = readGraphFromFile("attributes.graphml", new DummyGraphObjectBase.UndirectedNetworkFactory(), - new DummyVertex.Factory(), new DummyEdge.EdgeFactory()); //, new DummyEdge.HyperEdgeFactory()); - - Assert.assertEquals(6, graph.nodes().size()); - Assert.assertEquals(7, graph.edges().size()); - - Assert.assertEquals(1, reader.getGraphMLDocument().getGraphMetadata().size()); - - // Test node ids - int id = 0; - List nodes = new ArrayList(reader.getGraphMLDocument().getGraphMetadata().get(0).getNodeMap().values()); - Collections.sort(nodes, new Comparator() { - public int compare(NodeMetadata o1, NodeMetadata o2) { - return o1.getId().compareTo(o2.getId()); - } + reader = null; + } + + @Test(expected = GraphIOException.class) + public void testEmptyFile() throws Exception { + + String xml = ""; + readGraph( + xml, + new DummyGraphObjectBase.UndirectedNetworkFactory(), + new DummyVertex.Factory(), + new DummyEdge.EdgeFactory()); //, new DummyEdge.HyperEdgeFactory()); + } + + @Test + public void testBasics() throws Exception { + + String xml = + graphMLDocStart + + "" + + "yellow" + + "" + + "" + + "" + + "" + + "green" + + "" + + "" + + "" + + "blue" + + "" + + "" + + "1.0" + + "" + + "" + + ""; + + // Read the graph object. + Network graph = + readGraph( + xml, + new DummyGraphObjectBase.UndirectedNetworkFactory(), + new DummyVertex.Factory(), + new DummyEdge.EdgeFactory()); //, new DummyEdge.HyperEdgeFactory()); + + // Check out the graph. + Assert.assertNotNull(graph); + Assert.assertEquals(3, graph.nodes().size()); + Assert.assertEquals(1, graph.edges().size()); + + // Check out metadata. + Assert.assertEquals(1, reader.getGraphMLDocument().getGraphMetadata().size()); + List edges = + new ArrayList( + reader.getGraphMLDocument().getGraphMetadata().get(0).getEdgeMap().values()); + Assert.assertEquals(1, edges.size()); + Assert.assertEquals("n0", edges.get(0).getSource()); + Assert.assertEquals("n2", edges.get(0).getTarget()); + } + + @Test + public void testData() throws Exception { + + String xml = + graphMLDocStart + + "" + + "yellow" + + "" + + "" + + "" + + "" + + "green" + + "" + + "" + + "" + + "blue" + + "" + + "" + + "1.0" + + "" + + "" + + ""; + + // Read the graph object. + readGraph( + xml, + new DummyGraphObjectBase.UndirectedNetworkFactory(), + new DummyVertex.Factory(), + new DummyEdge.EdgeFactory()); //, new DummyEdge.HyperEdgeFactory()); + + // Check out metadata. + Assert.assertEquals(1, reader.getGraphMLDocument().getGraphMetadata().size()); + List edges = + new ArrayList( + reader.getGraphMLDocument().getGraphMetadata().get(0).getEdgeMap().values()); + List nodes = + new ArrayList( + reader.getGraphMLDocument().getGraphMetadata().get(0).getNodeMap().values()); + Collections.sort( + nodes, + new Comparator() { + public int compare(NodeMetadata o1, NodeMetadata o2) { + return o1.getId().compareTo(o2.getId()); + } }); - Assert.assertEquals(6, nodes.size()); - for (NodeMetadata md : nodes) { - Assert.assertEquals('n', md.getId().charAt(0)); - Assert.assertEquals(id++, Integer.parseInt(md.getId().substring(1))); - } - - // Test edge ids - id = 0; - List edges = new ArrayList(reader.getGraphMLDocument().getGraphMetadata().get(0).getEdgeMap().values()); - Collections.sort(edges, new Comparator() { - public int compare(EdgeMetadata o1, EdgeMetadata o2) { - return o1.getId().compareTo(o2.getId()); - } + Assert.assertEquals(1, edges.size()); + Assert.assertEquals("1.0", edges.get(0).getProperties().get("d1")); + Assert.assertEquals(3, nodes.size()); + Assert.assertEquals("green", nodes.get(0).getProperties().get("d0")); + Assert.assertEquals("yellow", nodes.get(1).getProperties().get("d0")); + Assert.assertEquals("blue", nodes.get(2).getProperties().get("d0")); + } + + @Test(expected = GraphIOException.class) + public void testEdgeWithInvalidNode() throws Exception { + + String xml = + graphMLDocStart + + "" + + "yellow" + + "" + + "" + + "" + + "" + + "green" + + "" + + "" + + "" + + "blue" + + "" + + "" + + // Invalid + // node: n3 + "1.0" + + "" + + ""; + + readGraph( + xml, + new DummyGraphObjectBase.UndirectedNetworkFactory(), + new DummyVertex.Factory(), + new DummyEdge.EdgeFactory()); //, new DummyEdge.HyperEdgeFactory()); + } + + // @Test + // public void testHypergraph() throws Exception { + // + // String xml = graphMLDocStart + // + "" + // + "yellow" + // + "" + // + "" + // + "" + // + "" + "green" + // + "" + "" + "" + // + "blue" + "" + // + "" + // + "" + "" + // + "" + "" + "" + ""; + // + // // Read the graph object. + // Hypergraph graph = readGraph(xml, new DummyGraphObjectBase.SetHypergraphFactory(), + // new DummyVertex.Factory(), new DummyEdge.EdgeFactory(), new DummyEdge.HyperEdgeFactory()); + // + // // Check out the graph. + // Assert.assertNotNull(graph); + // Assert.assertEquals(3, graph.nodes().size()); + // Assert.assertEquals(1, graph.edges().size()); + // Assert.assertEquals(0, graph.edges().size(EdgeType.DIRECTED)); + // Assert.assertEquals(1, graph.edges().size(EdgeType.UNDIRECTED)); + // + // // Check out metadata. + // Assert.assertEquals(1, reader.getGraphMLDocument().getGraphMetadata().size()); + // List edges = new ArrayList(reader.getGraphMLDocument().getGraphMetadata().get(0).getHyperEdgeMap().values()); + // Assert.assertEquals(1, edges.size()); + // Assert.assertEquals(3, edges.get(0).getEndpoints().size()); + // Assert.assertEquals("n0", edges.get(0).getEndpoints().get(0).getNode()); + // Assert.assertEquals("n1", edges.get(0).getEndpoints().get(1).getNode()); + // Assert.assertEquals("n2", edges.get(0).getEndpoints().get(2).getNode()); + // } + + // @Test(expected = IllegalArgumentException.class) + // public void testInvalidGraphFactory() throws Exception { + // + // // Need a hypergraph + // String xml = graphMLDocStart + // + "" + // + "yellow" + // + "" + // + "" + // + "" + // + "" + "green" + // + "" + "" + "" + // + "blue" + "" + // + "" + // + "" + "" + // + "" + "" + ""; + // + // // This will attempt to add an edge with an invalid number of incident vertices (3) + // // for an UndirectedGraph, which should trigger an IllegalArgumentException. + // readGraph(xml, new DummyGraphObjectBase.UndirectedNetworkFactory(), + // new DummyVertex.Factory(), new DummyEdge.EdgeFactory()); //, new DummyEdge.HyperEdgeFactory()); + // } + + @Test + public void testAttributesFile() throws Exception { + + // Read the graph object. + Network graph = + readGraphFromFile( + "attributes.graphml", + new DummyGraphObjectBase.UndirectedNetworkFactory(), + new DummyVertex.Factory(), + new DummyEdge.EdgeFactory()); //, new DummyEdge.HyperEdgeFactory()); + + Assert.assertEquals(6, graph.nodes().size()); + Assert.assertEquals(7, graph.edges().size()); + + Assert.assertEquals(1, reader.getGraphMLDocument().getGraphMetadata().size()); + + // Test node ids + int id = 0; + List nodes = + new ArrayList( + reader.getGraphMLDocument().getGraphMetadata().get(0).getNodeMap().values()); + Collections.sort( + nodes, + new Comparator() { + public int compare(NodeMetadata o1, NodeMetadata o2) { + return o1.getId().compareTo(o2.getId()); + } }); - Assert.assertEquals(7, edges.size()); - for (EdgeMetadata md : edges) { - Assert.assertEquals('e', md.getId().charAt(0)); - Assert.assertEquals(id++, Integer.parseInt(md.getId().substring(1))); - } - - Assert.assertEquals("green", nodes.get(0).getProperties().get("d0")); - Assert.assertEquals("yellow", nodes.get(1).getProperties().get("d0")); - Assert.assertEquals("blue", nodes.get(2).getProperties().get("d0")); - Assert.assertEquals("red", nodes.get(3).getProperties().get("d0")); - Assert.assertEquals("yellow", nodes.get(4).getProperties().get("d0")); - Assert.assertEquals("turquoise", nodes.get(5).getProperties().get("d0")); - - Assert.assertEquals("1.0", edges.get(0).getProperties().get("d1")); - Assert.assertEquals("1.0", edges.get(1).getProperties().get("d1")); - Assert.assertEquals("2.0", edges.get(2).getProperties().get("d1")); - Assert.assertEquals(null, edges.get(3).getProperties().get("d1")); - Assert.assertEquals(null, edges.get(4).getProperties().get("d1")); - Assert.assertEquals(null, edges.get(5).getProperties().get("d1")); - Assert.assertEquals("1.1", edges.get(6).getProperties().get("d1")); + Assert.assertEquals(6, nodes.size()); + for (NodeMetadata md : nodes) { + Assert.assertEquals('n', md.getId().charAt(0)); + Assert.assertEquals(id++, Integer.parseInt(md.getId().substring(1))); } -// @Test -// public void testHypergraphFile() throws Exception { -// -// Function> graphFactory = new Function>() { -// public Hypergraph apply(GraphMetadata md) { -// return new SetHypergraph(); -// } -// }; -// -// Function vertexFactory = new Function() { -// int n = 0; -// -// public Number apply(NodeMetadata md) { -// return n++; -// } -// }; -// -// Function edgeFactory = new Function() { -// int n = 100; -// -// public Number apply(EdgeMetadata md) { -// return n++; -// } -// }; -// -//// Function hyperEdgeFactory = new Function() { -//// int n = 0; -//// -//// public Number apply(HyperEdgeMetadata md) { -//// return n++; -//// } -//// }; -// -// // Read the graph object. -// Reader fileReader = new InputStreamReader(getClass().getResourceAsStream("hyper.graphml")); -// GraphMLReader2, Number, Number> reader = -// new GraphMLReader2, Number, Number>(fileReader, -// graphFactory, vertexFactory, edgeFactory); //, hyperEdgeFactory); -// -// // Read the graph. -// Network graph = reader.readGraph(); -// -// Assert.assertEquals(graph.nodes().size(), 7); -// Assert.assertEquals(graph.edges().size(), 4); -// -// // n0 -// Set incident = new HashSet(); -// incident.add(0); -// incident.add(100); -// Assert.assertEquals(incident, graph.incidentEdges(0)); -// -// // n1 -// incident.clear(); -// incident.add(0); -// incident.add(2); -// Assert.assertEquals(incident, graph.incidentEdges(1)); -// -// // n2 -// incident.clear(); -// incident.add(0); -// Assert.assertEquals(incident, graph.incidentEdges(2)); -// -// // n3 -// incident.clear(); -// incident.add(1); -// incident.add(2); -// Assert.assertEquals(incident, graph.incidentEdges(3)); -// -// // n4 -// incident.clear(); -// incident.add(1); -// incident.add(100); -// Assert.assertEquals(incident, graph.incidentEdges(4)); -// -// // n5 -// incident.clear(); -// incident.add(1); -// Assert.assertEquals(incident, graph.incidentEdges(5)); -// -// // n6 -// incident.clear(); -// incident.add(1); -// Assert.assertEquals(incident, graph.incidentEdges(6)); -// } - - /*@Test - public void testReader1Perf() throws Exception { - String fileName = "attributes.graphml"; - - long totalTime = 0; - int numTrials = 1000; - - for( int ix=0; ix, DummyVertex, DummyEdge> reader = new GraphMLReader, DummyVertex, DummyEdge>(new Factory() { - - public DummyVertex create() { - return new DummyVertex(); - } - - }, new Factory() { - public DummyEdge create() { - return new DummyEdge(); - } - }); - - Thread.sleep(10); - - long start = System.currentTimeMillis(); - Hypergraph graph = new UndirectedSparseGraph(); - reader.load(fileReader, graph); - long duration = System.currentTimeMillis() - start; - totalTime += duration; - } - - double avgTime = ((double)totalTime / (double)numTrials) / 1000.0; - - System.out.printf("Reader1: totalTime=%6d, numTrials=%6d, avgTime=%2.6f seconds", totalTime, numTrials, avgTime); - System.out.println(); - } - - @Test - public void testReader2Perf() throws Exception { - String fileName = "attributes.graphml"; - - long totalTime = 0; - int numTrials = 1000; - - // Test reader2 - for( int ix=0; ix, DummyVertex, DummyEdge>( - fileReader, new DummyGraphObjectBase.UndirectedSparseGraphFactory(), - new DummyVertex.Factory(), new DummyEdge.EdgeFactory(), new DummyEdge.HyperEdgeFactory()); - reader.init(); - - Thread.sleep(10); - - long start = System.currentTimeMillis(); - reader.readGraph(); - long duration = System.currentTimeMillis() - start; - totalTime += duration; - - reader.close(); - } - - double avgTime = ((double)totalTime / (double)numTrials) / 1000.0; - - System.out.printf("Reader2: totalTime=%6d, numTrials=%6d, avgTime=%2.6f seconds", totalTime, numTrials, avgTime); - System.out.println(); - }*/ - - private Network readGraph(String xml, Function> gf, - DummyVertex.Factory nf, DummyEdge.EdgeFactory ef) //, DummyEdge.HyperEdgeFactory hef) - throws GraphIOException { - Reader fileReader = new StringReader(xml); - reader = new GraphMLReader2, DummyVertex, DummyEdge>( - fileReader, gf, nf, ef); //, hef); - - return reader.readGraph(); - } - - private Network readGraphFromFile(String file, Function> gf, - DummyVertex.Factory nf, DummyEdge.EdgeFactory ef) //, DummyEdge.HyperEdgeFactory hef) - throws Exception { - InputStream is = getClass().getResourceAsStream(file); - Reader fileReader = new InputStreamReader(is); - reader = new GraphMLReader2, DummyVertex, DummyEdge>( - fileReader, gf, nf, ef); //, hef); - - return reader.readGraph(); + // Test edge ids + id = 0; + List edges = + new ArrayList( + reader.getGraphMLDocument().getGraphMetadata().get(0).getEdgeMap().values()); + Collections.sort( + edges, + new Comparator() { + public int compare(EdgeMetadata o1, EdgeMetadata o2) { + return o1.getId().compareTo(o2.getId()); + } + }); + Assert.assertEquals(7, edges.size()); + for (EdgeMetadata md : edges) { + Assert.assertEquals('e', md.getId().charAt(0)); + Assert.assertEquals(id++, Integer.parseInt(md.getId().substring(1))); } + Assert.assertEquals("green", nodes.get(0).getProperties().get("d0")); + Assert.assertEquals("yellow", nodes.get(1).getProperties().get("d0")); + Assert.assertEquals("blue", nodes.get(2).getProperties().get("d0")); + Assert.assertEquals("red", nodes.get(3).getProperties().get("d0")); + Assert.assertEquals("yellow", nodes.get(4).getProperties().get("d0")); + Assert.assertEquals("turquoise", nodes.get(5).getProperties().get("d0")); + + Assert.assertEquals("1.0", edges.get(0).getProperties().get("d1")); + Assert.assertEquals("1.0", edges.get(1).getProperties().get("d1")); + Assert.assertEquals("2.0", edges.get(2).getProperties().get("d1")); + Assert.assertEquals(null, edges.get(3).getProperties().get("d1")); + Assert.assertEquals(null, edges.get(4).getProperties().get("d1")); + Assert.assertEquals(null, edges.get(5).getProperties().get("d1")); + Assert.assertEquals("1.1", edges.get(6).getProperties().get("d1")); + } + + // @Test + // public void testHypergraphFile() throws Exception { + // + // Function> graphFactory = new Function>() { + // public Hypergraph apply(GraphMetadata md) { + // return new SetHypergraph(); + // } + // }; + // + // Function vertexFactory = new Function() { + // int n = 0; + // + // public Number apply(NodeMetadata md) { + // return n++; + // } + // }; + // + // Function edgeFactory = new Function() { + // int n = 100; + // + // public Number apply(EdgeMetadata md) { + // return n++; + // } + // }; + // + //// Function hyperEdgeFactory = new Function() { + //// int n = 0; + //// + //// public Number apply(HyperEdgeMetadata md) { + //// return n++; + //// } + //// }; + // + // // Read the graph object. + // Reader fileReader = new InputStreamReader(getClass().getResourceAsStream("hyper.graphml")); + // GraphMLReader2, Number, Number> reader = + // new GraphMLReader2, Number, Number>(fileReader, + // graphFactory, vertexFactory, edgeFactory); //, hyperEdgeFactory); + // + // // Read the graph. + // Network graph = reader.readGraph(); + // + // Assert.assertEquals(graph.nodes().size(), 7); + // Assert.assertEquals(graph.edges().size(), 4); + // + // // n0 + // Set incident = new HashSet(); + // incident.add(0); + // incident.add(100); + // Assert.assertEquals(incident, graph.incidentEdges(0)); + // + // // n1 + // incident.clear(); + // incident.add(0); + // incident.add(2); + // Assert.assertEquals(incident, graph.incidentEdges(1)); + // + // // n2 + // incident.clear(); + // incident.add(0); + // Assert.assertEquals(incident, graph.incidentEdges(2)); + // + // // n3 + // incident.clear(); + // incident.add(1); + // incident.add(2); + // Assert.assertEquals(incident, graph.incidentEdges(3)); + // + // // n4 + // incident.clear(); + // incident.add(1); + // incident.add(100); + // Assert.assertEquals(incident, graph.incidentEdges(4)); + // + // // n5 + // incident.clear(); + // incident.add(1); + // Assert.assertEquals(incident, graph.incidentEdges(5)); + // + // // n6 + // incident.clear(); + // incident.add(1); + // Assert.assertEquals(incident, graph.incidentEdges(6)); + // } + + /*@Test + public void testReader1Perf() throws Exception { + String fileName = "attributes.graphml"; + + long totalTime = 0; + int numTrials = 1000; + + for( int ix=0; ix, DummyVertex, DummyEdge> reader = new GraphMLReader, DummyVertex, DummyEdge>(new Factory() { + + public DummyVertex create() { + return new DummyVertex(); + } + + }, new Factory() { + public DummyEdge create() { + return new DummyEdge(); + } + }); + + Thread.sleep(10); + + long start = System.currentTimeMillis(); + Hypergraph graph = new UndirectedSparseGraph(); + reader.load(fileReader, graph); + long duration = System.currentTimeMillis() - start; + totalTime += duration; + } + + double avgTime = ((double)totalTime / (double)numTrials) / 1000.0; + + System.out.printf("Reader1: totalTime=%6d, numTrials=%6d, avgTime=%2.6f seconds", totalTime, numTrials, avgTime); + System.out.println(); + } + + @Test + public void testReader2Perf() throws Exception { + String fileName = "attributes.graphml"; + + long totalTime = 0; + int numTrials = 1000; + + // Test reader2 + for( int ix=0; ix, DummyVertex, DummyEdge>( + fileReader, new DummyGraphObjectBase.UndirectedSparseGraphFactory(), + new DummyVertex.Factory(), new DummyEdge.EdgeFactory(), new DummyEdge.HyperEdgeFactory()); + reader.init(); + + Thread.sleep(10); + + long start = System.currentTimeMillis(); + reader.readGraph(); + long duration = System.currentTimeMillis() - start; + totalTime += duration; + + reader.close(); + } + + double avgTime = ((double)totalTime / (double)numTrials) / 1000.0; + + System.out.printf("Reader2: totalTime=%6d, numTrials=%6d, avgTime=%2.6f seconds", totalTime, numTrials, avgTime); + System.out.println(); + }*/ + + private Network readGraph( + String xml, + Function> gf, + DummyVertex.Factory nf, + DummyEdge.EdgeFactory ef) //, DummyEdge.HyperEdgeFactory hef) + throws GraphIOException { + Reader fileReader = new StringReader(xml); + reader = + new GraphMLReader2, DummyVertex, DummyEdge>( + fileReader, gf, nf, ef); //, hef); + + return reader.readGraph(); + } + + private Network readGraphFromFile( + String file, + Function> gf, + DummyVertex.Factory nf, + DummyEdge.EdgeFactory ef) //, DummyEdge.HyperEdgeFactory hef) + throws Exception { + InputStream is = getClass().getResourceAsStream(file); + Reader fileReader = new InputStreamReader(is); + reader = + new GraphMLReader2, DummyVertex, DummyEdge>( + fileReader, gf, nf, ef); //, hef); + + return reader.readGraph(); + } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/AbstractParserTest.java b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/AbstractParserTest.java index 5abfa193..8cdb8427 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/AbstractParserTest.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/AbstractParserTest.java @@ -10,68 +10,64 @@ package edu.uci.ics.jung.io.graphml.parser; +import com.google.common.graph.MutableNetwork; +import edu.uci.ics.jung.io.graphml.DummyEdge; +import edu.uci.ics.jung.io.graphml.DummyGraphObjectBase; +import edu.uci.ics.jung.io.graphml.DummyVertex; +import edu.uci.ics.jung.io.graphml.KeyMap; import java.io.Reader; import java.io.StringReader; - import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; - import org.junit.After; import org.junit.Assert; import org.junit.Before; -import com.google.common.graph.MutableNetwork; +public abstract class AbstractParserTest { -import edu.uci.ics.jung.io.graphml.DummyEdge; -import edu.uci.ics.jung.io.graphml.DummyGraphObjectBase; -import edu.uci.ics.jung.io.graphml.DummyVertex; -import edu.uci.ics.jung.io.graphml.KeyMap; + private ElementParserRegistry, DummyVertex, DummyEdge> + registry; -public abstract class AbstractParserTest { + @Before + public void setUp() throws Exception { + registry = + new ElementParserRegistry, DummyVertex, DummyEdge>( + new KeyMap(), + new DummyGraphObjectBase.UndirectedNetworkFactory(), + new DummyVertex.Factory(), + new DummyEdge.EdgeFactory()); + // new DummyEdge.HyperEdgeFactory()); + } - - private ElementParserRegistry,DummyVertex,DummyEdge> registry; + @After + public void tearDown() throws Exception { + registry = null; + } - @Before - public void setUp() throws Exception { - registry = new ElementParserRegistry,DummyVertex,DummyEdge>( - new KeyMap(), - new DummyGraphObjectBase.UndirectedNetworkFactory(), - new DummyVertex.Factory(), - new DummyEdge.EdgeFactory()); -// new DummyEdge.HyperEdgeFactory()); - } + protected Object readObject(String xml) throws Exception { - @After - public void tearDown() throws Exception { - registry = null; - } - - protected Object readObject(String xml) throws Exception { + Reader fileReader = new StringReader(xml); + XMLInputFactory factory = XMLInputFactory.newInstance(); + XMLEventReader xmlEventReader = factory.createXMLEventReader(fileReader); + xmlEventReader = factory.createFilteredReader(xmlEventReader, new GraphMLEventFilter()); - Reader fileReader = new StringReader(xml); - XMLInputFactory factory = XMLInputFactory.newInstance(); - XMLEventReader xmlEventReader = factory.createXMLEventReader(fileReader); - xmlEventReader = factory.createFilteredReader(xmlEventReader, - new GraphMLEventFilter()); - - try { - while( xmlEventReader.hasNext() ) { - XMLEvent event = xmlEventReader.nextEvent(); - if (event.isStartElement()) { - - StartElement start = event.asStartElement(); - String name = start.getName().getLocalPart(); - return registry.getParser(name).parse(xmlEventReader, start); - } - } - } finally { - xmlEventReader.close(); + try { + while (xmlEventReader.hasNext()) { + XMLEvent event = xmlEventReader.nextEvent(); + if (event.isStartElement()) { + + StartElement start = event.asStartElement(); + String name = start.getName().getLocalPart(); + return registry.getParser(name).parse(xmlEventReader, start); } - - Assert.fail("failed to read object from XML: " + xml); - return null; + } + } finally { + xmlEventReader.close(); } + + Assert.fail("failed to read object from XML: " + xml); + return null; + } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestEdgeElementParser.java b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestEdgeElementParser.java index 810bc369..86bcc81b 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestEdgeElementParser.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestEdgeElementParser.java @@ -10,159 +10,147 @@ package edu.uci.ics.jung.io.graphml.parser; -import org.junit.Assert; -import org.junit.Test; - import edu.uci.ics.jung.io.GraphIOException; import edu.uci.ics.jung.io.graphml.EdgeMetadata; +import org.junit.Assert; +import org.junit.Test; public class TestEdgeElementParser extends AbstractParserTest { - @Test(expected= GraphIOException.class) - public void testNoSource() throws Exception { - - String xml = - ""; - - readObject(xml); - } - - @Test(expected= GraphIOException.class) - public void testNoTarget() throws Exception { - - String xml = - ""; - - readObject(xml); - } - - @Test - public void testId() throws Exception { - - String xml = - ""; - - EdgeMetadata edge = (EdgeMetadata) readObject(xml); - Assert.assertNotNull(edge); - Assert.assertEquals("e1", edge.getId()); - Assert.assertEquals(null, edge.getDescription()); - Assert.assertEquals("1", edge.getSource()); - Assert.assertEquals("2", edge.getTarget()); - Assert.assertEquals(null, edge.isDirected()); - Assert.assertEquals(null, edge.getSourcePort()); - Assert.assertEquals(null, edge.getTargetPort()); - } - - @Test - public void testDirectedTrue() throws Exception { - - String xml = - ""; - - EdgeMetadata edge = (EdgeMetadata) readObject(xml); - Assert.assertNotNull(edge); - Assert.assertEquals(null, edge.getId()); - Assert.assertEquals(null, edge.getDescription()); - Assert.assertEquals("1", edge.getSource()); - Assert.assertEquals("2", edge.getTarget()); - Assert.assertEquals(true, edge.isDirected()); - Assert.assertEquals(null, edge.getSourcePort()); - Assert.assertEquals(null, edge.getTargetPort()); - } - - @Test - public void testDirectedFalse() throws Exception { - - String xml = - ""; - - EdgeMetadata edge = (EdgeMetadata) readObject(xml); - Assert.assertNotNull(edge); - Assert.assertEquals(null, edge.getId()); - Assert.assertEquals(null, edge.getDescription()); - Assert.assertEquals("1", edge.getSource()); - Assert.assertEquals("2", edge.getTarget()); - Assert.assertEquals(false, edge.isDirected()); - Assert.assertEquals(null, edge.getSourcePort()); - Assert.assertEquals(null, edge.getTargetPort()); - } - - @Test - public void testSourceTargetPorts() throws Exception { - - String xml = - ""; - - EdgeMetadata edge = (EdgeMetadata) readObject(xml); - Assert.assertNotNull(edge); - Assert.assertEquals(null, edge.getId()); - Assert.assertEquals(null, edge.getDescription()); - Assert.assertEquals("1", edge.getSource()); - Assert.assertEquals("2", edge.getTarget()); - Assert.assertEquals(null, edge.isDirected()); - Assert.assertEquals("a", edge.getSourcePort()); - Assert.assertEquals("b", edge.getTargetPort()); - } - - @Test - public void testDesc() throws Exception { - - String xml = - "" + - "hello world" + - ""; - - EdgeMetadata edge = (EdgeMetadata) readObject(xml); - Assert.assertNotNull(edge); - Assert.assertEquals(null, edge.getId()); - Assert.assertEquals("hello world", edge.getDescription()); - Assert.assertEquals("1", edge.getSource()); - Assert.assertEquals("2", edge.getTarget()); - Assert.assertEquals(null, edge.isDirected()); - Assert.assertEquals(null, edge.getSourcePort()); - Assert.assertEquals(null, edge.getTargetPort()); - } - - @Test - public void testUserAttributes() throws Exception { - - String xml = - "" + - ""; - - EdgeMetadata edge = (EdgeMetadata) readObject(xml); - Assert.assertNotNull(edge); - Assert.assertEquals(null, edge.getId()); - Assert.assertEquals(null, edge.getDescription()); - Assert.assertEquals("1", edge.getSource()); - Assert.assertEquals("2", edge.getTarget()); - Assert.assertEquals(null, edge.isDirected()); - Assert.assertEquals(null, edge.getSourcePort()); - Assert.assertEquals(null, edge.getTargetPort()); - Assert.assertEquals(1, edge.getProperties().size()); - Assert.assertEquals("abc123", edge.getProperty("bob")); - } - - @Test - public void testData() throws Exception { - - String xml = - "" + - "value1" + - "value2" + - ""; - - EdgeMetadata edge = (EdgeMetadata) readObject(xml); - Assert.assertNotNull(edge); - Assert.assertEquals(null, edge.getId()); - Assert.assertEquals(null, edge.getDescription()); - Assert.assertEquals("1", edge.getSource()); - Assert.assertEquals("2", edge.getTarget()); - Assert.assertEquals(null, edge.isDirected()); - Assert.assertEquals(null, edge.getSourcePort()); - Assert.assertEquals(null, edge.getTargetPort()); - Assert.assertEquals(2, edge.getProperties().size()); - Assert.assertEquals("value1", edge.getProperty("d1")); - Assert.assertEquals("value2", edge.getProperty("d2")); - } + @Test(expected = GraphIOException.class) + public void testNoSource() throws Exception { + + String xml = ""; + + readObject(xml); + } + + @Test(expected = GraphIOException.class) + public void testNoTarget() throws Exception { + + String xml = ""; + + readObject(xml); + } + + @Test + public void testId() throws Exception { + + String xml = ""; + + EdgeMetadata edge = (EdgeMetadata) readObject(xml); + Assert.assertNotNull(edge); + Assert.assertEquals("e1", edge.getId()); + Assert.assertEquals(null, edge.getDescription()); + Assert.assertEquals("1", edge.getSource()); + Assert.assertEquals("2", edge.getTarget()); + Assert.assertEquals(null, edge.isDirected()); + Assert.assertEquals(null, edge.getSourcePort()); + Assert.assertEquals(null, edge.getTargetPort()); + } + + @Test + public void testDirectedTrue() throws Exception { + + String xml = ""; + + EdgeMetadata edge = (EdgeMetadata) readObject(xml); + Assert.assertNotNull(edge); + Assert.assertEquals(null, edge.getId()); + Assert.assertEquals(null, edge.getDescription()); + Assert.assertEquals("1", edge.getSource()); + Assert.assertEquals("2", edge.getTarget()); + Assert.assertEquals(true, edge.isDirected()); + Assert.assertEquals(null, edge.getSourcePort()); + Assert.assertEquals(null, edge.getTargetPort()); + } + + @Test + public void testDirectedFalse() throws Exception { + + String xml = ""; + + EdgeMetadata edge = (EdgeMetadata) readObject(xml); + Assert.assertNotNull(edge); + Assert.assertEquals(null, edge.getId()); + Assert.assertEquals(null, edge.getDescription()); + Assert.assertEquals("1", edge.getSource()); + Assert.assertEquals("2", edge.getTarget()); + Assert.assertEquals(false, edge.isDirected()); + Assert.assertEquals(null, edge.getSourcePort()); + Assert.assertEquals(null, edge.getTargetPort()); + } + + @Test + public void testSourceTargetPorts() throws Exception { + + String xml = ""; + + EdgeMetadata edge = (EdgeMetadata) readObject(xml); + Assert.assertNotNull(edge); + Assert.assertEquals(null, edge.getId()); + Assert.assertEquals(null, edge.getDescription()); + Assert.assertEquals("1", edge.getSource()); + Assert.assertEquals("2", edge.getTarget()); + Assert.assertEquals(null, edge.isDirected()); + Assert.assertEquals("a", edge.getSourcePort()); + Assert.assertEquals("b", edge.getTargetPort()); + } + + @Test + public void testDesc() throws Exception { + + String xml = "" + "hello world" + ""; + + EdgeMetadata edge = (EdgeMetadata) readObject(xml); + Assert.assertNotNull(edge); + Assert.assertEquals(null, edge.getId()); + Assert.assertEquals("hello world", edge.getDescription()); + Assert.assertEquals("1", edge.getSource()); + Assert.assertEquals("2", edge.getTarget()); + Assert.assertEquals(null, edge.isDirected()); + Assert.assertEquals(null, edge.getSourcePort()); + Assert.assertEquals(null, edge.getTargetPort()); + } + + @Test + public void testUserAttributes() throws Exception { + + String xml = "" + ""; + + EdgeMetadata edge = (EdgeMetadata) readObject(xml); + Assert.assertNotNull(edge); + Assert.assertEquals(null, edge.getId()); + Assert.assertEquals(null, edge.getDescription()); + Assert.assertEquals("1", edge.getSource()); + Assert.assertEquals("2", edge.getTarget()); + Assert.assertEquals(null, edge.isDirected()); + Assert.assertEquals(null, edge.getSourcePort()); + Assert.assertEquals(null, edge.getTargetPort()); + Assert.assertEquals(1, edge.getProperties().size()); + Assert.assertEquals("abc123", edge.getProperty("bob")); + } + + @Test + public void testData() throws Exception { + + String xml = + "" + + "value1" + + "value2" + + ""; + + EdgeMetadata edge = (EdgeMetadata) readObject(xml); + Assert.assertNotNull(edge); + Assert.assertEquals(null, edge.getId()); + Assert.assertEquals(null, edge.getDescription()); + Assert.assertEquals("1", edge.getSource()); + Assert.assertEquals("2", edge.getTarget()); + Assert.assertEquals(null, edge.isDirected()); + Assert.assertEquals(null, edge.getSourcePort()); + Assert.assertEquals(null, edge.getTargetPort()); + Assert.assertEquals(2, edge.getProperties().size()); + Assert.assertEquals("value1", edge.getProperty("d1")); + Assert.assertEquals("value2", edge.getProperty("d2")); + } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestEndpointElementParser.java b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestEndpointElementParser.java index fc990bbc..070ff4d7 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestEndpointElementParser.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestEndpointElementParser.java @@ -10,133 +10,117 @@ package edu.uci.ics.jung.io.graphml.parser; -import org.junit.Assert; -import org.junit.Test; - import edu.uci.ics.jung.io.GraphIOException; import edu.uci.ics.jung.io.graphml.EndpointMetadata; import edu.uci.ics.jung.io.graphml.EndpointMetadata.EndpointType; +import org.junit.Assert; +import org.junit.Test; public class TestEndpointElementParser extends AbstractParserTest { - @Test(expected= GraphIOException.class) - public void testNoNode() throws Exception { - - String xml = - ""; - - readObject(xml); - } - - @Test - public void testId() throws Exception { - - String xml = - ""; - - EndpointMetadata ep = (EndpointMetadata) readObject(xml); - Assert.assertNotNull(ep); - Assert.assertEquals("1", ep.getNode()); - Assert.assertEquals("ep1", ep.getId()); - Assert.assertEquals(null, ep.getDescription()); - Assert.assertEquals(null, ep.getPort()); - Assert.assertEquals(EndpointType.UNDIR, ep.getEndpointType()); - } - - @Test - public void testDesc() throws Exception { - - String xml = - "" + - "hello world" + - ""; - - EndpointMetadata ep = (EndpointMetadata) readObject(xml); - Assert.assertNotNull(ep); - Assert.assertEquals("1", ep.getNode()); - Assert.assertEquals("ep1", ep.getId()); - Assert.assertEquals("hello world", ep.getDescription()); - Assert.assertEquals(null, ep.getPort()); - Assert.assertEquals(EndpointType.UNDIR, ep.getEndpointType()); - } - - @Test - public void testPort() throws Exception { - - String xml = - "" + - ""; - - EndpointMetadata ep = (EndpointMetadata) readObject(xml); - Assert.assertNotNull(ep); - Assert.assertEquals("1", ep.getNode()); - Assert.assertEquals("ep1", ep.getId()); - Assert.assertEquals(null, ep.getDescription()); - Assert.assertEquals("abc123", ep.getPort()); - Assert.assertEquals(EndpointType.UNDIR, ep.getEndpointType()); - } - - @Test - public void testTypeIn() throws Exception { - - String xml = - "" + - ""; - - EndpointMetadata ep = (EndpointMetadata) readObject(xml); - Assert.assertNotNull(ep); - Assert.assertEquals("1", ep.getNode()); - Assert.assertEquals(null, ep.getId()); - Assert.assertEquals(null, ep.getDescription()); - Assert.assertEquals(null, ep.getPort()); - Assert.assertEquals(EndpointType.IN, ep.getEndpointType()); - } - - @Test - public void testTypeOut() throws Exception { - - String xml = - "" + - ""; - - EndpointMetadata ep = (EndpointMetadata) readObject(xml); - Assert.assertNotNull(ep); - Assert.assertEquals("1", ep.getNode()); - Assert.assertEquals(null, ep.getId()); - Assert.assertEquals(null, ep.getDescription()); - Assert.assertEquals(null, ep.getPort()); - Assert.assertEquals(EndpointType.OUT, ep.getEndpointType()); - } - - @Test - public void testTypeUndir() throws Exception { - - String xml = - "" + - ""; - - EndpointMetadata ep = (EndpointMetadata) readObject(xml); - Assert.assertNotNull(ep); - Assert.assertEquals("1", ep.getNode()); - Assert.assertEquals(null, ep.getId()); - Assert.assertEquals(null, ep.getDescription()); - Assert.assertEquals(null, ep.getPort()); - Assert.assertEquals(EndpointType.UNDIR, ep.getEndpointType()); - } - - @Test - public void testTypeInvalid() throws Exception { - - String xml = - "" + - ""; - - EndpointMetadata ep = (EndpointMetadata) readObject(xml); - Assert.assertNotNull(ep); - Assert.assertEquals("1", ep.getNode()); - Assert.assertEquals(null, ep.getId()); - Assert.assertEquals(null, ep.getDescription()); - Assert.assertEquals(null, ep.getPort()); - Assert.assertEquals(EndpointType.UNDIR, ep.getEndpointType()); - } + @Test(expected = GraphIOException.class) + public void testNoNode() throws Exception { + + String xml = ""; + + readObject(xml); + } + + @Test + public void testId() throws Exception { + + String xml = ""; + + EndpointMetadata ep = (EndpointMetadata) readObject(xml); + Assert.assertNotNull(ep); + Assert.assertEquals("1", ep.getNode()); + Assert.assertEquals("ep1", ep.getId()); + Assert.assertEquals(null, ep.getDescription()); + Assert.assertEquals(null, ep.getPort()); + Assert.assertEquals(EndpointType.UNDIR, ep.getEndpointType()); + } + + @Test + public void testDesc() throws Exception { + + String xml = "" + "hello world" + ""; + + EndpointMetadata ep = (EndpointMetadata) readObject(xml); + Assert.assertNotNull(ep); + Assert.assertEquals("1", ep.getNode()); + Assert.assertEquals("ep1", ep.getId()); + Assert.assertEquals("hello world", ep.getDescription()); + Assert.assertEquals(null, ep.getPort()); + Assert.assertEquals(EndpointType.UNDIR, ep.getEndpointType()); + } + + @Test + public void testPort() throws Exception { + + String xml = "" + ""; + + EndpointMetadata ep = (EndpointMetadata) readObject(xml); + Assert.assertNotNull(ep); + Assert.assertEquals("1", ep.getNode()); + Assert.assertEquals("ep1", ep.getId()); + Assert.assertEquals(null, ep.getDescription()); + Assert.assertEquals("abc123", ep.getPort()); + Assert.assertEquals(EndpointType.UNDIR, ep.getEndpointType()); + } + + @Test + public void testTypeIn() throws Exception { + + String xml = "" + ""; + + EndpointMetadata ep = (EndpointMetadata) readObject(xml); + Assert.assertNotNull(ep); + Assert.assertEquals("1", ep.getNode()); + Assert.assertEquals(null, ep.getId()); + Assert.assertEquals(null, ep.getDescription()); + Assert.assertEquals(null, ep.getPort()); + Assert.assertEquals(EndpointType.IN, ep.getEndpointType()); + } + + @Test + public void testTypeOut() throws Exception { + + String xml = "" + ""; + + EndpointMetadata ep = (EndpointMetadata) readObject(xml); + Assert.assertNotNull(ep); + Assert.assertEquals("1", ep.getNode()); + Assert.assertEquals(null, ep.getId()); + Assert.assertEquals(null, ep.getDescription()); + Assert.assertEquals(null, ep.getPort()); + Assert.assertEquals(EndpointType.OUT, ep.getEndpointType()); + } + + @Test + public void testTypeUndir() throws Exception { + + String xml = "" + ""; + + EndpointMetadata ep = (EndpointMetadata) readObject(xml); + Assert.assertNotNull(ep); + Assert.assertEquals("1", ep.getNode()); + Assert.assertEquals(null, ep.getId()); + Assert.assertEquals(null, ep.getDescription()); + Assert.assertEquals(null, ep.getPort()); + Assert.assertEquals(EndpointType.UNDIR, ep.getEndpointType()); + } + + @Test + public void testTypeInvalid() throws Exception { + + String xml = "" + ""; + + EndpointMetadata ep = (EndpointMetadata) readObject(xml); + Assert.assertNotNull(ep); + Assert.assertEquals("1", ep.getNode()); + Assert.assertEquals(null, ep.getId()); + Assert.assertEquals(null, ep.getDescription()); + Assert.assertEquals(null, ep.getPort()); + Assert.assertEquals(EndpointType.UNDIR, ep.getEndpointType()); + } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestGraphElementParser.java b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestGraphElementParser.java index 4f4a591e..963c3783 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestGraphElementParser.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestGraphElementParser.java @@ -10,198 +10,192 @@ package edu.uci.ics.jung.io.graphml.parser; +import edu.uci.ics.jung.io.GraphIOException; +import edu.uci.ics.jung.io.graphml.EdgeMetadata; +import edu.uci.ics.jung.io.graphml.GraphMetadata; +import edu.uci.ics.jung.io.graphml.GraphMetadata.EdgeDefault; +import edu.uci.ics.jung.io.graphml.NodeMetadata; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; - import org.junit.Assert; import org.junit.Test; -import edu.uci.ics.jung.io.GraphIOException; -import edu.uci.ics.jung.io.graphml.EdgeMetadata; -import edu.uci.ics.jung.io.graphml.GraphMetadata; -import edu.uci.ics.jung.io.graphml.NodeMetadata; -import edu.uci.ics.jung.io.graphml.GraphMetadata.EdgeDefault; - public class TestGraphElementParser extends AbstractParserTest { - @Test(expected= GraphIOException.class) - public void testNoEdgeDefault() throws Exception { - - String xml = - ""; - - readObject(xml); - } - - @Test - public void testEdgeDefaultDirected() throws Exception { - - String xml = - ""; - - GraphMetadata g = (GraphMetadata) readObject(xml); - Assert.assertNotNull(g); - Assert.assertEquals(EdgeDefault.DIRECTED, g.getEdgeDefault()); - Assert.assertEquals(null, g.getId()); - Assert.assertEquals(null, g.getDescription()); - Assert.assertEquals(0, g.getNodeMap().size()); - Assert.assertEquals(0, g.getEdgeMap().size()); - Assert.assertEquals(0, g.getHyperEdgeMap().size()); - } - - @Test - public void testEdgeDefaultUndirected() throws Exception { - - String xml = - ""; - - GraphMetadata g = (GraphMetadata) readObject(xml); - Assert.assertNotNull(g); - Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); - Assert.assertEquals(null, g.getId()); - Assert.assertEquals(null, g.getDescription()); - Assert.assertEquals(0, g.getNodeMap().size()); - Assert.assertEquals(0, g.getEdgeMap().size()); - Assert.assertEquals(0, g.getHyperEdgeMap().size()); - } - - @Test - public void testDesc() throws Exception { - - String xml = - "" + - "hello world" + - ""; - - GraphMetadata g = (GraphMetadata) readObject(xml); - Assert.assertNotNull(g); - Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); - Assert.assertEquals(null, g.getId()); - Assert.assertEquals("hello world", g.getDescription()); - Assert.assertEquals(0, g.getNodeMap().size()); - Assert.assertEquals(0, g.getEdgeMap().size()); - Assert.assertEquals(0, g.getHyperEdgeMap().size()); - } - - @Test - public void testNodes() throws Exception { - - String xml = - "" + - "" + - "" + - "" + - ""; - - GraphMetadata g = (GraphMetadata) readObject(xml); - Assert.assertNotNull(g); - Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); - Assert.assertEquals(null, g.getId()); - Assert.assertEquals(null, g.getDescription()); - Assert.assertEquals(3, g.getNodeMap().size()); - List nodes = new ArrayList(g.getNodeMap().values()); - Collections.sort(nodes, new Comparator() { - public int compare(NodeMetadata o1, NodeMetadata o2) { - return o1.getId().compareTo(o2.getId()); - } + @Test(expected = GraphIOException.class) + public void testNoEdgeDefault() throws Exception { + + String xml = ""; + + readObject(xml); + } + + @Test + public void testEdgeDefaultDirected() throws Exception { + + String xml = ""; + + GraphMetadata g = (GraphMetadata) readObject(xml); + Assert.assertNotNull(g); + Assert.assertEquals(EdgeDefault.DIRECTED, g.getEdgeDefault()); + Assert.assertEquals(null, g.getId()); + Assert.assertEquals(null, g.getDescription()); + Assert.assertEquals(0, g.getNodeMap().size()); + Assert.assertEquals(0, g.getEdgeMap().size()); + Assert.assertEquals(0, g.getHyperEdgeMap().size()); + } + + @Test + public void testEdgeDefaultUndirected() throws Exception { + + String xml = ""; + + GraphMetadata g = (GraphMetadata) readObject(xml); + Assert.assertNotNull(g); + Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); + Assert.assertEquals(null, g.getId()); + Assert.assertEquals(null, g.getDescription()); + Assert.assertEquals(0, g.getNodeMap().size()); + Assert.assertEquals(0, g.getEdgeMap().size()); + Assert.assertEquals(0, g.getHyperEdgeMap().size()); + } + + @Test + public void testDesc() throws Exception { + + String xml = "" + "hello world" + ""; + + GraphMetadata g = (GraphMetadata) readObject(xml); + Assert.assertNotNull(g); + Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); + Assert.assertEquals(null, g.getId()); + Assert.assertEquals("hello world", g.getDescription()); + Assert.assertEquals(0, g.getNodeMap().size()); + Assert.assertEquals(0, g.getEdgeMap().size()); + Assert.assertEquals(0, g.getHyperEdgeMap().size()); + } + + @Test + public void testNodes() throws Exception { + + String xml = + "" + + "" + + "" + + "" + + ""; + + GraphMetadata g = (GraphMetadata) readObject(xml); + Assert.assertNotNull(g); + Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); + Assert.assertEquals(null, g.getId()); + Assert.assertEquals(null, g.getDescription()); + Assert.assertEquals(3, g.getNodeMap().size()); + List nodes = new ArrayList(g.getNodeMap().values()); + Collections.sort( + nodes, + new Comparator() { + public int compare(NodeMetadata o1, NodeMetadata o2) { + return o1.getId().compareTo(o2.getId()); + } }); - Assert.assertEquals("1", nodes.get(0).getId()); - Assert.assertEquals("2", nodes.get(1).getId()); - Assert.assertEquals("3", nodes.get(2).getId()); - } - - @Test - public void testEdges() throws Exception { - - String xml = - "" + - "" + - "" + - "" + - "" + - "" + - ""; - - GraphMetadata g = (GraphMetadata) readObject(xml); - Assert.assertNotNull(g); - Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); - Assert.assertEquals(null, g.getId()); - Assert.assertEquals(null, g.getDescription()); - List edges = new ArrayList(g.getEdgeMap().values()); - Collections.sort(edges, new Comparator() { - public int compare(EdgeMetadata o1, EdgeMetadata o2) { - return o1.getSource().compareTo(o2.getSource()); - } + Assert.assertEquals("1", nodes.get(0).getId()); + Assert.assertEquals("2", nodes.get(1).getId()); + Assert.assertEquals("3", nodes.get(2).getId()); + } + + @Test + public void testEdges() throws Exception { + + String xml = + "" + + "" + + "" + + "" + + "" + + "" + + ""; + + GraphMetadata g = (GraphMetadata) readObject(xml); + Assert.assertNotNull(g); + Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); + Assert.assertEquals(null, g.getId()); + Assert.assertEquals(null, g.getDescription()); + List edges = new ArrayList(g.getEdgeMap().values()); + Collections.sort( + edges, + new Comparator() { + public int compare(EdgeMetadata o1, EdgeMetadata o2) { + return o1.getSource().compareTo(o2.getSource()); + } }); - Assert.assertEquals(2, edges.size()); - Assert.assertEquals("1", edges.get(0).getSource()); - Assert.assertEquals("2", edges.get(1).getSource()); - } - -// @Test -// public void testHyperEdges() throws Exception { -// -// String xml = -// "" + -// "" + -// "" + -// "" + -// "" + -// "" + -// "" + -// "" + -// "" + -// "" + -// "" + -// "" + -// "" + -// "" + -// "" + -// "" + -// ""; -// -// GraphMetadata g = (GraphMetadata) readObject(xml); -// Assert.assertNotNull(g); -// Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); -// Assert.assertEquals(null, g.getId()); -// Assert.assertEquals(null, g.getDescription()); -// Assert.assertEquals(3, g.getHyperEdgeMap().size()); -// } - - @Test - public void testUserAttributes() throws Exception { - - String xml = - "" + - ""; - - GraphMetadata g = (GraphMetadata) readObject(xml); - Assert.assertNotNull(g); - Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); - Assert.assertEquals(null, g.getId()); - Assert.assertEquals(null, g.getDescription()); - Assert.assertEquals(1, g.getProperties().size()); - Assert.assertEquals("abc123", g.getProperty("bob")); - } - - @Test - public void testData() throws Exception { - - String xml = - "" + - "value1" + - "value2" + - ""; - - GraphMetadata g = (GraphMetadata) readObject(xml); - Assert.assertNotNull(g); - Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); - Assert.assertEquals(null, g.getId()); - Assert.assertEquals(null, g.getDescription()); - Assert.assertEquals(2, g.getProperties().size()); - Assert.assertEquals("value1", g.getProperty("d1")); - Assert.assertEquals("value2", g.getProperty("d2")); - } + Assert.assertEquals(2, edges.size()); + Assert.assertEquals("1", edges.get(0).getSource()); + Assert.assertEquals("2", edges.get(1).getSource()); + } + + // @Test + // public void testHyperEdges() throws Exception { + // + // String xml = + // "" + + // "" + + // "" + + // "" + + // "" + + // "" + + // "" + + // "" + + // "" + + // "" + + // "" + + // "" + + // "" + + // "" + + // "" + + // "" + + // ""; + // + // GraphMetadata g = (GraphMetadata) readObject(xml); + // Assert.assertNotNull(g); + // Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); + // Assert.assertEquals(null, g.getId()); + // Assert.assertEquals(null, g.getDescription()); + // Assert.assertEquals(3, g.getHyperEdgeMap().size()); + // } + + @Test + public void testUserAttributes() throws Exception { + + String xml = "" + ""; + + GraphMetadata g = (GraphMetadata) readObject(xml); + Assert.assertNotNull(g); + Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); + Assert.assertEquals(null, g.getId()); + Assert.assertEquals(null, g.getDescription()); + Assert.assertEquals(1, g.getProperties().size()); + Assert.assertEquals("abc123", g.getProperty("bob")); + } + + @Test + public void testData() throws Exception { + + String xml = + "" + + "value1" + + "value2" + + ""; + + GraphMetadata g = (GraphMetadata) readObject(xml); + Assert.assertNotNull(g); + Assert.assertEquals(EdgeDefault.UNDIRECTED, g.getEdgeDefault()); + Assert.assertEquals(null, g.getId()); + Assert.assertEquals(null, g.getDescription()); + Assert.assertEquals(2, g.getProperties().size()); + Assert.assertEquals("value1", g.getProperty("d1")); + Assert.assertEquals("value2", g.getProperty("d2")); + } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestHyperEdgeElementParser.java b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestHyperEdgeElementParser.java index d2a77dad..20e05cf8 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestHyperEdgeElementParser.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestHyperEdgeElementParser.java @@ -10,105 +10,100 @@ package edu.uci.ics.jung.io.graphml.parser; -import org.junit.Assert; -import org.junit.Test; - -import edu.uci.ics.jung.io.graphml.HyperEdgeMetadata; - public class TestHyperEdgeElementParser extends AbstractParserTest { -// @Test -// public void testEmpty() throws Exception { -// -// String xml = -// ""; -// -// HyperEdgeMetadata edge = (HyperEdgeMetadata) readObject(xml); -// Assert.assertNotNull(edge); -// Assert.assertEquals(null, edge.getId()); -// Assert.assertEquals(null, edge.getDescription()); -// Assert.assertEquals(0, edge.getEndpoints().size()); -// } -// -// @Test -// public void testId() throws Exception { -// -// String xml = -// ""; -// -// HyperEdgeMetadata edge = (HyperEdgeMetadata) readObject(xml); -// Assert.assertNotNull(edge); -// Assert.assertEquals("e1", edge.getId()); -// Assert.assertEquals(null, edge.getDescription()); -// Assert.assertEquals(0, edge.getEndpoints().size()); -// } -// -// @Test -// public void testDesc() throws Exception { -// -// String xml = -// "" + -// "hello world" + -// ""; -// -// HyperEdgeMetadata edge = (HyperEdgeMetadata) readObject(xml); -// Assert.assertNotNull(edge); -// Assert.assertEquals(null, edge.getId()); -// Assert.assertEquals("hello world", edge.getDescription()); -// Assert.assertEquals(0, edge.getEndpoints().size()); -// } -// -// @Test -// public void testEndpoints() throws Exception { -// -// String xml = -// "" + -// "" + -// "" + -// "" + -// ""; -// -// HyperEdgeMetadata edge = (HyperEdgeMetadata) readObject(xml); -// Assert.assertNotNull(edge); -// Assert.assertEquals(null, edge.getId()); -// Assert.assertEquals(null, edge.getDescription()); -// Assert.assertEquals(3, edge.getEndpoints().size()); -// Assert.assertEquals("1", edge.getEndpoints().get(0).getNode()); -// Assert.assertEquals("2", edge.getEndpoints().get(1).getNode()); -// Assert.assertEquals("3", edge.getEndpoints().get(2).getNode()); -// } -// -// @Test -// public void testUserAttributes() throws Exception { -// -// String xml = -// ""; -// -// HyperEdgeMetadata edge = (HyperEdgeMetadata) readObject(xml); -// Assert.assertNotNull(edge); -// Assert.assertEquals(null, edge.getId()); -// Assert.assertEquals(null, edge.getDescription()); -// Assert.assertEquals(0, edge.getEndpoints().size()); -// Assert.assertEquals(1, edge.getProperties().size()); -// Assert.assertEquals("abc123", edge.getProperty("bob")); -// } -// -// @Test -// public void testData() throws Exception { -// -// String xml = -// "" + -// "value1" + -// "value2" + -// ""; -// -// HyperEdgeMetadata edge = (HyperEdgeMetadata) readObject(xml); -// Assert.assertNotNull(edge); -// Assert.assertEquals(null, edge.getId()); -// Assert.assertEquals(null, edge.getDescription()); -// Assert.assertEquals(0, edge.getEndpoints().size()); -// Assert.assertEquals(2, edge.getProperties().size()); -// Assert.assertEquals("value1", edge.getProperty("d1")); -// Assert.assertEquals("value2", edge.getProperty("d2")); -// } + // @Test + // public void testEmpty() throws Exception { + // + // String xml = + // ""; + // + // HyperEdgeMetadata edge = (HyperEdgeMetadata) readObject(xml); + // Assert.assertNotNull(edge); + // Assert.assertEquals(null, edge.getId()); + // Assert.assertEquals(null, edge.getDescription()); + // Assert.assertEquals(0, edge.getEndpoints().size()); + // } + // + // @Test + // public void testId() throws Exception { + // + // String xml = + // ""; + // + // HyperEdgeMetadata edge = (HyperEdgeMetadata) readObject(xml); + // Assert.assertNotNull(edge); + // Assert.assertEquals("e1", edge.getId()); + // Assert.assertEquals(null, edge.getDescription()); + // Assert.assertEquals(0, edge.getEndpoints().size()); + // } + // + // @Test + // public void testDesc() throws Exception { + // + // String xml = + // "" + + // "hello world" + + // ""; + // + // HyperEdgeMetadata edge = (HyperEdgeMetadata) readObject(xml); + // Assert.assertNotNull(edge); + // Assert.assertEquals(null, edge.getId()); + // Assert.assertEquals("hello world", edge.getDescription()); + // Assert.assertEquals(0, edge.getEndpoints().size()); + // } + // + // @Test + // public void testEndpoints() throws Exception { + // + // String xml = + // "" + + // "" + + // "" + + // "" + + // ""; + // + // HyperEdgeMetadata edge = (HyperEdgeMetadata) readObject(xml); + // Assert.assertNotNull(edge); + // Assert.assertEquals(null, edge.getId()); + // Assert.assertEquals(null, edge.getDescription()); + // Assert.assertEquals(3, edge.getEndpoints().size()); + // Assert.assertEquals("1", edge.getEndpoints().get(0).getNode()); + // Assert.assertEquals("2", edge.getEndpoints().get(1).getNode()); + // Assert.assertEquals("3", edge.getEndpoints().get(2).getNode()); + // } + // + // @Test + // public void testUserAttributes() throws Exception { + // + // String xml = + // ""; + // + // HyperEdgeMetadata edge = (HyperEdgeMetadata) readObject(xml); + // Assert.assertNotNull(edge); + // Assert.assertEquals(null, edge.getId()); + // Assert.assertEquals(null, edge.getDescription()); + // Assert.assertEquals(0, edge.getEndpoints().size()); + // Assert.assertEquals(1, edge.getProperties().size()); + // Assert.assertEquals("abc123", edge.getProperty("bob")); + // } + // + // @Test + // public void testData() throws Exception { + // + // String xml = + // "" + + // "value1" + + // "value2" + + // ""; + // + // HyperEdgeMetadata edge = (HyperEdgeMetadata) readObject(xml); + // Assert.assertNotNull(edge); + // Assert.assertEquals(null, edge.getId()); + // Assert.assertEquals(null, edge.getDescription()); + // Assert.assertEquals(0, edge.getEndpoints().size()); + // Assert.assertEquals(2, edge.getProperties().size()); + // Assert.assertEquals("value1", edge.getProperty("d1")); + // Assert.assertEquals("value2", edge.getProperty("d2")); + // } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestKeyElementParser.java b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestKeyElementParser.java index fb21bbde..bcc92ce9 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestKeyElementParser.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestKeyElementParser.java @@ -10,157 +10,138 @@ package edu.uci.ics.jung.io.graphml.parser; -import org.junit.Assert; -import org.junit.Test; - import edu.uci.ics.jung.io.GraphIOException; import edu.uci.ics.jung.io.graphml.Key; +import org.junit.Assert; +import org.junit.Test; public class TestKeyElementParser extends AbstractParserTest { - @Test(expected= GraphIOException.class) - public void testNoId() throws Exception { - - String xml = - ""; - - readObject(xml); - } - - @Test - public void testId() throws Exception { - - String xml = - ""; - - Key key = (Key) readObject(xml); - Assert.assertNotNull(key); - Assert.assertEquals("d1", key.getId()); - Assert.assertEquals(null, key.getDescription()); - Assert.assertEquals(null, key.getDefaultValue()); - Assert.assertEquals(null, key.getAttributeName()); - Assert.assertEquals(null, key.getAttributeType()); - Assert.assertEquals(Key.ForType.ALL, key.getForType()); - } - - @Test - public void testDesc() throws Exception { - - String xml = - "" + - "this is my key" + - ""; - - Key key = (Key) readObject(xml); - Assert.assertNotNull(key); - Assert.assertEquals("d1", key.getId()); - Assert.assertEquals("this is my key", key.getDescription()); - Assert.assertEquals(null, key.getDefaultValue()); - Assert.assertEquals(null, key.getAttributeName()); - Assert.assertEquals(null, key.getAttributeType()); - Assert.assertEquals(Key.ForType.ALL, key.getForType()); - } - - @Test - public void testDefault() throws Exception { - - String xml = - "" + - "yellow" + - ""; - - Key key = (Key) readObject(xml); - Assert.assertNotNull(key); - Assert.assertEquals("d1", key.getId()); - Assert.assertEquals(null, key.getDescription()); - Assert.assertEquals("yellow", key.getDefaultValue()); - Assert.assertEquals(null, key.getAttributeName()); - Assert.assertEquals(null, key.getAttributeType()); - Assert.assertEquals(Key.ForType.ALL, key.getForType()); - } - - @Test - public void testAttrNameType() throws Exception { - - String xml = - "" + - ""; - - Key key = (Key) readObject(xml); - Assert.assertNotNull(key); - Assert.assertEquals("d1", key.getId()); - Assert.assertEquals(null, key.getDescription()); - Assert.assertEquals(null, key.getDefaultValue()); - Assert.assertEquals("myattr", key.getAttributeName()); - Assert.assertEquals("double", key.getAttributeType()); - Assert.assertEquals(Key.ForType.ALL, key.getForType()); - } - - @Test - public void testForNode() throws Exception { - - String xml = - "" + - ""; - - Key key = (Key) readObject(xml); - Assert.assertNotNull(key); - Assert.assertEquals("d1", key.getId()); - Assert.assertEquals(null, key.getDescription()); - Assert.assertEquals(null, key.getDefaultValue()); - Assert.assertEquals(null, key.getAttributeName()); - Assert.assertEquals(null, key.getAttributeType()); - Assert.assertEquals(Key.ForType.NODE, key.getForType()); - } - - @Test - public void testForEdge() throws Exception { - - String xml = - "" + - ""; - - Key key = (Key) readObject(xml); - Assert.assertNotNull(key); - Assert.assertEquals("d1", key.getId()); - Assert.assertEquals(null, key.getDescription()); - Assert.assertEquals(null, key.getDefaultValue()); - Assert.assertEquals(null, key.getAttributeName()); - Assert.assertEquals(null, key.getAttributeType()); - Assert.assertEquals(Key.ForType.EDGE, key.getForType()); - } - - @Test - public void testForGraph() throws Exception { - - String xml = - "" + - ""; - - Key key = (Key) readObject(xml); - Assert.assertNotNull(key); - Assert.assertEquals("d1", key.getId()); - Assert.assertEquals(null, key.getDescription()); - Assert.assertEquals(null, key.getDefaultValue()); - Assert.assertEquals(null, key.getAttributeName()); - Assert.assertEquals(null, key.getAttributeType()); - Assert.assertEquals(Key.ForType.GRAPH, key.getForType()); - } - - @Test - public void testForAll() throws Exception { - - String xml = - "" + - ""; - - Key key = (Key) readObject(xml); - Assert.assertNotNull(key); - Assert.assertEquals("d1", key.getId()); - Assert.assertEquals(null, key.getDescription()); - Assert.assertEquals(null, key.getDefaultValue()); - Assert.assertEquals(null, key.getAttributeName()); - Assert.assertEquals(null, key.getAttributeType()); - Assert.assertEquals(Key.ForType.ALL, key.getForType()); - } + @Test(expected = GraphIOException.class) + public void testNoId() throws Exception { + + String xml = ""; + + readObject(xml); + } + + @Test + public void testId() throws Exception { + + String xml = ""; + + Key key = (Key) readObject(xml); + Assert.assertNotNull(key); + Assert.assertEquals("d1", key.getId()); + Assert.assertEquals(null, key.getDescription()); + Assert.assertEquals(null, key.getDefaultValue()); + Assert.assertEquals(null, key.getAttributeName()); + Assert.assertEquals(null, key.getAttributeType()); + Assert.assertEquals(Key.ForType.ALL, key.getForType()); + } + + @Test + public void testDesc() throws Exception { + + String xml = "" + "this is my key" + ""; + + Key key = (Key) readObject(xml); + Assert.assertNotNull(key); + Assert.assertEquals("d1", key.getId()); + Assert.assertEquals("this is my key", key.getDescription()); + Assert.assertEquals(null, key.getDefaultValue()); + Assert.assertEquals(null, key.getAttributeName()); + Assert.assertEquals(null, key.getAttributeType()); + Assert.assertEquals(Key.ForType.ALL, key.getForType()); + } + + @Test + public void testDefault() throws Exception { + + String xml = "" + "yellow" + ""; + + Key key = (Key) readObject(xml); + Assert.assertNotNull(key); + Assert.assertEquals("d1", key.getId()); + Assert.assertEquals(null, key.getDescription()); + Assert.assertEquals("yellow", key.getDefaultValue()); + Assert.assertEquals(null, key.getAttributeName()); + Assert.assertEquals(null, key.getAttributeType()); + Assert.assertEquals(Key.ForType.ALL, key.getForType()); + } + + @Test + public void testAttrNameType() throws Exception { + + String xml = "" + ""; + + Key key = (Key) readObject(xml); + Assert.assertNotNull(key); + Assert.assertEquals("d1", key.getId()); + Assert.assertEquals(null, key.getDescription()); + Assert.assertEquals(null, key.getDefaultValue()); + Assert.assertEquals("myattr", key.getAttributeName()); + Assert.assertEquals("double", key.getAttributeType()); + Assert.assertEquals(Key.ForType.ALL, key.getForType()); + } + + @Test + public void testForNode() throws Exception { + + String xml = "" + ""; + + Key key = (Key) readObject(xml); + Assert.assertNotNull(key); + Assert.assertEquals("d1", key.getId()); + Assert.assertEquals(null, key.getDescription()); + Assert.assertEquals(null, key.getDefaultValue()); + Assert.assertEquals(null, key.getAttributeName()); + Assert.assertEquals(null, key.getAttributeType()); + Assert.assertEquals(Key.ForType.NODE, key.getForType()); + } + + @Test + public void testForEdge() throws Exception { + + String xml = "" + ""; + + Key key = (Key) readObject(xml); + Assert.assertNotNull(key); + Assert.assertEquals("d1", key.getId()); + Assert.assertEquals(null, key.getDescription()); + Assert.assertEquals(null, key.getDefaultValue()); + Assert.assertEquals(null, key.getAttributeName()); + Assert.assertEquals(null, key.getAttributeType()); + Assert.assertEquals(Key.ForType.EDGE, key.getForType()); + } + + @Test + public void testForGraph() throws Exception { + + String xml = "" + ""; + + Key key = (Key) readObject(xml); + Assert.assertNotNull(key); + Assert.assertEquals("d1", key.getId()); + Assert.assertEquals(null, key.getDescription()); + Assert.assertEquals(null, key.getDefaultValue()); + Assert.assertEquals(null, key.getAttributeName()); + Assert.assertEquals(null, key.getAttributeType()); + Assert.assertEquals(Key.ForType.GRAPH, key.getForType()); + } + + @Test + public void testForAll() throws Exception { + + String xml = "" + ""; + + Key key = (Key) readObject(xml); + Assert.assertNotNull(key); + Assert.assertEquals("d1", key.getId()); + Assert.assertEquals(null, key.getDescription()); + Assert.assertEquals(null, key.getDefaultValue()); + Assert.assertEquals(null, key.getAttributeName()); + Assert.assertEquals(null, key.getAttributeType()); + Assert.assertEquals(Key.ForType.ALL, key.getForType()); + } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestNodeElementParser.java b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestNodeElementParser.java index d0ec22a4..ad57f03c 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestNodeElementParser.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestNodeElementParser.java @@ -10,122 +10,115 @@ package edu.uci.ics.jung.io.graphml.parser; -import org.junit.Assert; -import org.junit.Test; - import edu.uci.ics.jung.io.GraphIOException; import edu.uci.ics.jung.io.graphml.NodeMetadata; +import org.junit.Assert; +import org.junit.Test; public class TestNodeElementParser extends AbstractParserTest { - @Test(expected= GraphIOException.class) - public void testNoId() throws Exception { - - String xml = - ""; - - readObject(xml); - } - - @Test - public void testId() throws Exception { - - String xml = - ""; - - NodeMetadata node = (NodeMetadata) readObject(xml); - Assert.assertNotNull(node); - Assert.assertEquals("1", node.getId()); - Assert.assertEquals(null, node.getDescription()); - Assert.assertEquals(0, node.getPorts().size()); - } - - @Test - public void testDesc() throws Exception { - - String xml = - "" + - "this is my node" + - ""; - - NodeMetadata node = (NodeMetadata) readObject(xml); - Assert.assertNotNull(node); - Assert.assertEquals("1", node.getId()); - Assert.assertEquals("this is my node", node.getDescription()); - Assert.assertEquals(0, node.getPorts().size()); - } - - @Test - public void testPort() throws Exception { - - String xml = - "" + - "this is my node" + - "" + - "port 1" + - "" + - ""; - - NodeMetadata node = (NodeMetadata) readObject(xml); - Assert.assertNotNull(node); - Assert.assertEquals("1", node.getId()); - Assert.assertEquals("this is my node", node.getDescription()); - Assert.assertEquals(1, node.getPorts().size()); - Assert.assertEquals("p1", node.getPorts().get(0).getName()); - } - - @Test - public void testMultiPort() throws Exception { - - String xml = - "" + - "this is my node" + - "" + - "" + - "" + - "" + - ""; - - NodeMetadata node = (NodeMetadata) readObject(xml); - Assert.assertNotNull(node); - Assert.assertEquals("1", node.getId()); - Assert.assertEquals("this is my node", node.getDescription()); - Assert.assertEquals(4, node.getPorts().size()); - Assert.assertEquals("p1", node.getPorts().get(0).getName()); - Assert.assertEquals("p2", node.getPorts().get(1).getName()); - Assert.assertEquals("p3", node.getPorts().get(2).getName()); - Assert.assertEquals("p4", node.getPorts().get(3).getName()); - } - - @Test - public void testUserAttributes() throws Exception { - - String xml = - ""; - - NodeMetadata node = (NodeMetadata) readObject(xml); - Assert.assertNotNull(node); - Assert.assertEquals("1", node.getId()); - Assert.assertEquals(1, node.getProperties().size()); - Assert.assertEquals("abc123", node.getProperty("bob")); - Assert.assertEquals(0, node.getPorts().size()); - } - - @Test - public void testData() throws Exception { - - String xml = - "" + - "value1" + - "value2" + - ""; - - NodeMetadata node = (NodeMetadata) readObject(xml); - Assert.assertNotNull(node); - Assert.assertEquals("1", node.getId()); - Assert.assertEquals(2, node.getProperties().size()); - Assert.assertEquals("value1", node.getProperty("d1")); - Assert.assertEquals("value2", node.getProperty("d2")); - Assert.assertEquals(0, node.getPorts().size()); - } + @Test(expected = GraphIOException.class) + public void testNoId() throws Exception { + + String xml = ""; + + readObject(xml); + } + + @Test + public void testId() throws Exception { + + String xml = ""; + + NodeMetadata node = (NodeMetadata) readObject(xml); + Assert.assertNotNull(node); + Assert.assertEquals("1", node.getId()); + Assert.assertEquals(null, node.getDescription()); + Assert.assertEquals(0, node.getPorts().size()); + } + + @Test + public void testDesc() throws Exception { + + String xml = "" + "this is my node" + ""; + + NodeMetadata node = (NodeMetadata) readObject(xml); + Assert.assertNotNull(node); + Assert.assertEquals("1", node.getId()); + Assert.assertEquals("this is my node", node.getDescription()); + Assert.assertEquals(0, node.getPorts().size()); + } + + @Test + public void testPort() throws Exception { + + String xml = + "" + + "this is my node" + + "" + + "port 1" + + "" + + ""; + + NodeMetadata node = (NodeMetadata) readObject(xml); + Assert.assertNotNull(node); + Assert.assertEquals("1", node.getId()); + Assert.assertEquals("this is my node", node.getDescription()); + Assert.assertEquals(1, node.getPorts().size()); + Assert.assertEquals("p1", node.getPorts().get(0).getName()); + } + + @Test + public void testMultiPort() throws Exception { + + String xml = + "" + + "this is my node" + + "" + + "" + + "" + + "" + + ""; + + NodeMetadata node = (NodeMetadata) readObject(xml); + Assert.assertNotNull(node); + Assert.assertEquals("1", node.getId()); + Assert.assertEquals("this is my node", node.getDescription()); + Assert.assertEquals(4, node.getPorts().size()); + Assert.assertEquals("p1", node.getPorts().get(0).getName()); + Assert.assertEquals("p2", node.getPorts().get(1).getName()); + Assert.assertEquals("p3", node.getPorts().get(2).getName()); + Assert.assertEquals("p4", node.getPorts().get(3).getName()); + } + + @Test + public void testUserAttributes() throws Exception { + + String xml = ""; + + NodeMetadata node = (NodeMetadata) readObject(xml); + Assert.assertNotNull(node); + Assert.assertEquals("1", node.getId()); + Assert.assertEquals(1, node.getProperties().size()); + Assert.assertEquals("abc123", node.getProperty("bob")); + Assert.assertEquals(0, node.getPorts().size()); + } + + @Test + public void testData() throws Exception { + + String xml = + "" + + "value1" + + "value2" + + ""; + + NodeMetadata node = (NodeMetadata) readObject(xml); + Assert.assertNotNull(node); + Assert.assertEquals("1", node.getId()); + Assert.assertEquals(2, node.getProperties().size()); + Assert.assertEquals("value1", node.getProperty("d1")); + Assert.assertEquals("value2", node.getProperty("d2")); + Assert.assertEquals(0, node.getPorts().size()); + } } diff --git a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestPortElementParser.java b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestPortElementParser.java index 75e5fa03..1a957589 100644 --- a/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestPortElementParser.java +++ b/jung-io/src/test/java/edu/uci/ics/jung/io/graphml/parser/TestPortElementParser.java @@ -10,76 +10,69 @@ package edu.uci.ics.jung.io.graphml.parser; -import org.junit.Assert; -import org.junit.Test; - import edu.uci.ics.jung.io.GraphIOException; import edu.uci.ics.jung.io.graphml.PortMetadata; +import org.junit.Assert; +import org.junit.Test; public class TestPortElementParser extends AbstractParserTest { - @Test(expected= GraphIOException.class) - public void testNoName() throws Exception { - - String xml = - ""; - - readObject(xml); - } - - @Test - public void testName() throws Exception { - - String xml = - ""; - - PortMetadata port = (PortMetadata) readObject(xml); - Assert.assertNotNull(port); - Assert.assertEquals("p1", port.getName()); - Assert.assertEquals(null, port.getDescription()); - } - - @Test - public void testDesc() throws Exception { - - String xml = - "" + - "this is my port" + - ""; - - PortMetadata port = (PortMetadata) readObject(xml); - Assert.assertNotNull(port); - Assert.assertEquals("p1", port.getName()); - Assert.assertEquals("this is my port", port.getDescription()); - } - - @Test - public void testUserAttributes() throws Exception { - - String xml = - ""; - - PortMetadata port = (PortMetadata) readObject(xml); - Assert.assertNotNull(port); - Assert.assertEquals("p1", port.getName()); - Assert.assertEquals(1, port.getProperties().size()); - Assert.assertEquals("abc123", port.getProperty("bob")); - } - - @Test - public void testData() throws Exception { - - String xml = - "" + - "value1" + - "value2" + - ""; - - PortMetadata port = (PortMetadata) readObject(xml); - Assert.assertNotNull(port); - Assert.assertEquals("p1", port.getName()); - Assert.assertEquals(2, port.getProperties().size()); - Assert.assertEquals("value1", port.getProperty("d1")); - Assert.assertEquals("value2", port.getProperty("d2")); - } + @Test(expected = GraphIOException.class) + public void testNoName() throws Exception { + + String xml = ""; + + readObject(xml); + } + + @Test + public void testName() throws Exception { + + String xml = ""; + + PortMetadata port = (PortMetadata) readObject(xml); + Assert.assertNotNull(port); + Assert.assertEquals("p1", port.getName()); + Assert.assertEquals(null, port.getDescription()); + } + + @Test + public void testDesc() throws Exception { + + String xml = "" + "this is my port" + ""; + + PortMetadata port = (PortMetadata) readObject(xml); + Assert.assertNotNull(port); + Assert.assertEquals("p1", port.getName()); + Assert.assertEquals("this is my port", port.getDescription()); + } + + @Test + public void testUserAttributes() throws Exception { + + String xml = ""; + + PortMetadata port = (PortMetadata) readObject(xml); + Assert.assertNotNull(port); + Assert.assertEquals("p1", port.getName()); + Assert.assertEquals(1, port.getProperties().size()); + Assert.assertEquals("abc123", port.getProperty("bob")); + } + + @Test + public void testData() throws Exception { + + String xml = + "" + + "value1" + + "value2" + + ""; + + PortMetadata port = (PortMetadata) readObject(xml); + Assert.assertNotNull(port); + Assert.assertEquals("p1", port.getName()); + Assert.assertEquals(2, port.getProperties().size()); + Assert.assertEquals("value1", port.getProperty("d1")); + Assert.assertEquals("value2", port.getProperty("d2")); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/AnnotationsDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/AnnotationsDemo.java index 8bd8abf5..bb17caec 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/AnnotationsDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/AnnotationsDemo.java @@ -1,31 +1,14 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JApplet; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; - import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.FRLayout; import edu.uci.ics.jung.graph.util.TestGraphs; import edu.uci.ics.jung.visualization.DefaultVisualizationModel; @@ -45,154 +28,172 @@ import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; import edu.uci.ics.jung.visualization.renderers.Renderer; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.BorderFactory; +import javax.swing.JApplet; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; /** * Demonstrates annotation of graph elements. - * + * * @author Tom Nelson - * */ @SuppressWarnings("serial") public class AnnotationsDemo extends JApplet { - static final String instructions = - ""+ - "

    Instructions for Annotations

    "+ - "

    The Annotation Controls allow you to select:"+ - "

      "+ - "
    • Shape"+ - "
    • Color"+ - "
    • Fill (or outline)"+ - "
    • Above or below (UPPER/LOWER) the graph display"+ - "
    "+ - "

    Mouse Button one press starts a Shape,"+ - "

    drag and release to complete."+ - "

    Mouse Button three pops up an input dialog"+ - "

    for text. This will create a text annotation."+ - "

    You may use html for multi-line, etc."+ - "

    You may even use an image tag and image url"+ - "

    to put an image in the annotation."+ - "

    "+ - "

    To remove an annotation, shift-click on it"+ - "

    in the Annotations mode."+ - "

    If there is overlap, the Annotation with center"+ - "

    closest to the mouse point will be removed."; - - JDialog helpDialog; - - Paintable viewGrid; - - /** - * create an instance of a simple graph in two views with controls to - * demo the features. - * - */ - public AnnotationsDemo() { - - // create a simple graph for the demo - Network graph = TestGraphs.getOneComponentGraph(); - - // the preferred sizes for the two views - Dimension preferredSize1 = new Dimension(600,600); - - // create one layout for the graph - FRLayout layout = new FRLayout(graph.asGraph()); - layout.setMaxIterations(500); - - VisualizationModel vm = - new DefaultVisualizationModel(graph, layout, preferredSize1); - - // create 2 views that share the same model - final VisualizationViewer vv = - new VisualizationViewer(vm, preferredSize1); - vv.setBackground(Color.white); - vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(vv.getPickedEdgeState(), Color.black, Color.cyan)); - vv.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(vv.getPickedVertexState(), Color.red, Color.yellow)); - vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR); - - // add default listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - - Container content = getContentPane(); - Container panel = new JPanel(new BorderLayout()); - - GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv); - panel.add(gzsp); - - helpDialog = new JDialog(); - helpDialog.getContentPane().add(new JLabel(instructions)); - - RenderContext rc = vv.getRenderContext(); - AnnotatingGraphMousePlugin annotatingPlugin = - new AnnotatingGraphMousePlugin(rc); - // create a GraphMouse for the main view - // - final AnnotatingModalGraphMouse graphMouse = - new AnnotatingModalGraphMouse(rc, annotatingPlugin); - vv.setGraphMouse(graphMouse); - vv.addKeyListener(graphMouse.getModeKeyListener()); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + static final String instructions = + "" + + "

    Instructions for Annotations

    " + + "

    The Annotation Controls allow you to select:" + + "

      " + + "
    • Shape" + + "
    • Color" + + "
    • Fill (or outline)" + + "
    • Above or below (UPPER/LOWER) the graph display" + + "
    " + + "

    Mouse Button one press starts a Shape," + + "

    drag and release to complete." + + "

    Mouse Button three pops up an input dialog" + + "

    for text. This will create a text annotation." + + "

    You may use html for multi-line, etc." + + "

    You may even use an image tag and image url" + + "

    to put an image in the annotation." + + "

    " + + "

    To remove an annotation, shift-click on it" + + "

    in the Annotations mode." + + "

    If there is overlap, the Annotation with center" + + "

    closest to the mouse point will be removed."; + + JDialog helpDialog; + + Paintable viewGrid; + + /** create an instance of a simple graph in two views with controls to demo the features. */ + public AnnotationsDemo() { + + // create a simple graph for the demo + Network graph = TestGraphs.getOneComponentGraph(); + + // the preferred sizes for the two views + Dimension preferredSize1 = new Dimension(600, 600); + + // create one layout for the graph + FRLayout layout = new FRLayout(graph.asGraph()); + layout.setMaxIterations(500); + + VisualizationModel vm = + new DefaultVisualizationModel(graph, layout, preferredSize1); + + // create 2 views that share the same model + final VisualizationViewer vv = + new VisualizationViewer(vm, preferredSize1); + vv.setBackground(Color.white); + vv.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv.getPickedEdgeState(), Color.black, Color.cyan)); + vv.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer( + vv.getPickedVertexState(), Color.red, Color.yellow)); + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR); + + // add default listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + + Container content = getContentPane(); + Container panel = new JPanel(new BorderLayout()); + + GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv); + panel.add(gzsp); + + helpDialog = new JDialog(); + helpDialog.getContentPane().add(new JLabel(instructions)); + + RenderContext rc = vv.getRenderContext(); + AnnotatingGraphMousePlugin annotatingPlugin = + new AnnotatingGraphMousePlugin(rc); + // create a GraphMouse for the main view + // + final AnnotatingModalGraphMouse graphMouse = + new AnnotatingModalGraphMouse(rc, annotatingPlugin); + vv.setGraphMouse(graphMouse); + vv.addKeyListener(graphMouse.getModeKeyListener()); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - - JComboBox modeBox = graphMouse.getModeComboBox(); - modeBox.setSelectedItem(ModalGraphMouse.Mode.ANNOTATING); - - JButton help = new JButton("Help"); - help.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - helpDialog.pack(); - helpDialog.setVisible(true); - } + + JComboBox modeBox = graphMouse.getModeComboBox(); + modeBox.setSelectedItem(ModalGraphMouse.Mode.ANNOTATING); + + JButton help = new JButton("Help"); + help.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + helpDialog.pack(); + helpDialog.setVisible(true); + } }); - JPanel controls = new JPanel(); - JPanel zoomControls = new JPanel(); - zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom")); - zoomControls.add(plus); - zoomControls.add(minus); - controls.add(zoomControls); - - JPanel modeControls = new JPanel(); - modeControls.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); - modeControls.add(graphMouse.getModeComboBox()); - controls.add(modeControls); - - JPanel annotationControlPanel = new JPanel(); - annotationControlPanel.setBorder(BorderFactory.createTitledBorder("Annotation Controls")); - - AnnotationControls annotationControls = - new AnnotationControls(annotatingPlugin); - - annotationControlPanel.add(annotationControls.getAnnotationsToolBar()); - controls.add(annotationControlPanel); - - JPanel helpControls = new JPanel(); - helpControls.setBorder(BorderFactory.createTitledBorder("Help")); - helpControls.add(help); - controls.add(helpControls); - content.add(panel); - content.add(controls, BorderLayout.SOUTH); - } - - public static void main(String[] args) { - JFrame f = new JFrame(); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - f.getContentPane().add(new AnnotationsDemo()); - f.pack(); - f.setVisible(true); - } + JPanel controls = new JPanel(); + JPanel zoomControls = new JPanel(); + zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom")); + zoomControls.add(plus); + zoomControls.add(minus); + controls.add(zoomControls); + + JPanel modeControls = new JPanel(); + modeControls.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); + modeControls.add(graphMouse.getModeComboBox()); + controls.add(modeControls); + + JPanel annotationControlPanel = new JPanel(); + annotationControlPanel.setBorder(BorderFactory.createTitledBorder("Annotation Controls")); + + AnnotationControls annotationControls = + new AnnotationControls(annotatingPlugin); + + annotationControlPanel.add(annotationControls.getAnnotationsToolBar()); + controls.add(annotationControlPanel); + + JPanel helpControls = new JPanel(); + helpControls.setBorder(BorderFactory.createTitledBorder("Help")); + helpControls.add(help); + controls.add(helpControls); + content.add(panel); + content.add(controls, BorderLayout.SOUTH); + } + + public static void main(String[] args) { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.getContentPane().add(new AnnotationsDemo()); + f.pack(); + f.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/BalloonLayoutDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/BalloonLayoutDemo.java index 04633b66..7d1c7e08 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/BalloonLayoutDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/BalloonLayoutDemo.java @@ -1,41 +1,14 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.GridLayout; -import java.awt.Paint; -import java.awt.Shape; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.awt.geom.AffineTransform; -import java.awt.geom.Ellipse2D; -import java.awt.geom.Point2D; - -import javax.swing.BorderFactory; -import javax.swing.JApplet; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JFrame; -import javax.swing.JPanel; -import javax.swing.JRadioButton; -import javax.swing.JToggleButton; - import com.google.common.base.Functions; - import edu.uci.ics.jung.algorithms.layout.BalloonLayout; import edu.uci.ics.jung.algorithms.layout.TreeLayout; import edu.uci.ics.jung.graph.CTreeNetwork; @@ -59,231 +32,257 @@ import edu.uci.ics.jung.visualization.transform.shape.HyperbolicShapeTransformer; import edu.uci.ics.jung.visualization.transform.shape.ViewLensSupport; import edu.uci.ics.jung.visualization.util.Animator; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GridLayout; +import java.awt.Paint; +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Point2D; +import javax.swing.BorderFactory; +import javax.swing.JApplet; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JToggleButton; /** - * Demonstrates the visualization of a Tree using TreeLayout - * and BalloonLayout. An examiner lens performing a hyperbolic - * transformation of the view is also included. - * + * Demonstrates the visualization of a Tree using TreeLayout and BalloonLayout. An examiner lens + * performing a hyperbolic transformation of the view is also included. + * * @author Tom Nelson - * */ @SuppressWarnings("serial") public class BalloonLayoutDemo extends JApplet { - /** - * the graph - */ - CTreeNetwork graph; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - VisualizationServer.Paintable rings; - - String root; - - TreeLayout layout; - - BalloonLayout radialLayout; - /** - * provides a Hyperbolic lens for the view - */ - LensSupport hyperbolicViewSupport; - - public BalloonLayoutDemo() { - - // create a simple graph for the demo - graph = createTree(); - - layout = new TreeLayout(graph.asGraph()); - radialLayout = new BalloonLayout(graph.asGraph()); - radialLayout.setSize(new Dimension(900,900)); - vv = new VisualizationViewer(graph, layout, new Dimension(600,600)); - vv.setBackground(Color.white); - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.quadCurve(graph)); - vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - // add a listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); - rings = new Rings(radialLayout); - - Container content = getContentPane(); - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - content.add(panel); - - final DefaultModalGraphMouse graphMouse = - new DefaultModalGraphMouse(); - - vv.setGraphMouse(graphMouse); - vv.addKeyListener(graphMouse.getModeKeyListener()); - - hyperbolicViewSupport = - new ViewLensSupport(vv, new HyperbolicShapeTransformer(vv, - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), - new ModalLensGraphMouse()); - - - graphMouse.addItemListener(hyperbolicViewSupport.getGraphMouse().getModeListener()); - - JComboBox modeBox = graphMouse.getModeComboBox(); - modeBox.addItemListener(graphMouse.getModeListener()); - graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); - - final ScalingControl scaler = new CrossoverScalingControl(); - - vv.scaleToLayout(scaler); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + /** the graph */ + CTreeNetwork graph; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + VisualizationServer.Paintable rings; + + String root; + + TreeLayout layout; + + BalloonLayout radialLayout; + /** provides a Hyperbolic lens for the view */ + LensSupport hyperbolicViewSupport; + + public BalloonLayoutDemo() { + + // create a simple graph for the demo + graph = createTree(); + + layout = new TreeLayout(graph.asGraph()); + radialLayout = new BalloonLayout(graph.asGraph()); + radialLayout.setSize(new Dimension(900, 900)); + vv = new VisualizationViewer(graph, layout, new Dimension(600, 600)); + vv.setBackground(Color.white); + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.quadCurve(graph)); + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + // add a listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); + rings = new Rings(radialLayout); + + Container content = getContentPane(); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + content.add(panel); + + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + + vv.setGraphMouse(graphMouse); + vv.addKeyListener(graphMouse.getModeKeyListener()); + + hyperbolicViewSupport = + new ViewLensSupport( + vv, + new HyperbolicShapeTransformer( + vv, vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), + new ModalLensGraphMouse()); + + graphMouse.addItemListener(hyperbolicViewSupport.getGraphMouse().getModeListener()); + + JComboBox modeBox = graphMouse.getModeComboBox(); + modeBox.addItemListener(graphMouse.getModeListener()); + graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); + + final ScalingControl scaler = new CrossoverScalingControl(); + + vv.scaleToLayout(scaler); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - - JToggleButton radial = new JToggleButton("Balloon"); - radial.addItemListener(new ItemListener() { - - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - - LayoutTransition lt = - new LayoutTransition(vv, layout, radialLayout); - Animator animator = new Animator(lt); - animator.start(); - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).setToIdentity(); - vv.addPreRenderPaintable(rings); - } else { - - LayoutTransition lt = - new LayoutTransition(vv, radialLayout, layout); - Animator animator = new Animator(lt); - animator.start(); - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).setToIdentity(); - vv.removePreRenderPaintable(rings); - } - vv.repaint(); - }}); - final JRadioButton hyperView = new JRadioButton("Hyperbolic View"); - hyperView.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - hyperbolicViewSupport.activate(e.getStateChange() == ItemEvent.SELECTED); + + JToggleButton radial = new JToggleButton("Balloon"); + radial.addItemListener( + new ItemListener() { + + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + + LayoutTransition lt = + new LayoutTransition(vv, layout, radialLayout); + Animator animator = new Animator(lt); + animator.start(); + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .setToIdentity(); + vv.addPreRenderPaintable(rings); + } else { + + LayoutTransition lt = + new LayoutTransition(vv, radialLayout, layout); + Animator animator = new Animator(lt); + animator.start(); + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .setToIdentity(); + vv.removePreRenderPaintable(rings); } + vv.repaint(); + } + }); + final JRadioButton hyperView = new JRadioButton("Hyperbolic View"); + hyperView.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + hyperbolicViewSupport.activate(e.getStateChange() == ItemEvent.SELECTED); + } }); - JPanel scaleGrid = new JPanel(new GridLayout(1,0)); - scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); - - JPanel controls = new JPanel(); - scaleGrid.add(plus); - scaleGrid.add(minus); - controls.add(radial); - controls.add(scaleGrid); - controls.add(modeBox); - controls.add(hyperView); - content.add(controls, BorderLayout.SOUTH); + JPanel scaleGrid = new JPanel(new GridLayout(1, 0)); + scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); + + JPanel controls = new JPanel(); + scaleGrid.add(plus); + scaleGrid.add(minus); + controls.add(radial); + controls.add(scaleGrid); + controls.add(modeBox); + controls.add(hyperView); + content.add(controls, BorderLayout.SOUTH); + } + + class Rings implements VisualizationServer.Paintable { + + BalloonLayout layout; + + public Rings(BalloonLayout layout) { + this.layout = layout; } - - class Rings implements VisualizationServer.Paintable { - - BalloonLayout layout; - - public Rings(BalloonLayout layout) { - this.layout = layout; - } - - public void paint(Graphics g) { - g.setColor(Color.gray); - - Graphics2D g2d = (Graphics2D)g; - - Ellipse2D ellipse = new Ellipse2D.Double(); - for(String v : layout.nodes()) { - Double radius = layout.getRadii().get(v); - if(radius == null) continue; - Point2D p = layout.apply(v); - ellipse.setFrame(-radius, -radius, 2*radius, 2*radius); - AffineTransform at = AffineTransform.getTranslateInstance(p.getX(), p.getY()); - Shape shape = at.createTransformedShape(ellipse); - - MutableTransformer viewTransformer = - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); - - if(viewTransformer instanceof MutableTransformerDecorator) { - shape = vv.getRenderContext().getMultiLayerTransformer().transform(shape); - } else { - shape = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT,shape); - } - - g2d.draw(shape); - } - } - - public boolean useTransform() { - return true; - } + + public void paint(Graphics g) { + g.setColor(Color.gray); + + Graphics2D g2d = (Graphics2D) g; + + Ellipse2D ellipse = new Ellipse2D.Double(); + for (String v : layout.nodes()) { + Double radius = layout.getRadii().get(v); + if (radius == null) continue; + Point2D p = layout.apply(v); + ellipse.setFrame(-radius, -radius, 2 * radius, 2 * radius); + AffineTransform at = AffineTransform.getTranslateInstance(p.getX(), p.getY()); + Shape shape = at.createTransformedShape(ellipse); + + MutableTransformer viewTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); + + if (viewTransformer instanceof MutableTransformerDecorator) { + shape = vv.getRenderContext().getMultiLayerTransformer().transform(shape); + } else { + shape = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, shape); + } + + g2d.draw(shape); + } } - - /** - * - */ - private CTreeNetwork createTree() { - MutableCTreeNetwork tree = - TreeNetworkBuilder.builder().expectedNodeCount(27).build(); - - int edgeId = 0; - tree.addNode("A0"); - tree.addEdge("A0", "B0", edgeId++); - tree.addEdge("A0", "B1", edgeId++); - tree.addEdge("A0", "B2", edgeId++); - - tree.addEdge("B0", "C0", edgeId++); - tree.addEdge("B0", "C1", edgeId++); - tree.addEdge("B0", "C2", edgeId++); - tree.addEdge("B0", "C3", edgeId++); - - tree.addEdge("C2", "H0", edgeId++); - tree.addEdge("C2", "H1", edgeId++); - - tree.addEdge("B1", "D0", edgeId++); - tree.addEdge("B1", "D1", edgeId++); - tree.addEdge("B1", "D2", edgeId++); - - tree.addEdge("B2", "E0", edgeId++); - tree.addEdge("B2", "E1", edgeId++); - tree.addEdge("B2", "E2", edgeId++); - - tree.addEdge("D0", "F0", edgeId++); - tree.addEdge("D0", "F1", edgeId++); - tree.addEdge("D0", "F2", edgeId++); - - tree.addEdge("D1", "G0", edgeId++); - tree.addEdge("D1", "G1", edgeId++); - tree.addEdge("D1", "G2", edgeId++); - tree.addEdge("D1", "G3", edgeId++); - tree.addEdge("D1", "G4", edgeId++); - tree.addEdge("D1", "G5", edgeId++); - tree.addEdge("D1", "G6", edgeId++); - tree.addEdge("D1", "G7", edgeId++); - - return tree; + + public boolean useTransform() { + return true; } + } - public static void main(String[] args) { - JFrame frame = new JFrame(); - Container content = frame.getContentPane(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + /** */ + private CTreeNetwork createTree() { + MutableCTreeNetwork tree = + TreeNetworkBuilder.builder().expectedNodeCount(27).build(); - content.add(new BalloonLayoutDemo()); - frame.pack(); - frame.setVisible(true); - } + int edgeId = 0; + tree.addNode("A0"); + tree.addEdge("A0", "B0", edgeId++); + tree.addEdge("A0", "B1", edgeId++); + tree.addEdge("A0", "B2", edgeId++); + + tree.addEdge("B0", "C0", edgeId++); + tree.addEdge("B0", "C1", edgeId++); + tree.addEdge("B0", "C2", edgeId++); + tree.addEdge("B0", "C3", edgeId++); + + tree.addEdge("C2", "H0", edgeId++); + tree.addEdge("C2", "H1", edgeId++); + + tree.addEdge("B1", "D0", edgeId++); + tree.addEdge("B1", "D1", edgeId++); + tree.addEdge("B1", "D2", edgeId++); + + tree.addEdge("B2", "E0", edgeId++); + tree.addEdge("B2", "E1", edgeId++); + tree.addEdge("B2", "E2", edgeId++); + + tree.addEdge("D0", "F0", edgeId++); + tree.addEdge("D0", "F1", edgeId++); + tree.addEdge("D0", "F2", edgeId++); + + tree.addEdge("D1", "G0", edgeId++); + tree.addEdge("D1", "G1", edgeId++); + tree.addEdge("D1", "G2", edgeId++); + tree.addEdge("D1", "G3", edgeId++); + tree.addEdge("D1", "G4", edgeId++); + tree.addEdge("D1", "G5", edgeId++); + tree.addEdge("D1", "G6", edgeId++); + tree.addEdge("D1", "G7", edgeId++); + + return tree; + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + Container content = frame.getContentPane(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + content.add(new BalloonLayoutDemo()); + frame.pack(); + frame.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/ClusteringDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/ClusteringDemo.java index 0eb77ad9..83c9ed10 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/ClusteringDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/ClusteringDemo.java @@ -1,14 +1,32 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.samples; +import com.google.common.base.Functions; +import com.google.common.base.Supplier; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.Network; +import com.google.common.graph.NetworkBuilder; +import edu.uci.ics.jung.algorithms.cluster.EdgeBetweennessClusterer; +import edu.uci.ics.jung.algorithms.layout.AggregateLayout; +import edu.uci.ics.jung.algorithms.layout.CircleLayout; +import edu.uci.ics.jung.algorithms.layout.FRLayout; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.layout.util.Relaxer; +import edu.uci.ics.jung.io.PajekNetReader; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; @@ -28,7 +46,6 @@ import java.io.InputStreamReader; import java.util.Iterator; import java.util.Set; - import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; @@ -42,266 +59,259 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import com.google.common.base.Functions; -import com.google.common.base.Supplier; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.Network; -import com.google.common.graph.NetworkBuilder; - -import edu.uci.ics.jung.algorithms.cluster.EdgeBetweennessClusterer; -import edu.uci.ics.jung.algorithms.layout.AggregateLayout; -import edu.uci.ics.jung.algorithms.layout.CircleLayout; -import edu.uci.ics.jung.algorithms.layout.FRLayout; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.algorithms.layout.util.Relaxer; -import edu.uci.ics.jung.io.PajekNetReader; -import edu.uci.ics.jung.visualization.GraphZoomScrollPane; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; - - /** - * This simple app demonstrates how one can use our algorithms and visualization libraries in unison. - * In this case, we generate use the Zachary karate club data set, widely known in the social networks literature, then - * we cluster the vertices using an edge-betweenness clusterer, and finally we visualize the graph using - * Fruchtermain-Rheingold layout and provide a slider so that the user can adjust the clustering granularity. + * This simple app demonstrates how one can use our algorithms and visualization libraries in + * unison. In this case, we generate use the Zachary karate club data set, widely known in the + * social networks literature, then we cluster the vertices using an edge-betweenness clusterer, and + * finally we visualize the graph using Fruchtermain-Rheingold layout and provide a slider so that + * the user can adjust the clustering granularity. + * * @author Scott White */ @SuppressWarnings("serial") public class ClusteringDemo extends JApplet { - VisualizationViewer vv; - - LoadingCache vertexPaints = - CacheBuilder.newBuilder().build( - CacheLoader.from(Functions.constant(Color.white))); - LoadingCache edgePaints = - CacheBuilder.newBuilder().build( - CacheLoader.from(Functions.constant(Color.blue))); - - private static final Stroke THIN = new BasicStroke(1); - private static final Stroke THICK= new BasicStroke(2); - - public final Color[] similarColors = - { - new Color(216, 134, 134), - new Color(135, 137, 211), - new Color(134, 206, 189), - new Color(206, 176, 134), - new Color(194, 204, 134), - new Color(145, 214, 134), - new Color(133, 178, 209), - new Color(103, 148, 255), - new Color(60, 220, 220), - new Color(30, 250, 100) - }; - - public static void main(String[] args) throws IOException { - - ClusteringDemo cd = new ClusteringDemo(); - cd.start(); - // Add a restart button so the graph can be redrawn to fit the size of the frame - JFrame jf = new JFrame(); - jf.getContentPane().add(cd); - - jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - jf.pack(); - jf.setVisible(true); - } - - public void start() { - InputStream is = this.getClass().getClassLoader().getResourceAsStream("datasets/zachary.net"); - BufferedReader br = new BufferedReader( new InputStreamReader( is )); - - try - { - setUpView(br); - } - catch (IOException e) - { - System.out.println("Error in loading graph"); - e.printStackTrace(); - } - } - - private void setUpView(BufferedReader br) throws IOException { - - Supplier vertexFactory = new Supplier() { - int n = 0; - public Number get() { return n++; } + VisualizationViewer vv; + + LoadingCache vertexPaints = + CacheBuilder.newBuilder().build(CacheLoader.from(Functions.constant(Color.white))); + LoadingCache edgePaints = + CacheBuilder.newBuilder().build(CacheLoader.from(Functions.constant(Color.blue))); + + private static final Stroke THIN = new BasicStroke(1); + private static final Stroke THICK = new BasicStroke(2); + + public final Color[] similarColors = { + new Color(216, 134, 134), + new Color(135, 137, 211), + new Color(134, 206, 189), + new Color(206, 176, 134), + new Color(194, 204, 134), + new Color(145, 214, 134), + new Color(133, 178, 209), + new Color(103, 148, 255), + new Color(60, 220, 220), + new Color(30, 250, 100) + }; + + public static void main(String[] args) throws IOException { + + ClusteringDemo cd = new ClusteringDemo(); + cd.start(); + // Add a restart button so the graph can be redrawn to fit the size of the frame + JFrame jf = new JFrame(); + jf.getContentPane().add(cd); + + jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + jf.pack(); + jf.setVisible(true); + } + + public void start() { + InputStream is = this.getClass().getClassLoader().getResourceAsStream("datasets/zachary.net"); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + + try { + setUpView(br); + } catch (IOException e) { + System.out.println("Error in loading graph"); + e.printStackTrace(); + } + } + + private void setUpView(BufferedReader br) throws IOException { + + Supplier vertexFactory = + new Supplier() { + int n = 0; + + public Number get() { + return n++; + } }; - Supplier edgeFactory = new Supplier() { - int n = 0; - public Number get() { return n++; } + Supplier edgeFactory = + new Supplier() { + int n = 0; + + public Number get() { + return n++; + } }; - PajekNetReader, Number,Number> pnr = - new PajekNetReader, Number,Number>(vertexFactory, edgeFactory); - - final MutableNetwork graph = NetworkBuilder.undirected().build(); - - pnr.load(br, graph); - - //Create a simple layout frame - //specify the Fruchterman-Rheingold layout algorithm - final AggregateLayout layout = - new AggregateLayout(new FRLayout(graph.asGraph())); - - vv = new VisualizationViewer(graph, layout); - vv.setBackground( Color.white ); - //Tell the renderer to use our own customized color rendering - vv.getRenderContext().setVertexFillPaintTransformer(vertexPaints); - vv.getRenderContext().setVertexDrawPaintTransformer(v -> - vv.getPickedVertexState().isPicked(v) ? Color.CYAN : Color.BLACK); - - vv.getRenderContext().setEdgeDrawPaintTransformer(edgePaints); - - vv.getRenderContext().setEdgeStrokeTransformer(e -> - edgePaints.getUnchecked(e) == Color.LIGHT_GRAY ? THIN : THICK); - - //add restart button - JButton scramble = new JButton("Restart"); - scramble.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent arg0) { - Layout layout = vv.getGraphLayout(); - layout.initialize(); - Relaxer relaxer = vv.getModel().getRelaxer(); - if(relaxer != null) { - relaxer.stop(); - relaxer.prerelax(); - relaxer.relax(); - } - } - - }); - - DefaultModalGraphMouse gm = new DefaultModalGraphMouse(); - vv.setGraphMouse(gm); - - final JToggleButton groupVertices = new JToggleButton("Group Clusters"); - - //Create slider to adjust the number of edges to remove when clustering - final JSlider edgeBetweennessSlider = new JSlider(JSlider.HORIZONTAL); - edgeBetweennessSlider.setBackground(Color.WHITE); - edgeBetweennessSlider.setPreferredSize(new Dimension(210, 50)); - edgeBetweennessSlider.setPaintTicks(true); - edgeBetweennessSlider.setMaximum(graph.edges().size()); - edgeBetweennessSlider.setMinimum(0); - edgeBetweennessSlider.setValue(0); - edgeBetweennessSlider.setMajorTickSpacing(10); - edgeBetweennessSlider.setPaintLabels(true); - edgeBetweennessSlider.setPaintTicks(true); - -// edgeBetweennessSlider.setBorder(BorderFactory.createLineBorder(Color.black)); - //TO DO: edgeBetweennessSlider.add(new JLabel("Node Size (PageRank With Priors):")); - //I also want the slider value to appear - final JPanel eastControls = new JPanel(); - eastControls.setOpaque(true); - eastControls.setLayout(new BoxLayout(eastControls, BoxLayout.Y_AXIS)); - eastControls.add(Box.createVerticalGlue()); - eastControls.add(edgeBetweennessSlider); - - final String COMMANDSTRING = "Edges removed for clusters: "; - final String eastSize = COMMANDSTRING + edgeBetweennessSlider.getValue(); - - final TitledBorder sliderBorder = BorderFactory.createTitledBorder(eastSize); - eastControls.setBorder(sliderBorder); - //eastControls.add(eastSize); - eastControls.add(Box.createVerticalGlue()); - - groupVertices.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - clusterAndRecolor(layout, graph, edgeBetweennessSlider.getValue(), - similarColors, e.getStateChange() == ItemEvent.SELECTED); - vv.repaint(); - }}); - - - clusterAndRecolor(layout, graph, 0, similarColors, groupVertices.isSelected()); - - edgeBetweennessSlider.addChangeListener(new ChangeListener() { - public void stateChanged(ChangeEvent e) { - JSlider source = (JSlider) e.getSource(); - if (!source.getValueIsAdjusting()) { - int numEdgesToRemove = source.getValue(); - clusterAndRecolor(layout, graph, numEdgesToRemove, similarColors, - groupVertices.isSelected()); - sliderBorder.setTitle( - COMMANDSTRING + edgeBetweennessSlider.getValue()); - eastControls.repaint(); - vv.validate(); - vv.repaint(); - } - } - }); - - Container content = getContentPane(); - content.add(new GraphZoomScrollPane(vv)); - JPanel south = new JPanel(); - JPanel grid = new JPanel(new GridLayout(2,1)); - grid.add(scramble); - grid.add(groupVertices); - south.add(grid); - south.add(eastControls); - JPanel p = new JPanel(); - p.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); - p.add(gm.getModeComboBox()); - south.add(p); - content.add(south, BorderLayout.SOUTH); - } - - public void clusterAndRecolor(AggregateLayout layout, - Network graph, - int numEdgesToRemove, - Color[] colors, boolean groupClusters) { - - layout.removeAll(); - - EdgeBetweennessClusterer clusterer = - new EdgeBetweennessClusterer(numEdgesToRemove); - Set> clusterSet = clusterer.apply(graph); - Set edges = clusterer.getEdgesRemoved(); - - int i = 0; - //Set the colors of each node so that each cluster's vertices have the same color - for (Iterator> cIt = clusterSet.iterator(); cIt.hasNext();) { - - Set vertices = cIt.next(); - Color c = colors[i % colors.length]; - - colorCluster(vertices, c); - if(groupClusters == true) { - groupCluster(layout, vertices); - } - i++; - } - for (Number e : graph.edges()) { - edgePaints.put(e, edges.contains(e) ? Color.LIGHT_GRAY : Color.BLACK); - } - } - - private void colorCluster(Set vertices, Color c) { - for (Number v : vertices) { - vertexPaints.put(v, c); - } - } - - private void groupCluster(AggregateLayout layout, Set vertices) { - if(vertices.size() < vv.getModel().getNetwork().nodes().size()) { - Point2D center = layout.apply(vertices.iterator().next()); - MutableNetwork subGraph = NetworkBuilder.undirected().build(); - for(Number v : vertices) { - subGraph.addNode(v); - } - Layout subLayout = new CircleLayout(subGraph.asGraph()); - subLayout.setInitializer(vv.getGraphLayout()); - subLayout.setSize(new Dimension(40,40)); - - layout.put(subLayout,center); - vv.repaint(); - } - } + PajekNetReader, Number, Number> pnr = + new PajekNetReader, Number, Number>( + vertexFactory, edgeFactory); + + final MutableNetwork graph = NetworkBuilder.undirected().build(); + + pnr.load(br, graph); + + //Create a simple layout frame + //specify the Fruchterman-Rheingold layout algorithm + final AggregateLayout layout = + new AggregateLayout(new FRLayout(graph.asGraph())); + + vv = new VisualizationViewer(graph, layout); + vv.setBackground(Color.white); + //Tell the renderer to use our own customized color rendering + vv.getRenderContext().setVertexFillPaintTransformer(vertexPaints); + vv.getRenderContext() + .setVertexDrawPaintTransformer( + v -> vv.getPickedVertexState().isPicked(v) ? Color.CYAN : Color.BLACK); + + vv.getRenderContext().setEdgeDrawPaintTransformer(edgePaints); + + vv.getRenderContext() + .setEdgeStrokeTransformer( + e -> edgePaints.getUnchecked(e) == Color.LIGHT_GRAY ? THIN : THICK); + + //add restart button + JButton scramble = new JButton("Restart"); + scramble.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + Layout layout = vv.getGraphLayout(); + layout.initialize(); + Relaxer relaxer = vv.getModel().getRelaxer(); + if (relaxer != null) { + relaxer.stop(); + relaxer.prerelax(); + relaxer.relax(); + } + } + }); + + DefaultModalGraphMouse gm = new DefaultModalGraphMouse(); + vv.setGraphMouse(gm); + + final JToggleButton groupVertices = new JToggleButton("Group Clusters"); + + //Create slider to adjust the number of edges to remove when clustering + final JSlider edgeBetweennessSlider = new JSlider(JSlider.HORIZONTAL); + edgeBetweennessSlider.setBackground(Color.WHITE); + edgeBetweennessSlider.setPreferredSize(new Dimension(210, 50)); + edgeBetweennessSlider.setPaintTicks(true); + edgeBetweennessSlider.setMaximum(graph.edges().size()); + edgeBetweennessSlider.setMinimum(0); + edgeBetweennessSlider.setValue(0); + edgeBetweennessSlider.setMajorTickSpacing(10); + edgeBetweennessSlider.setPaintLabels(true); + edgeBetweennessSlider.setPaintTicks(true); + + // edgeBetweennessSlider.setBorder(BorderFactory.createLineBorder(Color.black)); + //TO DO: edgeBetweennessSlider.add(new JLabel("Node Size (PageRank With Priors):")); + //I also want the slider value to appear + final JPanel eastControls = new JPanel(); + eastControls.setOpaque(true); + eastControls.setLayout(new BoxLayout(eastControls, BoxLayout.Y_AXIS)); + eastControls.add(Box.createVerticalGlue()); + eastControls.add(edgeBetweennessSlider); + + final String COMMANDSTRING = "Edges removed for clusters: "; + final String eastSize = COMMANDSTRING + edgeBetweennessSlider.getValue(); + + final TitledBorder sliderBorder = BorderFactory.createTitledBorder(eastSize); + eastControls.setBorder(sliderBorder); + //eastControls.add(eastSize); + eastControls.add(Box.createVerticalGlue()); + + groupVertices.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + clusterAndRecolor( + layout, + graph, + edgeBetweennessSlider.getValue(), + similarColors, + e.getStateChange() == ItemEvent.SELECTED); + vv.repaint(); + } + }); + + clusterAndRecolor(layout, graph, 0, similarColors, groupVertices.isSelected()); + + edgeBetweennessSlider.addChangeListener( + new ChangeListener() { + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + if (!source.getValueIsAdjusting()) { + int numEdgesToRemove = source.getValue(); + clusterAndRecolor( + layout, graph, numEdgesToRemove, similarColors, groupVertices.isSelected()); + sliderBorder.setTitle(COMMANDSTRING + edgeBetweennessSlider.getValue()); + eastControls.repaint(); + vv.validate(); + vv.repaint(); + } + } + }); + + Container content = getContentPane(); + content.add(new GraphZoomScrollPane(vv)); + JPanel south = new JPanel(); + JPanel grid = new JPanel(new GridLayout(2, 1)); + grid.add(scramble); + grid.add(groupVertices); + south.add(grid); + south.add(eastControls); + JPanel p = new JPanel(); + p.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); + p.add(gm.getModeComboBox()); + south.add(p); + content.add(south, BorderLayout.SOUTH); + } + + public void clusterAndRecolor( + AggregateLayout layout, + Network graph, + int numEdgesToRemove, + Color[] colors, + boolean groupClusters) { + + layout.removeAll(); + + EdgeBetweennessClusterer clusterer = + new EdgeBetweennessClusterer(numEdgesToRemove); + Set> clusterSet = clusterer.apply(graph); + Set edges = clusterer.getEdgesRemoved(); + + int i = 0; + //Set the colors of each node so that each cluster's vertices have the same color + for (Iterator> cIt = clusterSet.iterator(); cIt.hasNext(); ) { + + Set vertices = cIt.next(); + Color c = colors[i % colors.length]; + + colorCluster(vertices, c); + if (groupClusters == true) { + groupCluster(layout, vertices); + } + i++; + } + for (Number e : graph.edges()) { + edgePaints.put(e, edges.contains(e) ? Color.LIGHT_GRAY : Color.BLACK); + } + } + + private void colorCluster(Set vertices, Color c) { + for (Number v : vertices) { + vertexPaints.put(v, c); + } + } + + private void groupCluster(AggregateLayout layout, Set vertices) { + if (vertices.size() < vv.getModel().getNetwork().nodes().size()) { + Point2D center = layout.apply(vertices.iterator().next()); + MutableNetwork subGraph = NetworkBuilder.undirected().build(); + for (Number v : vertices) { + subGraph.addNode(v); + } + Layout subLayout = new CircleLayout(subGraph.asGraph()); + subLayout.setInitializer(vv.getGraphLayout()); + subLayout.setSize(new Dimension(40, 40)); + + layout.put(subLayout, center); + vv.repaint(); + } + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/DemoLensVertexImageShaperDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/DemoLensVertexImageShaperDemo.java index e5ce1363..6eb3671c 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/DemoLensVertexImageShaperDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/DemoLensVertexImageShaperDemo.java @@ -1,13 +1,38 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.Network; +import com.google.common.graph.NetworkBuilder; +import edu.uci.ics.jung.algorithms.layout.FRLayout; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.LayeredIcon; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ModalGraphMouse.Mode; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer; +import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; +import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; +import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; +import edu.uci.ics.jung.visualization.decorators.VertexIconShapeTransformer; +import edu.uci.ics.jung.visualization.picking.PickedState; +import edu.uci.ics.jung.visualization.renderers.Checkmark; +import edu.uci.ics.jung.visualization.renderers.DefaultEdgeLabelRenderer; +import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer; +import edu.uci.ics.jung.visualization.transform.LayoutLensSupport; +import edu.uci.ics.jung.visualization.transform.LensSupport; +import edu.uci.ics.jung.visualization.transform.shape.MagnifyImageLensSupport; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; @@ -23,7 +48,6 @@ import java.awt.event.ItemListener; import java.util.HashMap; import java.util.Map; - import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.Icon; @@ -37,380 +61,332 @@ import javax.swing.JPanel; import javax.swing.JRadioButton; -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.Network; -import com.google.common.graph.NetworkBuilder; - -import edu.uci.ics.jung.algorithms.layout.FRLayout; -import edu.uci.ics.jung.visualization.GraphZoomScrollPane; -import edu.uci.ics.jung.visualization.LayeredIcon; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; -import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ModalGraphMouse.Mode; -import edu.uci.ics.jung.visualization.control.ScalingControl; -import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer; -import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; -import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; -import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; -import edu.uci.ics.jung.visualization.decorators.VertexIconShapeTransformer; -import edu.uci.ics.jung.visualization.picking.PickedState; -import edu.uci.ics.jung.visualization.renderers.Checkmark; -import edu.uci.ics.jung.visualization.renderers.DefaultEdgeLabelRenderer; -import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer; -import edu.uci.ics.jung.visualization.transform.LayoutLensSupport; -import edu.uci.ics.jung.visualization.transform.LensSupport; -import edu.uci.ics.jung.visualization.transform.shape.MagnifyImageLensSupport; - - /** - * Demonstrates the use of images to represent graph vertices. - * The images are added to the DefaultGraphLabelRenderer and can - * either be offset from the vertex, or centered on the vertex. - * Additionally, the relative positioning of the label and - * image is controlled by subclassing the DefaultGraphLabelRenderer - * and setting the appropriate properties on its JLabel superclass - * FancyGraphLabelRenderer - * - * The images used in this demo (courtesy of slashdot.org) are - * rectangular but with a transparent background. When vertices - * are represented by these images, it looks better if the actual - * shape of the opaque part of the image is computed so that the - * edge arrowheads follow the visual shape of the image. This demo - * uses the FourPassImageShaper class to compute the Shape from - * an image with transparent background. - * + * Demonstrates the use of images to represent graph vertices. The images are added to the + * DefaultGraphLabelRenderer and can either be offset from the vertex, or centered on the vertex. + * Additionally, the relative positioning of the label and image is controlled by subclassing the + * DefaultGraphLabelRenderer and setting the appropriate properties on its JLabel superclass + * FancyGraphLabelRenderer + * + *

    The images used in this demo (courtesy of slashdot.org) are rectangular but with a transparent + * background. When vertices are represented by these images, it looks better if the actual shape of + * the opaque part of the image is computed so that the edge arrowheads follow the visual shape of + * the image. This demo uses the FourPassImageShaper class to compute the Shape from an image with + * transparent background. + * * @author Tom Nelson - * */ public class DemoLensVertexImageShaperDemo extends JApplet { - /** - * - */ - private static final long serialVersionUID = 5432239991020505763L; + /** */ + private static final long serialVersionUID = 5432239991020505763L; - /** - * the graph - */ - Network graph; + /** the graph */ + Network graph; - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - /** - * some icon names to use - */ - String[] iconNames = { - "apple", - "gamespcgames", - "graphics3", - "humor", - "inputdevices", - "linux", - "music", - "os", - "privacy", - "wireless", - "x" - }; - - LensSupport viewSupport; - LensSupport modelSupport; - LensSupport magnifyLayoutSupport; - LensSupport magnifyViewSupport; - /** - * create an instance of a simple graph with controls to - * demo the zoom features. - * - */ - public DemoLensVertexImageShaperDemo() { - - // create a simple graph for the demo - graph = createGraph(); - - - - // a Map for the labels - Map map = new HashMap(); - for(int i=0; i < graph.nodes().size(); i++) { - map.put(i, iconNames[i%iconNames.length]); - } - - // a Map for the Icons - Map iconMap = new HashMap(); - for(int i=0; i < graph.nodes().size(); i++) { - String name = "/images/topic"+iconNames[i % iconNames.length]+".gif"; - try { - Icon icon = - new LayeredIcon(new ImageIcon(DemoLensVertexImageShaperDemo.class.getResource(name)).getImage()); - iconMap.put(i, icon); - } catch(Exception ex) { - System.err.println("You need slashdoticons.jar in your classpath to see the image "+name); - } - } - - - FRLayout layout = new FRLayout(graph.asGraph()); - layout.setMaxIterations(100); - vv = new VisualizationViewer(graph, layout, new Dimension(600,600)); - - Function vpf = - new PickableVertexPaintTransformer(vv.getPickedVertexState(), Color.white, Color.yellow); - vv.getRenderContext().setVertexFillPaintTransformer(vpf); - vv.getRenderContext().setEdgeDrawPaintTransformer( - new PickableEdgePaintTransformer(vv.getPickedEdgeState(), Color.black, Color.cyan)); - - vv.setBackground(Color.white); - - final Function vertexStringerImpl = - new VertexStringerImpl(map); - vv.getRenderContext().setVertexLabelTransformer(vertexStringerImpl); - vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan)); - vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan)); - - - // features on and off. For a real application, use VertexIconAndShapeFunction instead. - final VertexIconShapeTransformer vertexImageShapeFunction = - new VertexIconShapeTransformer(new EllipseVertexShapeTransformer()); - vertexImageShapeFunction.setIconMap(iconMap); - - final Function vertexIconFunction = Functions.forMap(iconMap); - - vv.getRenderContext().setVertexShapeTransformer(vertexImageShapeFunction); - vv.getRenderContext().setVertexIconTransformer(vertexIconFunction); - - // Get the pickedState and add a listener that will decorate the - // Vertex images with a checkmark icon when they are picked - PickedState ps = vv.getPickedVertexState(); - ps.addItemListener(new PickWithIconListener(vertexIconFunction)); - - vv.addPostRenderPaintable(new VisualizationViewer.Paintable(){ - int x; - int y; - Font font; - FontMetrics metrics; - int swidth; - int sheight; - String str = "Thank You, slashdot.org, for the images!"; - - public void paint(Graphics g) { - Dimension d = vv.getSize(); - if(font == null) { - font = new Font(g.getFont().getName(), Font.BOLD, 20); - metrics = g.getFontMetrics(font); - swidth = metrics.stringWidth(str); - sheight = metrics.getMaxAscent()+metrics.getMaxDescent(); - System.out.println("dimension width: " + d.width); - System.out.println("string width: " + swidth); - x = 100; // (d.width-swidth) / 2; - y = (int)(d.height-sheight*1.5); - } - g.setFont(font); - Color oldColor = g.getColor(); - g.setColor(Color.lightGray); - g.drawString(str, x, y); - g.setColor(oldColor); - } - public boolean useTransform() { - return false; + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + /** some icon names to use */ + String[] iconNames = { + "apple", + "gamespcgames", + "graphics3", + "humor", + "inputdevices", + "linux", + "music", + "os", + "privacy", + "wireless", + "x" + }; + + LensSupport viewSupport; + LensSupport modelSupport; + LensSupport magnifyLayoutSupport; + LensSupport magnifyViewSupport; + /** create an instance of a simple graph with controls to demo the zoom features. */ + public DemoLensVertexImageShaperDemo() { + + // create a simple graph for the demo + graph = createGraph(); + + // a Map for the labels + Map map = new HashMap(); + for (int i = 0; i < graph.nodes().size(); i++) { + map.put(i, iconNames[i % iconNames.length]); + } + + // a Map for the Icons + Map iconMap = new HashMap(); + for (int i = 0; i < graph.nodes().size(); i++) { + String name = "/images/topic" + iconNames[i % iconNames.length] + ".gif"; + try { + Icon icon = + new LayeredIcon( + new ImageIcon(DemoLensVertexImageShaperDemo.class.getResource(name)).getImage()); + iconMap.put(i, icon); + } catch (Exception ex) { + System.err.println("You need slashdoticons.jar in your classpath to see the image " + name); + } + } + + FRLayout layout = new FRLayout(graph.asGraph()); + layout.setMaxIterations(100); + vv = new VisualizationViewer(graph, layout, new Dimension(600, 600)); + + Function vpf = + new PickableVertexPaintTransformer( + vv.getPickedVertexState(), Color.white, Color.yellow); + vv.getRenderContext().setVertexFillPaintTransformer(vpf); + vv.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv.getPickedEdgeState(), Color.black, Color.cyan)); + + vv.setBackground(Color.white); + + final Function vertexStringerImpl = new VertexStringerImpl(map); + vv.getRenderContext().setVertexLabelTransformer(vertexStringerImpl); + vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan)); + vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan)); + + // features on and off. For a real application, use VertexIconAndShapeFunction instead. + final VertexIconShapeTransformer vertexImageShapeFunction = + new VertexIconShapeTransformer(new EllipseVertexShapeTransformer()); + vertexImageShapeFunction.setIconMap(iconMap); + + final Function vertexIconFunction = Functions.forMap(iconMap); + + vv.getRenderContext().setVertexShapeTransformer(vertexImageShapeFunction); + vv.getRenderContext().setVertexIconTransformer(vertexIconFunction); + + // Get the pickedState and add a listener that will decorate the + // Vertex images with a checkmark icon when they are picked + PickedState ps = vv.getPickedVertexState(); + ps.addItemListener(new PickWithIconListener(vertexIconFunction)); + + vv.addPostRenderPaintable( + new VisualizationViewer.Paintable() { + int x; + int y; + Font font; + FontMetrics metrics; + int swidth; + int sheight; + String str = "Thank You, slashdot.org, for the images!"; + + public void paint(Graphics g) { + Dimension d = vv.getSize(); + if (font == null) { + font = new Font(g.getFont().getName(), Font.BOLD, 20); + metrics = g.getFontMetrics(font); + swidth = metrics.stringWidth(str); + sheight = metrics.getMaxAscent() + metrics.getMaxDescent(); + System.out.println("dimension width: " + d.width); + System.out.println("string width: " + swidth); + x = 100; // (d.width-swidth) / 2; + y = (int) (d.height - sheight * 1.5); } + g.setFont(font); + Color oldColor = g.getColor(); + g.setColor(Color.lightGray); + g.drawString(str, x, y); + g.setColor(oldColor); + } + + public boolean useTransform() { + return false; + } }); - // add a listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - - Container content = getContentPane(); - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - content.add(panel); - - final DefaultModalGraphMouse graphMouse - = new DefaultModalGraphMouse(); - vv.setGraphMouse(graphMouse); - - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + // add a listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + + Container content = getContentPane(); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + content.add(panel); + + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + vv.setGraphMouse(graphMouse); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - - JComboBox modeBox = graphMouse.getModeComboBox(); - JPanel modePanel = new JPanel(); - modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); - modePanel.add(modeBox); - - JPanel scaleGrid = new JPanel(new GridLayout(1,0)); - scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); - JPanel controls = new JPanel(); - scaleGrid.add(plus); - scaleGrid.add(minus); - controls.add(scaleGrid); - - controls.add(modePanel); - content.add(controls, BorderLayout.SOUTH); - - this.viewSupport = new MagnifyImageLensSupport(vv); - - this.modelSupport = new LayoutLensSupport(vv); - - graphMouse.addItemListener(modelSupport.getGraphMouse().getModeListener()); - graphMouse.addItemListener(viewSupport.getGraphMouse().getModeListener()); - - ButtonGroup radio = new ButtonGroup(); - JRadioButton none = new JRadioButton("None"); - none.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - if(viewSupport != null) { - viewSupport.deactivate(); - } - if(modelSupport != null) { - modelSupport.deactivate(); - } + + JComboBox modeBox = graphMouse.getModeComboBox(); + JPanel modePanel = new JPanel(); + modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); + modePanel.add(modeBox); + + JPanel scaleGrid = new JPanel(new GridLayout(1, 0)); + scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); + JPanel controls = new JPanel(); + scaleGrid.add(plus); + scaleGrid.add(minus); + controls.add(scaleGrid); + + controls.add(modePanel); + content.add(controls, BorderLayout.SOUTH); + + this.viewSupport = new MagnifyImageLensSupport(vv); + + this.modelSupport = new LayoutLensSupport(vv); + + graphMouse.addItemListener(modelSupport.getGraphMouse().getModeListener()); + graphMouse.addItemListener(viewSupport.getGraphMouse().getModeListener()); + + ButtonGroup radio = new ButtonGroup(); + JRadioButton none = new JRadioButton("None"); + none.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (viewSupport != null) { + viewSupport.deactivate(); + } + if (modelSupport != null) { + modelSupport.deactivate(); } + } }); - none.setSelected(true); + none.setSelected(true); - JRadioButton hyperView = new JRadioButton("View"); - hyperView.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - viewSupport.activate(e.getStateChange() == ItemEvent.SELECTED); - } + JRadioButton hyperView = new JRadioButton("View"); + hyperView.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + viewSupport.activate(e.getStateChange() == ItemEvent.SELECTED); + } }); - JRadioButton hyperModel = new JRadioButton("Layout"); - hyperModel.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - modelSupport.activate(e.getStateChange() == ItemEvent.SELECTED); - } + JRadioButton hyperModel = new JRadioButton("Layout"); + hyperModel.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + modelSupport.activate(e.getStateChange() == ItemEvent.SELECTED); + } }); - radio.add(none); - radio.add(hyperView); - radio.add(hyperModel); - - JMenuBar menubar = new JMenuBar(); - JMenu modeMenu = graphMouse.getModeMenu(); - menubar.add(modeMenu); - - JPanel lensPanel = new JPanel(new GridLayout(2,0)); - lensPanel.setBorder(BorderFactory.createTitledBorder("Lens")); - lensPanel.add(none); - lensPanel.add(hyperView); - lensPanel.add(hyperModel); - controls.add(lensPanel); + radio.add(none); + radio.add(hyperView); + radio.add(hyperModel); + + JMenuBar menubar = new JMenuBar(); + JMenu modeMenu = graphMouse.getModeMenu(); + menubar.add(modeMenu); + + JPanel lensPanel = new JPanel(new GridLayout(2, 0)); + lensPanel.setBorder(BorderFactory.createTitledBorder("Lens")); + lensPanel.add(none); + lensPanel.add(hyperView); + lensPanel.add(hyperModel); + controls.add(lensPanel); + } + + /** + * A simple implementation of VertexStringer that gets Vertex labels from a Map + * + * @author Tom Nelson + */ + class VertexStringerImpl implements Function { + + Map map = new HashMap(); + + boolean enabled = true; + + public VertexStringerImpl(Map map) { + this.map = map; } - + /** - * A simple implementation of VertexStringer that - * gets Vertex labels from a Map - * - * @author Tom Nelson - * - * + * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.Vertex) */ - class VertexStringerImpl implements Function { - - Map map = new HashMap(); - - boolean enabled = true; - - public VertexStringerImpl(Map map) { - this.map = map; - } - - /** - * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.Vertex) - */ - public String apply(V v) { - if(isEnabled()) { - return map.get(v); - } else { - return ""; - } - } + public String apply(V v) { + if (isEnabled()) { + return map.get(v); + } else { + return ""; + } + } - /** - * @return Returns the enabled. - */ - public boolean isEnabled() { - return enabled; - } + /** @return Returns the enabled. */ + public boolean isEnabled() { + return enabled; + } - /** - * @param enabled The enabled to set. - */ - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } + /** @param enabled The enabled to set. */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; } - - Network createGraph() { - MutableNetwork graph = NetworkBuilder.directed().build(); - graph.addEdge(0, 1, new Double(Math.random())); - graph.addEdge(3, 0, new Double(Math.random())); - graph.addEdge(0, 4, new Double(Math.random())); - graph.addEdge(4, 5, new Double(Math.random())); - graph.addEdge(5, 3, new Double(Math.random())); - graph.addEdge(2, 1, new Double(Math.random())); - graph.addEdge(4, 1, new Double(Math.random())); - graph.addEdge(8, 2, new Double(Math.random())); - graph.addEdge(3, 8, new Double(Math.random())); - graph.addEdge(6, 7, new Double(Math.random())); - graph.addEdge(7, 5, new Double(Math.random())); - graph.addEdge(0, 9, new Double(Math.random())); - graph.addEdge(9, 8, new Double(Math.random())); - graph.addEdge(7, 6, new Double(Math.random())); - graph.addEdge(6, 5, new Double(Math.random())); - graph.addEdge(4, 2, new Double(Math.random())); - graph.addEdge(5, 4, new Double(Math.random())); - graph.addEdge(4, 10, new Double(Math.random())); - graph.addEdge(10, 4, new Double(Math.random())); - - return graph; + } + + Network createGraph() { + MutableNetwork graph = NetworkBuilder.directed().build(); + graph.addEdge(0, 1, new Double(Math.random())); + graph.addEdge(3, 0, new Double(Math.random())); + graph.addEdge(0, 4, new Double(Math.random())); + graph.addEdge(4, 5, new Double(Math.random())); + graph.addEdge(5, 3, new Double(Math.random())); + graph.addEdge(2, 1, new Double(Math.random())); + graph.addEdge(4, 1, new Double(Math.random())); + graph.addEdge(8, 2, new Double(Math.random())); + graph.addEdge(3, 8, new Double(Math.random())); + graph.addEdge(6, 7, new Double(Math.random())); + graph.addEdge(7, 5, new Double(Math.random())); + graph.addEdge(0, 9, new Double(Math.random())); + graph.addEdge(9, 8, new Double(Math.random())); + graph.addEdge(7, 6, new Double(Math.random())); + graph.addEdge(6, 5, new Double(Math.random())); + graph.addEdge(4, 2, new Double(Math.random())); + graph.addEdge(5, 4, new Double(Math.random())); + graph.addEdge(4, 10, new Double(Math.random())); + graph.addEdge(10, 4, new Double(Math.random())); + + return graph; + } + + public static class PickWithIconListener implements ItemListener { + Function imager; + Icon checked; + + public PickWithIconListener(Function imager) { + this.imager = imager; + checked = new Checkmark(Color.red); } - - public static class PickWithIconListener implements ItemListener { - Function imager; - Icon checked; - - public PickWithIconListener(Function imager) { - this.imager = imager; - checked = new Checkmark(Color.red); - } - public void itemStateChanged(ItemEvent e) { - Icon icon = imager.apply((Integer)e.getItem()); - if(icon != null && icon instanceof LayeredIcon) { - if(e.getStateChange() == ItemEvent.SELECTED) { - ((LayeredIcon)icon).add(checked); - } else { - ((LayeredIcon)icon).remove(checked); - } - } + public void itemStateChanged(ItemEvent e) { + Icon icon = imager.apply((Integer) e.getItem()); + if (icon != null && icon instanceof LayeredIcon) { + if (e.getStateChange() == ItemEvent.SELECTED) { + ((LayeredIcon) icon).add(checked); + } else { + ((LayeredIcon) icon).remove(checked); } + } } + } + public static void main(String[] args) { + JFrame frame = new JFrame(); + Container content = frame.getContentPane(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - public static void main(String[] args) { - JFrame frame = new JFrame(); - Container content = frame.getContentPane(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - content.add(new DemoLensVertexImageShaperDemo()); - frame.pack(); - frame.setVisible(true); - } + content.add(new DemoLensVertexImageShaperDemo()); + frame.pack(); + frame.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/DrawnIconVertexDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/DrawnIconVertexDemo.java index c55cbc12..a418a7c4 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/DrawnIconVertexDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/DrawnIconVertexDemo.java @@ -1,31 +1,17 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Container; -import java.awt.Graphics; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.Icon; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JPanel; - import com.google.common.base.Function; import com.google.common.graph.MutableNetwork; import com.google.common.graph.Network; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.layout.FRLayout; import edu.uci.ics.jung.visualization.GraphZoomScrollPane; import edu.uci.ics.jung.visualization.VisualizationViewer; @@ -37,145 +23,163 @@ import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; import edu.uci.ics.jung.visualization.renderers.DefaultEdgeLabelRenderer; import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; /** * A demo that shows drawn Icons as vertices - * - * @author Tom Nelson - * + * + * @author Tom Nelson */ public class DrawnIconVertexDemo { - /** - * the graph - */ - Network graph; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - public DrawnIconVertexDemo() { - - // create a simple graph for the demo - graph = createGraph(); - - vv = new VisualizationViewer(graph, new FRLayout(graph.asGraph())); - vv.getRenderContext().setVertexLabelTransformer(new Function(){ - - public String apply(Integer v) { - return "Vertex "+v; - }}); - vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan)); - vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan)); - - vv.getRenderContext().setVertexIconTransformer(new Function() { - - /* - * Implements the Icon interface to draw an Icon with background color and - * a text label - */ - public Icon apply(final Integer v) { - return new Icon() { - - public int getIconHeight() { - return 20; - } - - public int getIconWidth() { - return 20; - } - - public void paintIcon(Component c, Graphics g, - int x, int y) { - if(vv.getPickedVertexState().isPicked(v)) { - g.setColor(Color.yellow); - } else { - g.setColor(Color.red); - } - g.fillOval(x, y, 20, 20); - if(vv.getPickedVertexState().isPicked(v)) { - g.setColor(Color.black); - } else { - g.setColor(Color.white); - } - g.drawString(""+v, x+6, y+15); - - }}; - }}); - - vv.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(vv.getPickedVertexState(), Color.white, Color.yellow)); - vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(vv.getPickedEdgeState(), Color.black, Color.lightGray)); - - vv.setBackground(Color.white); - - // add my listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - - // create a frome to hold the graph - final JFrame frame = new JFrame(); - Container content = frame.getContentPane(); - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - content.add(panel); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - final DefaultModalGraphMouse gm - = new DefaultModalGraphMouse(); - vv.setGraphMouse(gm); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + /** the graph */ + Network graph; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + public DrawnIconVertexDemo() { + + // create a simple graph for the demo + graph = createGraph(); + + vv = new VisualizationViewer(graph, new FRLayout(graph.asGraph())); + vv.getRenderContext() + .setVertexLabelTransformer( + new Function() { + + public String apply(Integer v) { + return "Vertex " + v; + } + }); + vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan)); + vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan)); + + vv.getRenderContext() + .setVertexIconTransformer( + new Function() { + + /* + * Implements the Icon interface to draw an Icon with background color and + * a text label + */ + public Icon apply(final Integer v) { + return new Icon() { + + public int getIconHeight() { + return 20; + } + + public int getIconWidth() { + return 20; + } + + public void paintIcon(Component c, Graphics g, int x, int y) { + if (vv.getPickedVertexState().isPicked(v)) { + g.setColor(Color.yellow); + } else { + g.setColor(Color.red); + } + g.fillOval(x, y, 20, 20); + if (vv.getPickedVertexState().isPicked(v)) { + g.setColor(Color.black); + } else { + g.setColor(Color.white); + } + g.drawString("" + v, x + 6, y + 15); + } + }; + } + }); + + vv.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer( + vv.getPickedVertexState(), Color.white, Color.yellow)); + vv.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv.getPickedEdgeState(), Color.black, Color.lightGray)); + + vv.setBackground(Color.white); + + // add my listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + + // create a frome to hold the graph + final JFrame frame = new JFrame(); + Container content = frame.getContentPane(); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + content.add(panel); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + final DefaultModalGraphMouse gm = + new DefaultModalGraphMouse(); + vv.setGraphMouse(gm); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - JPanel controls = new JPanel(); - controls.add(plus); - controls.add(minus); - controls.add(gm.getModeComboBox()); - content.add(controls, BorderLayout.SOUTH); - - frame.pack(); - frame.setVisible(true); - } - - Network createGraph() { - MutableNetwork graph = NetworkBuilder.directed().build(); - graph.addEdge(0, 1, new Double(Math.random())); - graph.addEdge(3, 0, new Double(Math.random())); - graph.addEdge(0, 4, new Double(Math.random())); - graph.addEdge(4, 5, new Double(Math.random())); - graph.addEdge(5, 3, new Double(Math.random())); - graph.addEdge(2, 1, new Double(Math.random())); - graph.addEdge(4, 1, new Double(Math.random())); - graph.addEdge(8, 2, new Double(Math.random())); - graph.addEdge(3, 8, new Double(Math.random())); - graph.addEdge(6, 7, new Double(Math.random())); - graph.addEdge(7, 5, new Double(Math.random())); - graph.addEdge(0, 9, new Double(Math.random())); - graph.addEdge(9, 8, new Double(Math.random())); - graph.addEdge(7, 6, new Double(Math.random())); - graph.addEdge(6, 5, new Double(Math.random())); - graph.addEdge(4, 2, new Double(Math.random())); - graph.addEdge(5, 4, new Double(Math.random())); - graph.addEdge(4, 10, new Double(Math.random())); - graph.addEdge(10, 4, new Double(Math.random())); - - return graph; - } - - public static void main(String[] args) - { - new DrawnIconVertexDemo(); - } + JPanel controls = new JPanel(); + controls.add(plus); + controls.add(minus); + controls.add(gm.getModeComboBox()); + content.add(controls, BorderLayout.SOUTH); + + frame.pack(); + frame.setVisible(true); + } + + Network createGraph() { + MutableNetwork graph = NetworkBuilder.directed().build(); + graph.addEdge(0, 1, new Double(Math.random())); + graph.addEdge(3, 0, new Double(Math.random())); + graph.addEdge(0, 4, new Double(Math.random())); + graph.addEdge(4, 5, new Double(Math.random())); + graph.addEdge(5, 3, new Double(Math.random())); + graph.addEdge(2, 1, new Double(Math.random())); + graph.addEdge(4, 1, new Double(Math.random())); + graph.addEdge(8, 2, new Double(Math.random())); + graph.addEdge(3, 8, new Double(Math.random())); + graph.addEdge(6, 7, new Double(Math.random())); + graph.addEdge(7, 5, new Double(Math.random())); + graph.addEdge(0, 9, new Double(Math.random())); + graph.addEdge(9, 8, new Double(Math.random())); + graph.addEdge(7, 6, new Double(Math.random())); + graph.addEdge(6, 5, new Double(Math.random())); + graph.addEdge(4, 2, new Double(Math.random())); + graph.addEdge(5, 4, new Double(Math.random())); + graph.addEdge(4, 10, new Double(Math.random())); + graph.addEdge(10, 4, new Double(Math.random())); + + return graph; + } + + public static void main(String[] args) { + new DrawnIconVertexDemo(); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/EdgeLabelDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/EdgeLabelDemo.java index e0252e7c..d3f019bc 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/EdgeLabelDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/EdgeLabelDemo.java @@ -1,13 +1,32 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; +import com.google.common.base.Function; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.Network; +import com.google.common.graph.NetworkBuilder; +import edu.uci.ics.jung.algorithms.layout.CircleLayout; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.decorators.EdgeShape; +import edu.uci.ics.jung.visualization.decorators.ParallelEdgeShapeTransformer; +import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; +import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; +import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; +import edu.uci.ics.jung.visualization.renderers.EdgeLabelRenderer; +import edu.uci.ics.jung.visualization.renderers.VertexLabelRenderer; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; @@ -18,7 +37,6 @@ import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; - import javax.swing.AbstractButton; import javax.swing.BorderFactory; import javax.swing.BoundedRangeModel; @@ -36,266 +54,252 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import com.google.common.base.Function; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.Network; -import com.google.common.graph.NetworkBuilder; - -import edu.uci.ics.jung.algorithms.layout.CircleLayout; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.GraphZoomScrollPane; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; -import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ScalingControl; -import edu.uci.ics.jung.visualization.decorators.EdgeShape; -import edu.uci.ics.jung.visualization.decorators.ParallelEdgeShapeTransformer; -import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; -import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; -import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; -import edu.uci.ics.jung.visualization.renderers.EdgeLabelRenderer; -import edu.uci.ics.jung.visualization.renderers.VertexLabelRenderer; - /** - * Demonstrates jung support for drawing edge labels that - * can be positioned at any point along the edge, and can - * be rotated to be parallel with the edge. - * + * Demonstrates jung support for drawing edge labels that can be positioned at any point along the + * edge, and can be rotated to be parallel with the edge. + * * @author Tom Nelson - * */ public class EdgeLabelDemo extends JApplet { - private static final long serialVersionUID = -6077157664507049647L; - - /** - * the graph - */ - Network graph; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - /** - */ - VertexLabelRenderer vertexLabelRenderer; - EdgeLabelRenderer edgeLabelRenderer; - - ScalingControl scaler = new CrossoverScalingControl(); - - /** - * create an instance of a simple graph with controls to - * demo the label positioning features - * - */ - @SuppressWarnings("serial") - public EdgeLabelDemo() { - - // create a simple graph for the demo - graph = buildGraph(); - - Layout layout = new CircleLayout(graph.asGraph()); - vv = new VisualizationViewer(graph, layout, new Dimension(600,400)); - vv.setBackground(Color.white); - - vertexLabelRenderer = vv.getRenderContext().getVertexLabelRenderer(); - edgeLabelRenderer = vv.getRenderContext().getEdgeLabelRenderer(); - - Function stringer = new Function(){ - public String apply(Number e) { - return "Edge:"+graph.incidentNodes(e).toString(); - } + private static final long serialVersionUID = -6077157664507049647L; + + /** the graph */ + Network graph; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + /** */ + VertexLabelRenderer vertexLabelRenderer; + + EdgeLabelRenderer edgeLabelRenderer; + + ScalingControl scaler = new CrossoverScalingControl(); + + /** create an instance of a simple graph with controls to demo the label positioning features */ + @SuppressWarnings("serial") + public EdgeLabelDemo() { + + // create a simple graph for the demo + graph = buildGraph(); + + Layout layout = new CircleLayout(graph.asGraph()); + vv = new VisualizationViewer(graph, layout, new Dimension(600, 400)); + vv.setBackground(Color.white); + + vertexLabelRenderer = vv.getRenderContext().getVertexLabelRenderer(); + edgeLabelRenderer = vv.getRenderContext().getEdgeLabelRenderer(); + + Function stringer = + new Function() { + public String apply(Number e) { + return "Edge:" + graph.incidentNodes(e).toString(); + } }; - vv.getRenderContext().setEdgeLabelTransformer(stringer); - vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(vv.getPickedEdgeState(), Color.black, Color.cyan)); - vv.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(vv.getPickedVertexState(), Color.red, Color.yellow)); - // add my listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - - // create a frome to hold the graph - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - Container content = getContentPane(); - content.add(panel); - - final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse(); - vv.setGraphMouse(graphMouse); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + vv.getRenderContext().setEdgeLabelTransformer(stringer); + vv.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv.getPickedEdgeState(), Color.black, Color.cyan)); + vv.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer( + vv.getPickedVertexState(), Color.red, Color.yellow)); + // add my listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + + // create a frome to hold the graph + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + Container content = getContentPane(); + content.add(panel); + + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + vv.setGraphMouse(graphMouse); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - - ButtonGroup radio = new ButtonGroup(); - JRadioButton lineButton = new JRadioButton("Line"); - lineButton.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); - vv.repaint(); - } + + ButtonGroup radio = new ButtonGroup(); + JRadioButton lineButton = new JRadioButton("Line"); + lineButton.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); + vv.repaint(); } + } }); - - JRadioButton quadButton = new JRadioButton("QuadCurve"); - quadButton.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.quadCurve(graph)); - vv.repaint(); - } + + JRadioButton quadButton = new JRadioButton("QuadCurve"); + quadButton.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.quadCurve(graph)); + vv.repaint(); } + } }); - - JRadioButton cubicButton = new JRadioButton("CubicCurve"); - cubicButton.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.cubicCurve(graph)); - vv.repaint(); - } + + JRadioButton cubicButton = new JRadioButton("CubicCurve"); + cubicButton.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.cubicCurve(graph)); + vv.repaint(); } + } }); - radio.add(lineButton); - radio.add(quadButton); - radio.add(cubicButton); - - graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); - - JCheckBox rotate = new JCheckBox("

    EdgeType

    Parallel

    "); - rotate.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - AbstractButton b = (AbstractButton)e.getSource(); - edgeLabelRenderer.setRotateEdgeLabels(b.isSelected()); - vv.repaint(); - } + radio.add(lineButton); + radio.add(quadButton); + radio.add(cubicButton); + + graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); + + JCheckBox rotate = new JCheckBox("
    EdgeType

    Parallel

    "); + rotate.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + AbstractButton b = (AbstractButton) e.getSource(); + edgeLabelRenderer.setRotateEdgeLabels(b.isSelected()); + vv.repaint(); + } }); - rotate.setSelected(true); - EdgeClosenessUpdater edgeClosenessUpdater = new EdgeClosenessUpdater(); - JSlider closenessSlider = new JSlider(edgeClosenessUpdater.rangeModel) { - public Dimension getPreferredSize() { - Dimension d = super.getPreferredSize(); - d.width /= 2; - return d; - } + rotate.setSelected(true); + EdgeClosenessUpdater edgeClosenessUpdater = new EdgeClosenessUpdater(); + JSlider closenessSlider = + new JSlider(edgeClosenessUpdater.rangeModel) { + public Dimension getPreferredSize() { + Dimension d = super.getPreferredSize(); + d.width /= 2; + return d; + } }; - - JSlider edgeOffsetSlider = new JSlider(0,50) { - public Dimension getPreferredSize() { - Dimension d = super.getPreferredSize(); - d.width /= 2; - return d; - } + + JSlider edgeOffsetSlider = + new JSlider(0, 50) { + public Dimension getPreferredSize() { + Dimension d = super.getPreferredSize(); + d.width /= 2; + return d; + } }; - edgeOffsetSlider.addChangeListener(new ChangeListener() { - @SuppressWarnings("rawtypes") - public void stateChanged(ChangeEvent e) { - JSlider s = (JSlider)e.getSource(); - Function edgeShapeFunction - = vv.getRenderContext().getEdgeShapeTransformer(); - if (edgeShapeFunction instanceof ParallelEdgeShapeTransformer) { - ((ParallelEdgeShapeTransformer)edgeShapeFunction) - .setControlOffsetIncrement(s.getValue()); - vv.repaint(); - } + edgeOffsetSlider.addChangeListener( + new ChangeListener() { + @SuppressWarnings("rawtypes") + public void stateChanged(ChangeEvent e) { + JSlider s = (JSlider) e.getSource(); + Function edgeShapeFunction = + vv.getRenderContext().getEdgeShapeTransformer(); + if (edgeShapeFunction instanceof ParallelEdgeShapeTransformer) { + ((ParallelEdgeShapeTransformer) edgeShapeFunction) + .setControlOffsetIncrement(s.getValue()); + vv.repaint(); } + } }); - - Box controls = Box.createHorizontalBox(); - - JPanel zoomPanel = new JPanel(new GridLayout(0,1)); - zoomPanel.setBorder(BorderFactory.createTitledBorder("Scale")); - zoomPanel.add(plus); - zoomPanel.add(minus); - - JPanel edgePanel = new JPanel(new GridLayout(0,1)); - edgePanel.setBorder(BorderFactory.createTitledBorder("EdgeType Type")); - edgePanel.add(lineButton); - edgePanel.add(quadButton); - edgePanel.add(cubicButton); - - JPanel rotatePanel = new JPanel(); - rotatePanel.setBorder(BorderFactory.createTitledBorder("Alignment")); - rotatePanel.add(rotate); - - JPanel labelPanel = new JPanel(new BorderLayout()); - JPanel sliderPanel = new JPanel(new GridLayout(3,1)); - JPanel sliderLabelPanel = new JPanel(new GridLayout(3,1)); - JPanel offsetPanel = new JPanel(new BorderLayout()); - offsetPanel.setBorder(BorderFactory.createTitledBorder("Offset")); - sliderPanel.add(closenessSlider); - sliderPanel.add(edgeOffsetSlider); - sliderLabelPanel.add(new JLabel("Closeness", JLabel.RIGHT)); - sliderLabelPanel.add(new JLabel("Edges", JLabel.RIGHT)); - offsetPanel.add(sliderLabelPanel, BorderLayout.WEST); - offsetPanel.add(sliderPanel); - labelPanel.add(offsetPanel); - labelPanel.add(rotatePanel, BorderLayout.WEST); - - JPanel modePanel = new JPanel(new GridLayout(2,1)); - modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); - modePanel.add(graphMouse.getModeComboBox()); - - controls.add(zoomPanel); - controls.add(edgePanel); - controls.add(labelPanel); - controls.add(modePanel); - content.add(controls, BorderLayout.SOUTH); - quadButton.setSelected(true); - } - - /** - * subclassed to hold two BoundedRangeModel instances that - * are used by JSliders to move the edge label positions - * @author Tom Nelson - * - * - */ - class EdgeClosenessUpdater { - BoundedRangeModel rangeModel; - - public EdgeClosenessUpdater() { //double undirected, double directed) { - int initialValue = ((int) vv.getRenderContext().getEdgeLabelCloseness() * 10) / 10; - this.rangeModel = new DefaultBoundedRangeModel(initialValue, 0, 0, 10); - - rangeModel.addChangeListener(new ChangeListener() { - public void stateChanged(ChangeEvent e) { - vv.getRenderContext().setEdgeLabelCloseness(rangeModel.getValue() / 10f); - vv.repaint(); - } - }); - } - } - - Network buildGraph() { - MutableNetwork graph = NetworkBuilder.directed().build(); - - graph.addEdge(0, 1, new Double(Math.random())); - graph.addEdge(0, 1, new Double(Math.random())); - graph.addEdge(0, 1, new Double(Math.random())); - graph.addEdge(1, 0, new Double(Math.random())); - graph.addEdge(1, 0, new Double(Math.random())); - graph.addEdge(1, 2, new Double(Math.random())); - graph.addEdge(1, 2, new Double(Math.random())); - - return graph; - } - - public static void main(String[] args) { - JFrame frame = new JFrame(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - Container content = frame.getContentPane(); - content.add(new EdgeLabelDemo()); - frame.pack(); - frame.setVisible(true); + + Box controls = Box.createHorizontalBox(); + + JPanel zoomPanel = new JPanel(new GridLayout(0, 1)); + zoomPanel.setBorder(BorderFactory.createTitledBorder("Scale")); + zoomPanel.add(plus); + zoomPanel.add(minus); + + JPanel edgePanel = new JPanel(new GridLayout(0, 1)); + edgePanel.setBorder(BorderFactory.createTitledBorder("EdgeType Type")); + edgePanel.add(lineButton); + edgePanel.add(quadButton); + edgePanel.add(cubicButton); + + JPanel rotatePanel = new JPanel(); + rotatePanel.setBorder(BorderFactory.createTitledBorder("Alignment")); + rotatePanel.add(rotate); + + JPanel labelPanel = new JPanel(new BorderLayout()); + JPanel sliderPanel = new JPanel(new GridLayout(3, 1)); + JPanel sliderLabelPanel = new JPanel(new GridLayout(3, 1)); + JPanel offsetPanel = new JPanel(new BorderLayout()); + offsetPanel.setBorder(BorderFactory.createTitledBorder("Offset")); + sliderPanel.add(closenessSlider); + sliderPanel.add(edgeOffsetSlider); + sliderLabelPanel.add(new JLabel("Closeness", JLabel.RIGHT)); + sliderLabelPanel.add(new JLabel("Edges", JLabel.RIGHT)); + offsetPanel.add(sliderLabelPanel, BorderLayout.WEST); + offsetPanel.add(sliderPanel); + labelPanel.add(offsetPanel); + labelPanel.add(rotatePanel, BorderLayout.WEST); + + JPanel modePanel = new JPanel(new GridLayout(2, 1)); + modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); + modePanel.add(graphMouse.getModeComboBox()); + + controls.add(zoomPanel); + controls.add(edgePanel); + controls.add(labelPanel); + controls.add(modePanel); + content.add(controls, BorderLayout.SOUTH); + quadButton.setSelected(true); + } + + /** + * subclassed to hold two BoundedRangeModel instances that are used by JSliders to move the edge + * label positions + * + * @author Tom Nelson + */ + class EdgeClosenessUpdater { + BoundedRangeModel rangeModel; + + public EdgeClosenessUpdater() { //double undirected, double directed) { + int initialValue = ((int) vv.getRenderContext().getEdgeLabelCloseness() * 10) / 10; + this.rangeModel = new DefaultBoundedRangeModel(initialValue, 0, 0, 10); + + rangeModel.addChangeListener( + new ChangeListener() { + public void stateChanged(ChangeEvent e) { + vv.getRenderContext().setEdgeLabelCloseness(rangeModel.getValue() / 10f); + vv.repaint(); + } + }); } + } + + Network buildGraph() { + MutableNetwork graph = NetworkBuilder.directed().build(); + + graph.addEdge(0, 1, new Double(Math.random())); + graph.addEdge(0, 1, new Double(Math.random())); + graph.addEdge(0, 1, new Double(Math.random())); + graph.addEdge(1, 0, new Double(Math.random())); + graph.addEdge(1, 0, new Double(Math.random())); + graph.addEdge(1, 2, new Double(Math.random())); + graph.addEdge(1, 2, new Double(Math.random())); + + return graph; + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + Container content = frame.getContentPane(); + content.add(new EdgeLabelDemo()); + frame.pack(); + frame.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/GraphEditorDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/GraphEditorDemo.java index fc1d5881..97776e3e 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/GraphEditorDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/GraphEditorDemo.java @@ -1,13 +1,28 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.NetworkBuilder; +import edu.uci.ics.jung.algorithms.layout.AbstractLayout; +import edu.uci.ics.jung.algorithms.layout.StaticLayout; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.annotations.AnnotationControls; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.EditingModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ModalGraphMouse.Mode; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; @@ -19,7 +34,6 @@ import java.awt.print.Printable; import java.awt.print.PrinterJob; import java.io.File; - import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.JApplet; @@ -33,266 +47,240 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; -import com.google.common.base.Function; -import com.google.common.base.Supplier; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.NetworkBuilder; - -import edu.uci.ics.jung.algorithms.layout.AbstractLayout; -import edu.uci.ics.jung.algorithms.layout.StaticLayout; -import edu.uci.ics.jung.visualization.GraphZoomScrollPane; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.annotations.AnnotationControls; -import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; -import edu.uci.ics.jung.visualization.control.EditingModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ModalGraphMouse.Mode; -import edu.uci.ics.jung.visualization.control.ScalingControl; -import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; - /** - * Shows how to create a graph editor with JUNG. - * Mouse modes and actions are explained in the help text. - * The application version of GraphEditorDemo provides a - * File menu with an option to save the visible graph as - * a jpeg file. - * + * Shows how to create a graph editor with JUNG. Mouse modes and actions are explained in the help + * text. The application version of GraphEditorDemo provides a File menu with an option to save the + * visible graph as a jpeg file. + * * @author Tom Nelson - * */ public class GraphEditorDemo extends JApplet implements Printable { - /** - * - */ - private static final long serialVersionUID = -2023243689258876709L; - - /** - * the graph - */ - MutableNetwork graph; - - AbstractLayout layout; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - String instructions = - ""+ - "

    All Modes:

    "+ - "
      "+ - "
    • Right-click an empty area for Create Vertex popup"+ - "
    • Right-click on a Vertex for Delete Vertex popup"+ - "
    • Right-click on a Vertex for Add Edge menus
      (if there are selected Vertices)"+ - "
    • Right-click on an Edge for Delete Edge popup"+ - "
    • Mousewheel scales with a crossover value of 1.0.

      "+ - " - scales the graph layout when the combined scale is greater than 1

      "+ - " - scales the graph view when the combined scale is less than 1"+ - - "

    "+ - "

    Editing Mode:

    "+ - "
      "+ - "
    • Left-click an empty area to create a new Vertex"+ - "
    • Left-click on a Vertex and drag to another Vertex to create an Undirected Edge"+ - "
    • Shift+Left-click on a Vertex and drag to another Vertex to create a Directed Edge"+ - "
    "+ - "

    Picking Mode:

    "+ - "
      "+ - "
    • Mouse1 on a Vertex selects the vertex"+ - "
    • Mouse1 elsewhere unselects all Vertices"+ - "
    • Mouse1+Shift on a Vertex adds/removes Vertex selection"+ - "
    • Mouse1+drag on a Vertex moves all selected Vertices"+ - "
    • Mouse1+drag elsewhere selects Vertices in a region"+ - "
    • Mouse1+Shift+drag adds selection of Vertices in a new region"+ - "
    • Mouse1+CTRL on a Vertex selects the vertex and centers the display on it"+ - "
    • Mouse1 double-click on a vertex or edge allows you to edit the label"+ - "
    "+ - "

    Transforming Mode:

    "+ - "
      "+ - "
    • Mouse1+drag pans the graph"+ - "
    • Mouse1+Shift+drag rotates the graph"+ - "
    • Mouse1+CTRL(or Command)+drag shears the graph"+ - "
    • Mouse1 double-click on a vertex or edge allows you to edit the label"+ - "
    "+ - "

    Annotation Mode:

    "+ - "
      "+ - "
    • Mouse1 begins drawing of a Rectangle"+ - "
    • Mouse1+drag defines the Rectangle shape"+ - "
    • Mouse1 release adds the Rectangle as an annotation"+ - "
    • Mouse1+Shift begins drawing of an Ellipse"+ - "
    • Mouse1+Shift+drag defines the Ellipse shape"+ - "
    • Mouse1+Shift release adds the Ellipse as an annotation"+ - "
    • Mouse3 shows a popup to input text, which will become"+ - "
    • a text annotation on the graph at the mouse location"+ - "
    "+ - ""; - - /** - * create an instance of a simple graph with popup controls to - * create a graph. - * - */ - public GraphEditorDemo() { - - // create a simple graph for the demo - graph = NetworkBuilder.directed().allowsParallelEdges(true).allowsSelfLoops(true).build(); - - this.layout = new StaticLayout(graph.asGraph(), new Dimension(600,600)); - - vv = new VisualizationViewer(graph, layout); - vv.setBackground(Color.white); - - Function labeller = new ToStringLabeller(); - vv.getRenderContext().setVertexLabelTransformer(labeller); - vv.getRenderContext().setEdgeLabelTransformer(labeller); - - vv.setVertexToolTipTransformer(vv.getRenderContext().getVertexLabelTransformer()); - - - Container content = getContentPane(); - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - content.add(panel); - Supplier vertexFactory = new VertexFactory(); - Supplier edgeFactory = new EdgeFactory(); - - final EditingModalGraphMouse graphMouse = - new EditingModalGraphMouse(vv.getRenderContext(), vertexFactory, edgeFactory); - - // the EditingGraphMouse will pass mouse event coordinates to the - // vertexLocations function to set the locations of the vertices as - // they are created - vv.setGraphMouse(graphMouse); - vv.addKeyListener(graphMouse.getModeKeyListener()); - - graphMouse.setMode(ModalGraphMouse.Mode.EDITING); - - final ScalingControl scaler = new CrossoverScalingControl(); - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + /** */ + private static final long serialVersionUID = -2023243689258876709L; + + /** the graph */ + MutableNetwork graph; + + AbstractLayout layout; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + String instructions = + "" + + "

    All Modes:

    " + + "
      " + + "
    • Right-click an empty area for Create Vertex popup" + + "
    • Right-click on a Vertex for Delete Vertex popup" + + "
    • Right-click on a Vertex for Add Edge menus
      (if there are selected Vertices)" + + "
    • Right-click on an Edge for Delete Edge popup" + + "
    • Mousewheel scales with a crossover value of 1.0.

      " + + " - scales the graph layout when the combined scale is greater than 1

      " + + " - scales the graph view when the combined scale is less than 1" + + "

    " + + "

    Editing Mode:

    " + + "
      " + + "
    • Left-click an empty area to create a new Vertex" + + "
    • Left-click on a Vertex and drag to another Vertex to create an Undirected Edge" + + "
    • Shift+Left-click on a Vertex and drag to another Vertex to create a Directed Edge" + + "
    " + + "

    Picking Mode:

    " + + "
      " + + "
    • Mouse1 on a Vertex selects the vertex" + + "
    • Mouse1 elsewhere unselects all Vertices" + + "
    • Mouse1+Shift on a Vertex adds/removes Vertex selection" + + "
    • Mouse1+drag on a Vertex moves all selected Vertices" + + "
    • Mouse1+drag elsewhere selects Vertices in a region" + + "
    • Mouse1+Shift+drag adds selection of Vertices in a new region" + + "
    • Mouse1+CTRL on a Vertex selects the vertex and centers the display on it" + + "
    • Mouse1 double-click on a vertex or edge allows you to edit the label" + + "
    " + + "

    Transforming Mode:

    " + + "
      " + + "
    • Mouse1+drag pans the graph" + + "
    • Mouse1+Shift+drag rotates the graph" + + "
    • Mouse1+CTRL(or Command)+drag shears the graph" + + "
    • Mouse1 double-click on a vertex or edge allows you to edit the label" + + "
    " + + "

    Annotation Mode:

    " + + "
      " + + "
    • Mouse1 begins drawing of a Rectangle" + + "
    • Mouse1+drag defines the Rectangle shape" + + "
    • Mouse1 release adds the Rectangle as an annotation" + + "
    • Mouse1+Shift begins drawing of an Ellipse" + + "
    • Mouse1+Shift+drag defines the Ellipse shape" + + "
    • Mouse1+Shift release adds the Ellipse as an annotation" + + "
    • Mouse3 shows a popup to input text, which will become" + + "
    • a text annotation on the graph at the mouse location" + + "
    " + + ""; + + /** create an instance of a simple graph with popup controls to create a graph. */ + public GraphEditorDemo() { + + // create a simple graph for the demo + graph = NetworkBuilder.directed().allowsParallelEdges(true).allowsSelfLoops(true).build(); + + this.layout = new StaticLayout(graph.asGraph(), new Dimension(600, 600)); + + vv = new VisualizationViewer(graph, layout); + vv.setBackground(Color.white); + + Function labeller = new ToStringLabeller(); + vv.getRenderContext().setVertexLabelTransformer(labeller); + vv.getRenderContext().setEdgeLabelTransformer(labeller); + + vv.setVertexToolTipTransformer(vv.getRenderContext().getVertexLabelTransformer()); + + Container content = getContentPane(); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + content.add(panel); + Supplier vertexFactory = new VertexFactory(); + Supplier edgeFactory = new EdgeFactory(); + + final EditingModalGraphMouse graphMouse = + new EditingModalGraphMouse( + vv.getRenderContext(), vertexFactory, edgeFactory); + + // the EditingGraphMouse will pass mouse event coordinates to the + // vertexLocations function to set the locations of the vertices as + // they are created + vv.setGraphMouse(graphMouse); + vv.addKeyListener(graphMouse.getModeKeyListener()); + + graphMouse.setMode(ModalGraphMouse.Mode.EDITING); + + final ScalingControl scaler = new CrossoverScalingControl(); + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - - JButton help = new JButton("Help"); - help.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - JOptionPane.showMessageDialog(vv, instructions); - }}); - - AnnotationControls annotationControls = - new AnnotationControls(graphMouse.getAnnotatingPlugin()); - JPanel controls = new JPanel(); - controls.add(plus); - controls.add(minus); - JComboBox modeBox = graphMouse.getModeComboBox(); - controls.add(modeBox); - controls.add(annotationControls.getAnnotationsToolBar()); - controls.add(help); - content.add(controls, BorderLayout.SOUTH); - } - - /** - * copy the visible part of the graph to a file as a jpeg image - * @param file the file in which to save the graph image - */ - public void writeJPEGImage(File file) { - int width = vv.getWidth(); - int height = vv.getHeight(); - - BufferedImage bi = new BufferedImage(width, height, - BufferedImage.TYPE_INT_RGB); - Graphics2D graphics = bi.createGraphics(); - vv.paint(graphics); - graphics.dispose(); - - try { - ImageIO.write(bi, "jpeg", file); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public int print(java.awt.Graphics graphics, - java.awt.print.PageFormat pageFormat, int pageIndex) - throws java.awt.print.PrinterException { - if (pageIndex > 0) { - return (Printable.NO_SUCH_PAGE); - } else { - java.awt.Graphics2D g2d = (java.awt.Graphics2D) graphics; - vv.setDoubleBuffered(false); - g2d.translate(pageFormat.getImageableX(), pageFormat - .getImageableY()); - - vv.paint(g2d); - vv.setDoubleBuffered(true); - - return (Printable.PAGE_EXISTS); - } + + JButton help = new JButton("Help"); + help.addActionListener( + new ActionListener() { + + public void actionPerformed(ActionEvent e) { + JOptionPane.showMessageDialog(vv, instructions); + } + }); + + AnnotationControls annotationControls = + new AnnotationControls(graphMouse.getAnnotatingPlugin()); + JPanel controls = new JPanel(); + controls.add(plus); + controls.add(minus); + JComboBox modeBox = graphMouse.getModeComboBox(); + controls.add(modeBox); + controls.add(annotationControls.getAnnotationsToolBar()); + controls.add(help); + content.add(controls, BorderLayout.SOUTH); + } + + /** + * copy the visible part of the graph to a file as a jpeg image + * + * @param file the file in which to save the graph image + */ + public void writeJPEGImage(File file) { + int width = vv.getWidth(); + int height = vv.getHeight(); + + BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = bi.createGraphics(); + vv.paint(graphics); + graphics.dispose(); + + try { + ImageIO.write(bi, "jpeg", file); + } catch (Exception e) { + e.printStackTrace(); } - - class VertexFactory implements Supplier { + } - int i=0; + public int print(java.awt.Graphics graphics, java.awt.print.PageFormat pageFormat, int pageIndex) + throws java.awt.print.PrinterException { + if (pageIndex > 0) { + return (Printable.NO_SUCH_PAGE); + } else { + java.awt.Graphics2D g2d = (java.awt.Graphics2D) graphics; + vv.setDoubleBuffered(false); + g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); - public Number get() { - return i++; - } + vv.paint(g2d); + vv.setDoubleBuffered(true); + + return (Printable.PAGE_EXISTS); } - - class EdgeFactory implements Supplier { - - int i=0; - - public Number get() { - return i++; - } + } + + class VertexFactory implements Supplier { + + int i = 0; + + public Number get() { + return i++; } + } - @SuppressWarnings("serial") - public static void main(String[] args) { - JFrame frame = new JFrame(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - final GraphEditorDemo demo = new GraphEditorDemo(); - - JMenu menu = new JMenu("File"); - menu.add(new AbstractAction("Make Image") { - public void actionPerformed(ActionEvent e) { - JFileChooser chooser = new JFileChooser(); - int option = chooser.showSaveDialog(demo); - if(option == JFileChooser.APPROVE_OPTION) { - File file = chooser.getSelectedFile(); - demo.writeJPEGImage(file); - } - }}); - menu.add(new AbstractAction("Print") { - public void actionPerformed(ActionEvent e) { - PrinterJob printJob = PrinterJob.getPrinterJob(); - printJob.setPrintable(demo); - if (printJob.printDialog()) { - try { - printJob.print(); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - }}); - JPopupMenu.setDefaultLightWeightPopupEnabled(false); - JMenuBar menuBar = new JMenuBar(); - menuBar.add(menu); - frame.setJMenuBar(menuBar); - frame.getContentPane().add(demo); - frame.pack(); - frame.setVisible(true); + class EdgeFactory implements Supplier { + + int i = 0; + + public Number get() { + return i++; } -} + } + @SuppressWarnings("serial") + public static void main(String[] args) { + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + final GraphEditorDemo demo = new GraphEditorDemo(); + + JMenu menu = new JMenu("File"); + menu.add( + new AbstractAction("Make Image") { + public void actionPerformed(ActionEvent e) { + JFileChooser chooser = new JFileChooser(); + int option = chooser.showSaveDialog(demo); + if (option == JFileChooser.APPROVE_OPTION) { + File file = chooser.getSelectedFile(); + demo.writeJPEGImage(file); + } + } + }); + menu.add( + new AbstractAction("Print") { + public void actionPerformed(ActionEvent e) { + PrinterJob printJob = PrinterJob.getPrinterJob(); + printJob.setPrintable(demo); + if (printJob.printDialog()) { + try { + printJob.print(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + }); + JPopupMenu.setDefaultLightWeightPopupEnabled(false); + JMenuBar menuBar = new JMenuBar(); + menuBar.add(menu); + frame.setJMenuBar(menuBar); + frame.getContentPane().add(demo); + frame.pack(); + frame.setVisible(true); + } +} diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/GraphFromGraphMLDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/GraphFromGraphMLDemo.java index ee167650..b52e6a53 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/GraphFromGraphMLDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/GraphFromGraphMLDemo.java @@ -1,34 +1,17 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseEvent; -import java.io.IOException; - -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JMenuBar; -import javax.swing.JPanel; -import javax.xml.parsers.ParserConfigurationException; - -import org.xml.sax.SAXException; - import com.google.common.base.Function; import com.google.common.base.Supplier; import com.google.common.graph.MutableNetwork; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.layout.FRLayout; import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.io.GraphMLReader; @@ -43,135 +26,157 @@ import edu.uci.ics.jung.visualization.renderers.BasicVertexLabelRenderer.InsidePositioner; import edu.uci.ics.jung.visualization.renderers.GradientVertexRenderer; import edu.uci.ics.jung.visualization.renderers.Renderer; - +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.io.IOException; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JMenuBar; +import javax.swing.JPanel; +import javax.xml.parsers.ParserConfigurationException; +import org.xml.sax.SAXException; /** * Demonstrates loading (and visualizing) a graph from a GraphML file. - * + * * @author Tom Nelson - * */ public class GraphFromGraphMLDemo { - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - /** - * Creates an instance showing a simple graph with controls to demonstrate the zoom features. - * @param filename the file containing the graph data we're reading - * @throws ParserConfigurationException if a SAX parser cannot be constructed - * @throws SAXException if the SAX parser factory cannot be constructed - * @throws IOException if the file cannot be read - */ - public GraphFromGraphMLDemo(String filename) throws ParserConfigurationException, SAXException, IOException { - - Supplier vertexFactory = new Supplier() { - int n = 0; - public Number get() { return n++; } - }; - Supplier edgeFactory = new Supplier() { - int n = 0; - public Number get() { return n++; } - }; - - GraphMLReader, Number, Number> gmlr = - new GraphMLReader, Number, Number>(vertexFactory, edgeFactory); - final MutableNetwork graph = NetworkBuilder.directed().allowsSelfLoops(true).build(); - gmlr.load(filename, graph); - - // create a simple graph for the demo - Layout layout = new FRLayout(graph.asGraph()); - vv = new VisualizationViewer(graph, layout); - - vv.addGraphMouseListener(new TestGraphMouseListener()); - vv.getRenderer().setVertexRenderer( - new GradientVertexRenderer(vv, - Color.white, Color.red, - Color.white, Color.blue, - false)); - - // add my listeners for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - vv.setEdgeToolTipTransformer(new Function() { - public String apply(Number edge) { - return "E"+graph.incidentNodes(edge).toString(); - }}); - - vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - vv.getRenderer().getVertexLabelRenderer().setPositioner(new InsidePositioner()); - vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.AUTO); - - // create a frome to hold the graph - final JFrame frame = new JFrame(); - Container content = frame.getContentPane(); - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - content.add(panel); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - final AbstractModalGraphMouse graphMouse = new DefaultModalGraphMouse(); - vv.setGraphMouse(graphMouse); - vv.addKeyListener(graphMouse.getModeKeyListener()); - - JMenuBar menubar = new JMenuBar(); - menubar.add(graphMouse.getModeMenu()); - panel.setCorner(menubar); - - - vv.addKeyListener(graphMouse.getModeKeyListener()); - vv.setToolTipText("
    Type 'p' for Pick mode

    Type 't' for Transform mode"); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + /** + * Creates an instance showing a simple graph with controls to demonstrate the zoom features. + * + * @param filename the file containing the graph data we're reading + * @throws ParserConfigurationException if a SAX parser cannot be constructed + * @throws SAXException if the SAX parser factory cannot be constructed + * @throws IOException if the file cannot be read + */ + public GraphFromGraphMLDemo(String filename) + throws ParserConfigurationException, SAXException, IOException { + + Supplier vertexFactory = + new Supplier() { + int n = 0; + + public Number get() { + return n++; + } + }; + Supplier edgeFactory = + new Supplier() { + int n = 0; + + public Number get() { + return n++; + } + }; + + GraphMLReader, Number, Number> gmlr = + new GraphMLReader, Number, Number>( + vertexFactory, edgeFactory); + final MutableNetwork graph = + NetworkBuilder.directed().allowsSelfLoops(true).build(); + gmlr.load(filename, graph); + + // create a simple graph for the demo + Layout layout = new FRLayout(graph.asGraph()); + vv = new VisualizationViewer(graph, layout); + + vv.addGraphMouseListener(new TestGraphMouseListener()); + vv.getRenderer() + .setVertexRenderer( + new GradientVertexRenderer( + vv, Color.white, Color.red, Color.white, Color.blue, false)); + + // add my listeners for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + vv.setEdgeToolTipTransformer( + new Function() { + public String apply(Number edge) { + return "E" + graph.incidentNodes(edge).toString(); + } + }); + + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + vv.getRenderer().getVertexLabelRenderer().setPositioner(new InsidePositioner()); + vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.AUTO); + + // create a frome to hold the graph + final JFrame frame = new JFrame(); + Container content = frame.getContentPane(); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + content.add(panel); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + final AbstractModalGraphMouse graphMouse = new DefaultModalGraphMouse(); + vv.setGraphMouse(graphMouse); + vv.addKeyListener(graphMouse.getModeKeyListener()); + + JMenuBar menubar = new JMenuBar(); + menubar.add(graphMouse.getModeMenu()); + panel.setCorner(menubar); + + vv.addKeyListener(graphMouse.getModeKeyListener()); + vv.setToolTipText("

    Type 'p' for Pick mode

    Type 't' for Transform mode"); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - JPanel controls = new JPanel(); - controls.add(plus); - controls.add(minus); - content.add(controls, BorderLayout.SOUTH); + JPanel controls = new JPanel(); + controls.add(plus); + controls.add(minus); + content.add(controls, BorderLayout.SOUTH); + + frame.pack(); + frame.setVisible(true); + } - frame.pack(); - frame.setVisible(true); + /** A nested class to demo the GraphMouseListener finding the right vertices after zoom/pan */ + static class TestGraphMouseListener implements GraphMouseListener { + + public void graphClicked(V v, MouseEvent me) { + System.err.println("Vertex " + v + " was clicked at (" + me.getX() + "," + me.getY() + ")"); } - - /** - * A nested class to demo the GraphMouseListener finding the - * right vertices after zoom/pan - */ - static class TestGraphMouseListener implements GraphMouseListener { - - public void graphClicked(V v, MouseEvent me) { - System.err.println("Vertex "+v+" was clicked at ("+me.getX()+","+me.getY()+")"); - } - public void graphPressed(V v, MouseEvent me) { - System.err.println("Vertex "+v+" was pressed at ("+me.getX()+","+me.getY()+")"); - } - public void graphReleased(V v, MouseEvent me) { - System.err.println("Vertex "+v+" was released at ("+me.getX()+","+me.getY()+")"); - } + + public void graphPressed(V v, MouseEvent me) { + System.err.println("Vertex " + v + " was pressed at (" + me.getX() + "," + me.getY() + ")"); } - /** - * @param args if this contains at least one element, the first will be used as the file to read - * @throws ParserConfigurationException if a SAX parser cannot be constructed - * @throws SAXException if the SAX parser factory cannot be constructed - * @throws IOException if the file cannot be read - */ - public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException - { - String filename = "simple.graphml"; - if(args.length > 0) filename = args[0]; - new GraphFromGraphMLDemo(filename); + public void graphReleased(V v, MouseEvent me) { + System.err.println("Vertex " + v + " was released at (" + me.getX() + "," + me.getY() + ")"); } + } + + /** + * @param args if this contains at least one element, the first will be used as the file to read + * @throws ParserConfigurationException if a SAX parser cannot be constructed + * @throws SAXException if the SAX parser factory cannot be constructed + * @throws IOException if the file cannot be read + */ + public static void main(String[] args) + throws ParserConfigurationException, SAXException, IOException { + String filename = "simple.graphml"; + if (args.length > 0) filename = args[0]; + new GraphFromGraphMLDemo(filename); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/GraphZoomScrollPaneDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/GraphZoomScrollPaneDemo.java index 7db0d10a..a873d26b 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/GraphZoomScrollPaneDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/GraphZoomScrollPaneDemo.java @@ -1,36 +1,18 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.Paint; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseEvent; - -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JPanel; - import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.graph.MutableNetwork; import com.google.common.graph.Network; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.layout.KKLayout; import edu.uci.ics.jung.visualization.GraphZoomScrollPane; import edu.uci.ics.jung.visualization.Layer; @@ -44,203 +26,221 @@ import edu.uci.ics.jung.visualization.renderers.BasicVertexLabelRenderer.InsidePositioner; import edu.uci.ics.jung.visualization.renderers.GradientVertexRenderer; import edu.uci.ics.jung.visualization.renderers.Renderer; - +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Paint; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; /** - * Demonstrates the use of GraphZoomScrollPane. - * This class shows the VisualizationViewer zooming - * and panning capabilities, using horizontal and - * vertical scrollbars. + * Demonstrates the use of GraphZoomScrollPane. This class shows the + * VisualizationViewer zooming and panning capabilities, using horizontal and vertical + * scrollbars. + * + *

    This demo also shows ToolTips on graph vertices and edges, and a key listener to change graph + * mouse modes. * - *

    This demo also shows ToolTips on graph vertices and edges, - * and a key listener to change graph mouse modes. - * * @author Tom Nelson - * */ public class GraphZoomScrollPaneDemo { - /** - * the graph - */ - Network graph; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - /** - * create an instance of a simple graph with controls to - * demo the zoom features. - * - */ - public GraphZoomScrollPaneDemo() { - - // create a simple graph for the demo - graph = createGraph(); - - ImageIcon sandstoneIcon = null; - String imageLocation = "/images/Sandstone.jpg"; - try { - sandstoneIcon = - new ImageIcon(getClass().getResource(imageLocation)); - } catch(Exception ex) { - System.err.println("Can't load \""+imageLocation+"\""); - } - final ImageIcon icon = sandstoneIcon; - vv = new VisualizationViewer(graph, new KKLayout(graph.asGraph())); - - if(icon != null) { - vv.addPreRenderPaintable(new VisualizationViewer.Paintable(){ - public void paint(Graphics g) { - Dimension d = vv.getSize(); - g.drawImage(icon.getImage(),0,0,d.width,d.height,vv); - } - public boolean useTransform() { return false; } - }); - } - vv.addPostRenderPaintable(new VisualizationViewer.Paintable(){ - int x; - int y; - Font font; - FontMetrics metrics; - int swidth; - int sheight; - String str = "GraphZoomScrollPane Demo"; - + /** the graph */ + Network graph; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + /** create an instance of a simple graph with controls to demo the zoom features. */ + public GraphZoomScrollPaneDemo() { + + // create a simple graph for the demo + graph = createGraph(); + + ImageIcon sandstoneIcon = null; + String imageLocation = "/images/Sandstone.jpg"; + try { + sandstoneIcon = new ImageIcon(getClass().getResource(imageLocation)); + } catch (Exception ex) { + System.err.println("Can't load \"" + imageLocation + "\""); + } + final ImageIcon icon = sandstoneIcon; + vv = new VisualizationViewer(graph, new KKLayout(graph.asGraph())); + + if (icon != null) { + vv.addPreRenderPaintable( + new VisualizationViewer.Paintable() { public void paint(Graphics g) { - Dimension d = vv.getSize(); - if(font == null) { - font = new Font(g.getFont().getName(), Font.BOLD, 30); - metrics = g.getFontMetrics(font); - swidth = metrics.stringWidth(str); - sheight = metrics.getMaxAscent()+metrics.getMaxDescent(); - x = (d.width-swidth)/2; - y = (int)(d.height-sheight*1.5); - } - g.setFont(font); - Color oldColor = g.getColor(); - g.setColor(Color.lightGray); - g.drawString(str, x, y); - g.setColor(oldColor); - } - public boolean useTransform() { - return false; + Dimension d = vv.getSize(); + g.drawImage(icon.getImage(), 0, 0, d.width, d.height, vv); } - }); - vv.addGraphMouseListener(new TestGraphMouseListener()); - vv.getRenderer().setVertexRenderer( - new GradientVertexRenderer(vv, - Color.white, Color.red, - Color.white, Color.blue, - false)); - vv.getRenderContext().setEdgeDrawPaintTransformer(Functions.constant(Color.lightGray)); - vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); - vv.getRenderContext().setArrowDrawPaintTransformer(Functions.constant(Color.lightGray)); - - // add my listeners for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - vv.setEdgeToolTipTransformer(new Function() { - public String apply(Number edge) { - return "E"+graph.incidentNodes(edge).toString(); - }}); - - vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - vv.getRenderer().getVertexLabelRenderer().setPositioner(new InsidePositioner()); - vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.AUTO); - vv.setForeground(Color.lightGray); - - // create a frome to hold the graph - final JFrame frame = new JFrame(); - Container content = frame.getContentPane(); - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - content.add(panel); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - final AbstractModalGraphMouse graphMouse = new DefaultModalGraphMouse(); - vv.setGraphMouse(graphMouse); - - vv.addKeyListener(graphMouse.getModeKeyListener()); - vv.setToolTipText("

    Type 'p' for Pick mode

    Type 't' for Transform mode"); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); + public boolean useTransform() { + return false; } - }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); + }); + } + vv.addPostRenderPaintable( + new VisualizationViewer.Paintable() { + int x; + int y; + Font font; + FontMetrics metrics; + int swidth; + int sheight; + String str = "GraphZoomScrollPane Demo"; + + public void paint(Graphics g) { + Dimension d = vv.getSize(); + if (font == null) { + font = new Font(g.getFont().getName(), Font.BOLD, 30); + metrics = g.getFontMetrics(font); + swidth = metrics.stringWidth(str); + sheight = metrics.getMaxAscent() + metrics.getMaxDescent(); + x = (d.width - swidth) / 2; + y = (int) (d.height - sheight * 1.5); } + g.setFont(font); + Color oldColor = g.getColor(); + g.setColor(Color.lightGray); + g.drawString(str, x, y); + g.setColor(oldColor); + } + + public boolean useTransform() { + return false; + } }); - JButton reset = new JButton("reset"); - reset.addActionListener(new ActionListener() { + vv.addGraphMouseListener(new TestGraphMouseListener()); + vv.getRenderer() + .setVertexRenderer( + new GradientVertexRenderer( + vv, Color.white, Color.red, Color.white, Color.blue, false)); + vv.getRenderContext().setEdgeDrawPaintTransformer(Functions.constant(Color.lightGray)); + vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); + vv.getRenderContext().setArrowDrawPaintTransformer(Functions.constant(Color.lightGray)); + + // add my listeners for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + vv.setEdgeToolTipTransformer( + new Function() { + public String apply(Number edge) { + return "E" + graph.incidentNodes(edge).toString(); + } + }); - public void actionPerformed(ActionEvent e) { - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).setToIdentity(); - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).setToIdentity(); - }}); + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + vv.getRenderer().getVertexLabelRenderer().setPositioner(new InsidePositioner()); + vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.AUTO); + vv.setForeground(Color.lightGray); + + // create a frome to hold the graph + final JFrame frame = new JFrame(); + Container content = frame.getContentPane(); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + content.add(panel); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + final AbstractModalGraphMouse graphMouse = new DefaultModalGraphMouse(); + vv.setGraphMouse(graphMouse); + + vv.addKeyListener(graphMouse.getModeKeyListener()); + vv.setToolTipText("

    Type 'p' for Pick mode

    Type 't' for Transform mode"); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } + }); + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } + }); - JPanel controls = new JPanel(); - controls.add(plus); - controls.add(minus); - controls.add(reset); - content.add(controls, BorderLayout.SOUTH); + JButton reset = new JButton("reset"); + reset.addActionListener( + new ActionListener() { + + public void actionPerformed(ActionEvent e) { + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .setToIdentity(); + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.VIEW) + .setToIdentity(); + } + }); - frame.pack(); - frame.setVisible(true); - } - - Network createGraph() { - MutableNetwork graph = NetworkBuilder.directed().build(); - graph.addEdge(0, 1, new Double(Math.random())); - graph.addEdge(3, 0, new Double(Math.random())); - graph.addEdge(0, 4, new Double(Math.random())); - graph.addEdge(4, 5, new Double(Math.random())); - graph.addEdge(5, 3, new Double(Math.random())); - graph.addEdge(2, 1, new Double(Math.random())); - graph.addEdge(4, 1, new Double(Math.random())); - graph.addEdge(8, 2, new Double(Math.random())); - graph.addEdge(3, 8, new Double(Math.random())); - graph.addEdge(6, 7, new Double(Math.random())); - graph.addEdge(7, 5, new Double(Math.random())); - graph.addEdge(0, 9, new Double(Math.random())); - graph.addEdge(9, 8, new Double(Math.random())); - graph.addEdge(7, 6, new Double(Math.random())); - graph.addEdge(6, 5, new Double(Math.random())); - graph.addEdge(4, 2, new Double(Math.random())); - graph.addEdge(5, 4, new Double(Math.random())); - graph.addEdge(4, 10, new Double(Math.random())); - graph.addEdge(10, 4, new Double(Math.random())); - - return graph; + JPanel controls = new JPanel(); + controls.add(plus); + controls.add(minus); + controls.add(reset); + content.add(controls, BorderLayout.SOUTH); + + frame.pack(); + frame.setVisible(true); + } + + Network createGraph() { + MutableNetwork graph = NetworkBuilder.directed().build(); + graph.addEdge(0, 1, new Double(Math.random())); + graph.addEdge(3, 0, new Double(Math.random())); + graph.addEdge(0, 4, new Double(Math.random())); + graph.addEdge(4, 5, new Double(Math.random())); + graph.addEdge(5, 3, new Double(Math.random())); + graph.addEdge(2, 1, new Double(Math.random())); + graph.addEdge(4, 1, new Double(Math.random())); + graph.addEdge(8, 2, new Double(Math.random())); + graph.addEdge(3, 8, new Double(Math.random())); + graph.addEdge(6, 7, new Double(Math.random())); + graph.addEdge(7, 5, new Double(Math.random())); + graph.addEdge(0, 9, new Double(Math.random())); + graph.addEdge(9, 8, new Double(Math.random())); + graph.addEdge(7, 6, new Double(Math.random())); + graph.addEdge(6, 5, new Double(Math.random())); + graph.addEdge(4, 2, new Double(Math.random())); + graph.addEdge(5, 4, new Double(Math.random())); + graph.addEdge(4, 10, new Double(Math.random())); + graph.addEdge(10, 4, new Double(Math.random())); + + return graph; + } + + /** A nested class to demo the GraphMouseListener finding the right vertices after zoom/pan */ + static class TestGraphMouseListener implements GraphMouseListener { + + public void graphClicked(V v, MouseEvent me) { + System.err.println("Vertex " + v + " was clicked at (" + me.getX() + "," + me.getY() + ")"); } - /** - * A nested class to demo the GraphMouseListener finding the - * right vertices after zoom/pan - */ - static class TestGraphMouseListener implements GraphMouseListener { - - public void graphClicked(V v, MouseEvent me) { - System.err.println("Vertex "+v+" was clicked at ("+me.getX()+","+me.getY()+")"); - } - public void graphPressed(V v, MouseEvent me) { - System.err.println("Vertex "+v+" was pressed at ("+me.getX()+","+me.getY()+")"); - } - public void graphReleased(V v, MouseEvent me) { - System.err.println("Vertex "+v+" was released at ("+me.getX()+","+me.getY()+")"); - } + public void graphPressed(V v, MouseEvent me) { + System.err.println("Vertex " + v + " was pressed at (" + me.getX() + "," + me.getY() + ")"); } - public static void main(String[] args) - { - new GraphZoomScrollPaneDemo(); + public void graphReleased(V v, MouseEvent me) { + System.err.println("Vertex " + v + " was released at (" + me.getX() + "," + me.getY() + ")"); } + } + + public static void main(String[] args) { + new GraphZoomScrollPaneDemo(); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/ImageEdgeLabelDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/ImageEdgeLabelDemo.java index 2663fea4..7c7e1869 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/ImageEdgeLabelDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/ImageEdgeLabelDemo.java @@ -1,34 +1,17 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.net.URL; - -import javax.swing.BorderFactory; -import javax.swing.JApplet; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JFrame; -import javax.swing.JPanel; - import com.google.common.base.Function; import com.google.common.graph.MutableNetwork; import com.google.common.graph.Network; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.layout.FRLayout; import edu.uci.ics.jung.visualization.GraphZoomScrollPane; import edu.uci.ics.jung.visualization.VisualizationViewer; @@ -40,134 +23,152 @@ import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; import edu.uci.ics.jung.visualization.renderers.DefaultEdgeLabelRenderer; import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.net.URL; +import javax.swing.BorderFactory; +import javax.swing.JApplet; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JPanel; /** * Demonstrates the use of images on graph edge labels. - * + * * @author Tom Nelson - * */ public class ImageEdgeLabelDemo extends JApplet { - /** - * - */ - private static final long serialVersionUID = -4332663871914930864L; - - private static final int VERTEX_COUNT=11; - - /** - * the graph - */ - Network graph; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - public ImageEdgeLabelDemo() { - - // create a simple graph for the demo - graph = createGraph(VERTEX_COUNT); - - FRLayout layout = new FRLayout(graph.asGraph()); - layout.setMaxIterations(100); - vv = new VisualizationViewer(graph, layout, new Dimension(400,400)); - - vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(vv.getPickedEdgeState(), Color.black, Color.cyan)); - - vv.setBackground(Color.white); - - vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan)); - vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan)); - vv.getRenderContext().setEdgeLabelTransformer(new Function() { - URL url = getClass().getResource("/images/lightning-s.gif"); - public String apply(Number input) { - return ""; - }}); - - // add a listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - vv.setEdgeToolTipTransformer(new ToStringLabeller()); - Container content = getContentPane(); - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - content.add(panel); - - final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse(); - vv.setGraphMouse(graphMouse); - vv.addKeyListener(graphMouse.getModeKeyListener()); - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + /** */ + private static final long serialVersionUID = -4332663871914930864L; + + private static final int VERTEX_COUNT = 11; + + /** the graph */ + Network graph; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + public ImageEdgeLabelDemo() { + + // create a simple graph for the demo + graph = createGraph(VERTEX_COUNT); + + FRLayout layout = new FRLayout(graph.asGraph()); + layout.setMaxIterations(100); + vv = new VisualizationViewer(graph, layout, new Dimension(400, 400)); + + vv.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv.getPickedEdgeState(), Color.black, Color.cyan)); + + vv.setBackground(Color.white); + + vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan)); + vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan)); + vv.getRenderContext() + .setEdgeLabelTransformer( + new Function() { + URL url = getClass().getResource("/images/lightning-s.gif"); + + public String apply(Number input) { + return ""; + } + }); + + // add a listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + vv.setEdgeToolTipTransformer(new ToStringLabeller()); + Container content = getContentPane(); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + content.add(panel); + + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + vv.setGraphMouse(graphMouse); + vv.addKeyListener(graphMouse.getModeKeyListener()); + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - JComboBox modeBox = graphMouse.getModeComboBox(); - JPanel modePanel = new JPanel(); - modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); - modePanel.add(modeBox); - - JPanel scaleGrid = new JPanel(new GridLayout(1,0)); - scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); - JPanel controls = new JPanel(); - scaleGrid.add(plus); - scaleGrid.add(minus); - controls.add(scaleGrid); - controls.add(modePanel); - content.add(controls, BorderLayout.SOUTH); - } - - /** - * create some vertices - * @param count how many to create - * @return the Vertices in an array - */ - private Network createGraph(int vertexCount) { - MutableNetwork graph = NetworkBuilder.directed().build(); - for (int i = 0; i < vertexCount; i++) { - graph.addNode(i); - } - int j=0; - graph.addEdge(0, 1, j++); - graph.addEdge(3, 0, j++); - graph.addEdge(0, 4, j++); - graph.addEdge(4, 5, j++); - graph.addEdge(5, 3, j++); - graph.addEdge(2, 1, j++); - graph.addEdge(4, 1, j++); - graph.addEdge(8, 2, j++); - graph.addEdge(3, 8, j++); - graph.addEdge(6, 7, j++); - graph.addEdge(7, 5, j++); - graph.addEdge(0, 9, j++); - graph.addEdge(9, 8, j++); - graph.addEdge(7, 6, j++); - graph.addEdge(6, 5, j++); - graph.addEdge(4, 2, j++); - graph.addEdge(5, 4, j++); - graph.addEdge(4, 10, j++); - graph.addEdge(10, 4, j++); - - return graph; - } + JComboBox modeBox = graphMouse.getModeComboBox(); + JPanel modePanel = new JPanel(); + modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); + modePanel.add(modeBox); - public static void main(String[] args) { - JFrame frame = new JFrame(); - Container content = frame.getContentPane(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + JPanel scaleGrid = new JPanel(new GridLayout(1, 0)); + scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); + JPanel controls = new JPanel(); + scaleGrid.add(plus); + scaleGrid.add(minus); + controls.add(scaleGrid); + controls.add(modePanel); + content.add(controls, BorderLayout.SOUTH); + } - content.add(new ImageEdgeLabelDemo()); - frame.pack(); - frame.setVisible(true); + /** + * create some vertices + * + * @param count how many to create + * @return the Vertices in an array + */ + private Network createGraph(int vertexCount) { + MutableNetwork graph = NetworkBuilder.directed().build(); + for (int i = 0; i < vertexCount; i++) { + graph.addNode(i); } + int j = 0; + graph.addEdge(0, 1, j++); + graph.addEdge(3, 0, j++); + graph.addEdge(0, 4, j++); + graph.addEdge(4, 5, j++); + graph.addEdge(5, 3, j++); + graph.addEdge(2, 1, j++); + graph.addEdge(4, 1, j++); + graph.addEdge(8, 2, j++); + graph.addEdge(3, 8, j++); + graph.addEdge(6, 7, j++); + graph.addEdge(7, 5, j++); + graph.addEdge(0, 9, j++); + graph.addEdge(9, 8, j++); + graph.addEdge(7, 6, j++); + graph.addEdge(6, 5, j++); + graph.addEdge(4, 2, j++); + graph.addEdge(5, 4, j++); + graph.addEdge(4, 10, j++); + graph.addEdge(10, 4, j++); + + return graph; + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + Container content = frame.getContentPane(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + content.add(new ImageEdgeLabelDemo()); + frame.pack(); + frame.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/InternalFrameSatelliteViewDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/InternalFrameSatelliteViewDemo.java index 649f7048..bb42f300 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/InternalFrameSatelliteViewDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/InternalFrameSatelliteViewDemo.java @@ -1,31 +1,14 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JDesktopPane; -import javax.swing.JFrame; -import javax.swing.JInternalFrame; -import javax.swing.JOptionPane; -import javax.swing.JPanel; - import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.ISOMLayout; import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.graph.util.TestGraphs; @@ -39,168 +22,191 @@ import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.JOptionPane; +import javax.swing.JPanel; /** * Similar to the SatelliteViewDemo, but using JInternalFrame. - * + * * @author Tom Nelson - * */ public class InternalFrameSatelliteViewDemo { - static final String instructions = - ""+ - "

    Instructions for Mouse Listeners

    "+ - "

    There are two modes, Transforming and Picking."+ - "

    The modes are selected with a toggle button."+ - - "

    Transforming Mode:"+ - "

      "+ - "
    • Mouse1+drag pans the graph"+ - "
    • Mouse1+Shift+drag rotates the graph"+ - "
    • Mouse1+CTRL(or Command)+drag shears the graph"+ - "
    "+ - - "Picking Mode:"+ - "
      "+ - "
    • Mouse1 on a Vertex selects the vertex"+ - "
    • Mouse1 elsewhere unselects all Vertices"+ - "
    • Mouse1+Shift on a Vertex adds/removes Vertex selection"+ - "
    • Mouse1+drag on a Vertex moves all selected Vertices"+ - "
    • Mouse1+drag elsewhere selects Vertices in a region"+ - "
    • Mouse1+Shift+drag adds selection of Vertices in a new region"+ - "
    • Mouse1+CTRL on a Vertex selects the vertex and centers the display on it"+ - "
    "+ - "Both Modes:"+ - "
      "+ - "
    • Mousewheel scales the layout > 1 and scales the view < 1"; - - /** - * the graph - */ - Network graph; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - VisualizationViewer satellite; - - JInternalFrame dialog; - - JDesktopPane desktop; - - /** - * create an instance of a simple graph with controls to - * demo the zoom features. - * - */ - public InternalFrameSatelliteViewDemo() { - - // create a simple graph for the demo - graph = TestGraphs.getOneComponentGraph(); - - Layout layout = new ISOMLayout(graph); - - vv = new VisualizationViewer(graph, layout, new Dimension(600,600)); - vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(vv.getPickedEdgeState(), Color.black, Color.cyan)); - vv.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(vv.getPickedVertexState(), Color.red, Color.yellow)); - - // add my listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - final DefaultModalGraphMouse graphMouse - = new DefaultModalGraphMouse(); - vv.setGraphMouse(graphMouse); - - satellite = - new SatelliteVisualizationViewer(vv, new Dimension(200,200)); - satellite.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(satellite.getPickedEdgeState(), Color.black, Color.cyan)); - satellite.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(satellite.getPickedVertexState(), Color.red, Color.yellow)); - - ScalingControl satelliteScaler = new CrossoverScalingControl(); - satellite.scaleToLayout(satelliteScaler); - - JFrame frame = new JFrame(); - desktop = new JDesktopPane(); - Container content = frame.getContentPane(); - JPanel panel = new JPanel(new BorderLayout()); - panel.add(desktop); - content.add(panel); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JInternalFrame vvFrame = new JInternalFrame(); - vvFrame.getContentPane().add(vv); - vvFrame.pack(); - vvFrame.setVisible(true); //necessary as of 1.3 - desktop.add(vvFrame); - try { - vvFrame.setSelected(true); - } catch (java.beans.PropertyVetoException e) {} - - dialog = new JInternalFrame(); - desktop.add(dialog); - content = dialog.getContentPane(); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + static final String instructions = + "" + + "

      Instructions for Mouse Listeners

      " + + "

      There are two modes, Transforming and Picking." + + "

      The modes are selected with a toggle button." + + "

      Transforming Mode:" + + "

        " + + "
      • Mouse1+drag pans the graph" + + "
      • Mouse1+Shift+drag rotates the graph" + + "
      • Mouse1+CTRL(or Command)+drag shears the graph" + + "
      " + + "Picking Mode:" + + "
        " + + "
      • Mouse1 on a Vertex selects the vertex" + + "
      • Mouse1 elsewhere unselects all Vertices" + + "
      • Mouse1+Shift on a Vertex adds/removes Vertex selection" + + "
      • Mouse1+drag on a Vertex moves all selected Vertices" + + "
      • Mouse1+drag elsewhere selects Vertices in a region" + + "
      • Mouse1+Shift+drag adds selection of Vertices in a new region" + + "
      • Mouse1+CTRL on a Vertex selects the vertex and centers the display on it" + + "
      " + + "Both Modes:" + + "
        " + + "
      • Mousewheel scales the layout > 1 and scales the view < 1"; + + /** the graph */ + Network graph; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + VisualizationViewer satellite; + + JInternalFrame dialog; + + JDesktopPane desktop; + + /** create an instance of a simple graph with controls to demo the zoom features. */ + public InternalFrameSatelliteViewDemo() { + + // create a simple graph for the demo + graph = TestGraphs.getOneComponentGraph(); + + Layout layout = new ISOMLayout(graph); + + vv = new VisualizationViewer(graph, layout, new Dimension(600, 600)); + vv.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv.getPickedEdgeState(), Color.black, Color.cyan)); + vv.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer( + vv.getPickedVertexState(), Color.red, Color.yellow)); + + // add my listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + vv.setGraphMouse(graphMouse); + + satellite = new SatelliteVisualizationViewer(vv, new Dimension(200, 200)); + satellite + .getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + satellite.getPickedEdgeState(), Color.black, Color.cyan)); + satellite + .getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer( + satellite.getPickedVertexState(), Color.red, Color.yellow)); + + ScalingControl satelliteScaler = new CrossoverScalingControl(); + satellite.scaleToLayout(satelliteScaler); + + JFrame frame = new JFrame(); + desktop = new JDesktopPane(); + Container content = frame.getContentPane(); + JPanel panel = new JPanel(new BorderLayout()); + panel.add(desktop); + content.add(panel); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + JInternalFrame vvFrame = new JInternalFrame(); + vvFrame.getContentPane().add(vv); + vvFrame.pack(); + vvFrame.setVisible(true); //necessary as of 1.3 + desktop.add(vvFrame); + try { + vvFrame.setSelected(true); + } catch (java.beans.PropertyVetoException e) { + } + + dialog = new JInternalFrame(); + desktop.add(dialog); + content = dialog.getContentPane(); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - JButton dismiss = new JButton("Dismiss"); - dismiss.addActionListener(new ActionListener(){ - public void actionPerformed(ActionEvent e) { - dialog.setVisible(false); - } + JButton dismiss = new JButton("Dismiss"); + dismiss.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + dialog.setVisible(false); + } }); - JButton help = new JButton("Help"); - help.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - JOptionPane.showInternalMessageDialog(dialog, instructions, - "Instructions", JOptionPane.PLAIN_MESSAGE); - } + JButton help = new JButton("Help"); + help.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + JOptionPane.showInternalMessageDialog( + dialog, instructions, "Instructions", JOptionPane.PLAIN_MESSAGE); + } }); - JPanel controls = new JPanel(new GridLayout(2,2)); - controls.add(plus); - controls.add(minus); - controls.add(dismiss); - controls.add(help); - content.add(satellite); - content.add(controls, BorderLayout.SOUTH); - - JButton zoomer = new JButton("Show Satellite View"); - zoomer.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - dialog.pack(); - dialog.setLocation(desktop.getWidth()-dialog.getWidth(),0); - dialog.show(); - try { - dialog.setSelected(true); - } catch (java.beans.PropertyVetoException ex) {} + JPanel controls = new JPanel(new GridLayout(2, 2)); + controls.add(plus); + controls.add(minus); + controls.add(dismiss); + controls.add(help); + content.add(satellite); + content.add(controls, BorderLayout.SOUTH); + + JButton zoomer = new JButton("Show Satellite View"); + zoomer.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + dialog.pack(); + dialog.setLocation(desktop.getWidth() - dialog.getWidth(), 0); + dialog.show(); + try { + dialog.setSelected(true); + } catch (java.beans.PropertyVetoException ex) { } + } }); - JComboBox modeBox = graphMouse.getModeComboBox(); - modeBox.addItemListener(((ModalGraphMouse)satellite.getGraphMouse()).getModeListener()); - JPanel p = new JPanel(); - p.add(zoomer); - p.add(modeBox); + JComboBox modeBox = graphMouse.getModeComboBox(); + modeBox.addItemListener(((ModalGraphMouse) satellite.getGraphMouse()).getModeListener()); + JPanel p = new JPanel(); + p.add(zoomer); + p.add(modeBox); - frame.getContentPane().add(p, BorderLayout.SOUTH); - frame.setSize(800, 800); - frame.setVisible(true); - } + frame.getContentPane().add(p, BorderLayout.SOUTH); + frame.setSize(800, 800); + frame.setVisible(true); + } - public static void main(String[] args) { - new InternalFrameSatelliteViewDemo(); - } + public static void main(String[] args) { + new InternalFrameSatelliteViewDemo(); + } } - diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/L2RTreeLayoutDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/L2RTreeLayoutDemo.java index 4c20df75..718fa691 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/L2RTreeLayoutDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/L2RTreeLayoutDemo.java @@ -1,13 +1,34 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; +import com.google.common.base.Functions; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.layout.PolarPoint; +import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout; +import edu.uci.ics.jung.algorithms.layout.TreeLayout; +import edu.uci.ics.jung.graph.CTreeNetwork; +import edu.uci.ics.jung.graph.MutableCTreeNetwork; +import edu.uci.ics.jung.graph.TreeNetworkBuilder; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationServer; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ModalGraphMouse.Mode; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.decorators.EdgeShape; +import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; +import edu.uci.ics.jung.visualization.layout.LayoutTransition; +import edu.uci.ics.jung.visualization.util.Animator; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; @@ -27,7 +48,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - import javax.swing.BorderFactory; import javax.swing.JApplet; import javax.swing.JButton; @@ -36,234 +56,215 @@ import javax.swing.JPanel; import javax.swing.JToggleButton; -import com.google.common.base.Functions; - -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.algorithms.layout.PolarPoint; -import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout; -import edu.uci.ics.jung.algorithms.layout.TreeLayout; -import edu.uci.ics.jung.graph.CTreeNetwork; -import edu.uci.ics.jung.graph.MutableCTreeNetwork; -import edu.uci.ics.jung.graph.TreeNetworkBuilder; -import edu.uci.ics.jung.visualization.GraphZoomScrollPane; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationServer; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; -import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ModalGraphMouse.Mode; -import edu.uci.ics.jung.visualization.control.ScalingControl; -import edu.uci.ics.jung.visualization.decorators.EdgeShape; -import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; -import edu.uci.ics.jung.visualization.layout.LayoutTransition; -import edu.uci.ics.jung.visualization.util.Animator; - /** - * A variant of TreeLayoutDemo that rotates the view by 90 degrees from the - * default orientation. + * A variant of TreeLayoutDemo that rotates the view by 90 degrees from the default orientation. + * * @author Tom Nelson - * */ @SuppressWarnings("serial") public class L2RTreeLayoutDemo extends JApplet { - /** - * the graph - */ - CTreeNetwork graph; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - VisualizationServer.Paintable rings; - - String root; - - TreeLayout treeLayout; - - RadialTreeLayout radialLayout; - - public L2RTreeLayoutDemo() { - - // create a simple graph for the demo - graph = createTree(); - - treeLayout = new TreeLayout(graph.asGraph()); - radialLayout = new RadialTreeLayout(graph.asGraph()); - radialLayout.setSize(new Dimension(600,600)); - vv = new VisualizationViewer(graph, treeLayout, new Dimension(600,600)); - vv.setBackground(Color.white); - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.quadCurve(graph)); - vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - // add a listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); - rings = new Rings(); - - setLtoR(vv); - - Container content = getContentPane(); - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - content.add(panel); - - final DefaultModalGraphMouse graphMouse - = new DefaultModalGraphMouse(); - - vv.setGraphMouse(graphMouse); - - JComboBox modeBox = graphMouse.getModeComboBox(); - modeBox.addItemListener(graphMouse.getModeListener()); - graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + /** the graph */ + CTreeNetwork graph; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + VisualizationServer.Paintable rings; + + String root; + + TreeLayout treeLayout; + + RadialTreeLayout radialLayout; + + public L2RTreeLayoutDemo() { + + // create a simple graph for the demo + graph = createTree(); + + treeLayout = new TreeLayout(graph.asGraph()); + radialLayout = new RadialTreeLayout(graph.asGraph()); + radialLayout.setSize(new Dimension(600, 600)); + vv = new VisualizationViewer(graph, treeLayout, new Dimension(600, 600)); + vv.setBackground(Color.white); + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.quadCurve(graph)); + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + // add a listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); + rings = new Rings(); + + setLtoR(vv); + + Container content = getContentPane(); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + content.add(panel); + + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + + vv.setGraphMouse(graphMouse); + + JComboBox modeBox = graphMouse.getModeComboBox(); + modeBox.addItemListener(graphMouse.getModeListener()); + graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } + }); + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); + + JToggleButton radial = new JToggleButton("Radial"); + radial.addItemListener( + new ItemListener() { + + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + + LayoutTransition lt = + new LayoutTransition(vv, treeLayout, radialLayout); + Animator animator = new Animator(lt); + animator.start(); + vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); + vv.addPreRenderPaintable(rings); + } else { + LayoutTransition lt = + new LayoutTransition(vv, radialLayout, treeLayout); + Animator animator = new Animator(lt); + animator.start(); + vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); + setLtoR(vv); + vv.removePreRenderPaintable(rings); } + + vv.repaint(); + } }); - - JToggleButton radial = new JToggleButton("Radial"); - radial.addItemListener(new ItemListener() { - - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - - LayoutTransition lt = - new LayoutTransition(vv, treeLayout, radialLayout); - Animator animator = new Animator(lt); - animator.start(); - vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); - vv.addPreRenderPaintable(rings); - } else { - LayoutTransition lt = - new LayoutTransition(vv, radialLayout, treeLayout); - Animator animator = new Animator(lt); - animator.start(); - vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); - setLtoR(vv); - vv.removePreRenderPaintable(rings); - } - - vv.repaint(); - }}); - - JPanel scaleGrid = new JPanel(new GridLayout(1,0)); - scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); - - JPanel controls = new JPanel(); - scaleGrid.add(plus); - scaleGrid.add(minus); - controls.add(radial); - controls.add(scaleGrid); - controls.add(modeBox); - - content.add(controls, BorderLayout.SOUTH); + + JPanel scaleGrid = new JPanel(new GridLayout(1, 0)); + scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); + + JPanel controls = new JPanel(); + scaleGrid.add(plus); + scaleGrid.add(minus); + controls.add(radial); + controls.add(scaleGrid); + controls.add(modeBox); + + content.add(controls, BorderLayout.SOUTH); + } + + private void setLtoR(VisualizationViewer vv) { + Layout layout = vv.getModel().getGraphLayout(); + Dimension d = layout.getSize(); + Point2D center = new Point2D.Double(d.width / 2, d.height / 2); + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .rotate(-Math.PI / 2, center); + } + + class Rings implements VisualizationServer.Paintable { + + Collection depths; + + public Rings() { + depths = getDepths(); } - - private void setLtoR(VisualizationViewer vv) { - Layout layout = vv.getModel().getGraphLayout(); - Dimension d = layout.getSize(); - Point2D center = new Point2D.Double(d.width/2, d.height/2); - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).rotate(-Math.PI/2, center); + + private Collection getDepths() { + Set depths = new HashSet(); + Map polarLocations = radialLayout.getPolarLocations(); + for (String v : graph.nodes()) { + PolarPoint pp = polarLocations.get(v); + depths.add(pp.getRadius()); + } + return depths; } - - class Rings implements VisualizationServer.Paintable { - - Collection depths; - - public Rings() { - depths = getDepths(); - } - - private Collection getDepths() { - Set depths = new HashSet(); - Map polarLocations = radialLayout.getPolarLocations(); - for(String v : graph.nodes()) { - PolarPoint pp = polarLocations.get(v); - depths.add(pp.getRadius()); - } - return depths; - } - - public void paint(Graphics g) { - g.setColor(Color.lightGray); - - Graphics2D g2d = (Graphics2D)g; - Point2D center = radialLayout.getCenter(); - - Ellipse2D ellipse = new Ellipse2D.Double(); - for(double d : depths) { - ellipse.setFrameFromDiagonal(center.getX()-d, center.getY()-d, - center.getX()+d, center.getY()+d); - Shape shape = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).transform(ellipse); - g2d.draw(shape); - } - } - - public boolean useTransform() { - return true; - } + + public void paint(Graphics g) { + g.setColor(Color.lightGray); + + Graphics2D g2d = (Graphics2D) g; + Point2D center = radialLayout.getCenter(); + + Ellipse2D ellipse = new Ellipse2D.Double(); + for (double d : depths) { + ellipse.setFrameFromDiagonal( + center.getX() - d, center.getY() - d, center.getX() + d, center.getY() + d); + Shape shape = + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .transform(ellipse); + g2d.draw(shape); + } } - - /** - * - */ - private CTreeNetwork createTree() { - MutableCTreeNetwork tree = - TreeNetworkBuilder.builder().expectedNodeCount(27).build(); - - tree.addNode("root"); - - int edgeId = 0; - tree.addEdge("root", "V0", edgeId++); - tree.addEdge("V0", "V1", edgeId++); - tree.addEdge("V0", "V2", edgeId++); - tree.addEdge("V1", "V4", edgeId++); - tree.addEdge("V2", "V3", edgeId++); - tree.addEdge("V2", "V5", edgeId++); - tree.addEdge("V4", "V6", edgeId++); - tree.addEdge("V4", "V7", edgeId++); - tree.addEdge("V3", "V8", edgeId++); - tree.addEdge("V6", "V9", edgeId++); - tree.addEdge("V4", "V10", edgeId++); - - tree.addEdge("root", "A0", edgeId++); - tree.addEdge("A0", "A1", edgeId++); - tree.addEdge("A0", "A2", edgeId++); - tree.addEdge("A0", "A3", edgeId++); - - tree.addEdge("root", "B0", edgeId++); - tree.addEdge("B0", "B1", edgeId++); - tree.addEdge("B0", "B2", edgeId++); - tree.addEdge("B1", "B4", edgeId++); - tree.addEdge("B2", "B3", edgeId++); - tree.addEdge("B2", "B5", edgeId++); - tree.addEdge("B4", "B6", edgeId++); - tree.addEdge("B4", "B7", edgeId++); - tree.addEdge("B3", "B8", edgeId++); - tree.addEdge("B6", "B9", edgeId++); - - return tree; + + public boolean useTransform() { + return true; } + } - public static void main(String[] args) { - JFrame frame = new JFrame(); - Container content = frame.getContentPane(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + /** */ + private CTreeNetwork createTree() { + MutableCTreeNetwork tree = + TreeNetworkBuilder.builder().expectedNodeCount(27).build(); - content.add(new L2RTreeLayoutDemo()); - frame.pack(); - frame.setVisible(true); - } + tree.addNode("root"); + + int edgeId = 0; + tree.addEdge("root", "V0", edgeId++); + tree.addEdge("V0", "V1", edgeId++); + tree.addEdge("V0", "V2", edgeId++); + tree.addEdge("V1", "V4", edgeId++); + tree.addEdge("V2", "V3", edgeId++); + tree.addEdge("V2", "V5", edgeId++); + tree.addEdge("V4", "V6", edgeId++); + tree.addEdge("V4", "V7", edgeId++); + tree.addEdge("V3", "V8", edgeId++); + tree.addEdge("V6", "V9", edgeId++); + tree.addEdge("V4", "V10", edgeId++); + + tree.addEdge("root", "A0", edgeId++); + tree.addEdge("A0", "A1", edgeId++); + tree.addEdge("A0", "A2", edgeId++); + tree.addEdge("A0", "A3", edgeId++); + + tree.addEdge("root", "B0", edgeId++); + tree.addEdge("B0", "B1", edgeId++); + tree.addEdge("B0", "B2", edgeId++); + tree.addEdge("B1", "B4", edgeId++); + tree.addEdge("B2", "B3", edgeId++); + tree.addEdge("B2", "B5", edgeId++); + tree.addEdge("B4", "B6", edgeId++); + tree.addEdge("B4", "B7", edgeId++); + tree.addEdge("B3", "B8", edgeId++); + tree.addEdge("B6", "B9", edgeId++); + + return tree; + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + Container content = frame.getContentPane(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + content.add(new L2RTreeLayoutDemo()); + frame.pack(); + frame.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/LensDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/LensDemo.java index 932b3572..b92ac8cf 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/LensDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/LensDemo.java @@ -1,13 +1,43 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.Network; +import com.google.common.graph.NetworkBuilder; +import edu.uci.ics.jung.algorithms.layout.FRLayout; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.layout.StaticLayout; +import edu.uci.ics.jung.graph.util.TestGraphs; +import edu.uci.ics.jung.visualization.DefaultVisualizationModel; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationModel; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; +import edu.uci.ics.jung.visualization.control.LensMagnificationGraphMousePlugin; +import edu.uci.ics.jung.visualization.control.ModalLensGraphMouse; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; +import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; +import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; +import edu.uci.ics.jung.visualization.picking.PickedState; +import edu.uci.ics.jung.visualization.transform.HyperbolicTransformer; +import edu.uci.ics.jung.visualization.transform.LayoutLensSupport; +import edu.uci.ics.jung.visualization.transform.LensSupport; +import edu.uci.ics.jung.visualization.transform.MagnifyTransformer; +import edu.uci.ics.jung.visualization.transform.shape.HyperbolicShapeTransformer; +import edu.uci.ics.jung.visualization.transform.shape.MagnifyShapeTransformer; +import edu.uci.ics.jung.visualization.transform.shape.ViewLensSupport; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; @@ -28,7 +58,6 @@ import java.awt.geom.Rectangle2D; import java.util.HashMap; import java.util.Map; - import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ButtonGroup; @@ -43,408 +72,367 @@ import javax.swing.JRadioButton; import javax.swing.plaf.basic.BasicLabelUI; -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.Network; -import com.google.common.graph.NetworkBuilder; - -import edu.uci.ics.jung.algorithms.layout.FRLayout; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.algorithms.layout.StaticLayout; -import edu.uci.ics.jung.graph.util.TestGraphs; -import edu.uci.ics.jung.visualization.DefaultVisualizationModel; -import edu.uci.ics.jung.visualization.GraphZoomScrollPane; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationModel; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; -import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; -import edu.uci.ics.jung.visualization.control.LensMagnificationGraphMousePlugin; -import edu.uci.ics.jung.visualization.control.ModalLensGraphMouse; -import edu.uci.ics.jung.visualization.control.ScalingControl; -import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; -import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; -import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; -import edu.uci.ics.jung.visualization.picking.PickedState; -import edu.uci.ics.jung.visualization.transform.HyperbolicTransformer; -import edu.uci.ics.jung.visualization.transform.LayoutLensSupport; -import edu.uci.ics.jung.visualization.transform.LensSupport; -import edu.uci.ics.jung.visualization.transform.MagnifyTransformer; -import edu.uci.ics.jung.visualization.transform.shape.HyperbolicShapeTransformer; -import edu.uci.ics.jung.visualization.transform.shape.MagnifyShapeTransformer; -import edu.uci.ics.jung.visualization.transform.shape.ViewLensSupport; - /** - * Demonstrates the use of HyperbolicTransform - * and MagnifyTransform - * applied to either the model (graph layout) or the view - * (VisualizationViewer) - * The hyperbolic transform is applied in an elliptical lens - * that affects that part of the visualization. - * + * Demonstrates the use of HyperbolicTransform and MagnifyTransform + * applied to either the model (graph layout) or the view (VisualizationViewer) The hyperbolic + * transform is applied in an elliptical lens that affects that part of the visualization. + * * @author Tom Nelson - * */ @SuppressWarnings("serial") public class LensDemo extends JApplet { - /** - * the graph - */ - Network graph; - - FRLayout graphLayout; - - /** - * a grid shaped graph - */ - Network grid; - - Layout gridLayout; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - /** - * provides a Hyperbolic lens for the view - */ - LensSupport hyperbolicViewSupport; - /** - * provides a magnification lens for the view - */ - LensSupport magnifyViewSupport; - - /** - * provides a Hyperbolic lens for the model - */ - LensSupport hyperbolicLayoutSupport; - /** - * provides a magnification lens for the model - */ - LensSupport magnifyLayoutSupport; - - ScalingControl scaler; - - /** - * create an instance of a simple graph with controls to - * demo the zoomand hyperbolic features. - * - */ - public LensDemo() { - - // create a simple graph for the demo - graph = TestGraphs.getOneComponentGraph(); - - graphLayout = new FRLayout(graph.asGraph()); - graphLayout.setMaxIterations(1000); - - Dimension preferredSize = new Dimension(600,600); - Map map = new HashMap(); - Function vlf = - Functions.forMap(map); - grid = this.generateVertexGrid(map, preferredSize, 25); - gridLayout = new StaticLayout(grid.asGraph(), vlf, preferredSize); - - final VisualizationModel visualizationModel = - new DefaultVisualizationModel(graph, graphLayout, preferredSize); - vv = new VisualizationViewer(visualizationModel, preferredSize); - - PickedState ps = vv.getPickedVertexState(); - PickedState pes = vv.getPickedEdgeState(); - vv.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(ps, Color.red, Color.yellow)); - vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(pes, Color.black, Color.cyan)); - vv.setBackground(Color.white); - - vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - - final Function ovals = vv.getRenderContext().getVertexShapeTransformer(); - final Function squares = - Functions.constant(new Rectangle2D.Float(-10,-10,20,20)); - - // add a listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - - Container content = getContentPane(); - GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv); - content.add(gzsp); - - /** - * the regular graph mouse for the normal view - */ - final DefaultModalGraphMouse graphMouse - = new DefaultModalGraphMouse(); - - vv.setGraphMouse(graphMouse); - vv.addKeyListener(graphMouse.getModeKeyListener()); - - hyperbolicViewSupport = - new ViewLensSupport(vv, new HyperbolicShapeTransformer(vv, - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), - new ModalLensGraphMouse()); - hyperbolicLayoutSupport = - new LayoutLensSupport(vv, new HyperbolicTransformer(vv, - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT)), - new ModalLensGraphMouse()); - magnifyViewSupport = - new ViewLensSupport(vv, new MagnifyShapeTransformer(vv, - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), - new ModalLensGraphMouse(new LensMagnificationGraphMousePlugin(1.f, 6.f, .2f))); - magnifyLayoutSupport = - new LayoutLensSupport(vv, new MagnifyTransformer(vv, - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT)), - new ModalLensGraphMouse(new LensMagnificationGraphMousePlugin(1.f, 6.f, .2f))); - hyperbolicLayoutSupport.getLensTransformer().setLensShape(hyperbolicViewSupport.getLensTransformer().getLensShape()); - magnifyViewSupport.getLensTransformer().setLensShape(hyperbolicLayoutSupport.getLensTransformer().getLensShape()); - magnifyLayoutSupport.getLensTransformer().setLensShape(magnifyViewSupport.getLensTransformer().getLensShape()); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + /** the graph */ + Network graph; + + FRLayout graphLayout; + + /** a grid shaped graph */ + Network grid; + + Layout gridLayout; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + /** provides a Hyperbolic lens for the view */ + LensSupport hyperbolicViewSupport; + /** provides a magnification lens for the view */ + LensSupport magnifyViewSupport; + + /** provides a Hyperbolic lens for the model */ + LensSupport hyperbolicLayoutSupport; + /** provides a magnification lens for the model */ + LensSupport magnifyLayoutSupport; + + ScalingControl scaler; + + /** create an instance of a simple graph with controls to demo the zoomand hyperbolic features. */ + public LensDemo() { + + // create a simple graph for the demo + graph = TestGraphs.getOneComponentGraph(); + + graphLayout = new FRLayout(graph.asGraph()); + graphLayout.setMaxIterations(1000); + + Dimension preferredSize = new Dimension(600, 600); + Map map = new HashMap(); + Function vlf = Functions.forMap(map); + grid = this.generateVertexGrid(map, preferredSize, 25); + gridLayout = new StaticLayout(grid.asGraph(), vlf, preferredSize); + + final VisualizationModel visualizationModel = + new DefaultVisualizationModel(graph, graphLayout, preferredSize); + vv = new VisualizationViewer(visualizationModel, preferredSize); + + PickedState ps = vv.getPickedVertexState(); + PickedState pes = vv.getPickedEdgeState(); + vv.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer(ps, Color.red, Color.yellow)); + vv.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer(pes, Color.black, Color.cyan)); + vv.setBackground(Color.white); + + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + + final Function ovals = vv.getRenderContext().getVertexShapeTransformer(); + final Function squares = + Functions.constant(new Rectangle2D.Float(-10, -10, 20, 20)); + + // add a listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + + Container content = getContentPane(); + GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv); + content.add(gzsp); + + /** the regular graph mouse for the normal view */ + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + + vv.setGraphMouse(graphMouse); + vv.addKeyListener(graphMouse.getModeKeyListener()); + + hyperbolicViewSupport = + new ViewLensSupport( + vv, + new HyperbolicShapeTransformer( + vv, vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), + new ModalLensGraphMouse()); + hyperbolicLayoutSupport = + new LayoutLensSupport( + vv, + new HyperbolicTransformer( + vv, vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT)), + new ModalLensGraphMouse()); + magnifyViewSupport = + new ViewLensSupport( + vv, + new MagnifyShapeTransformer( + vv, vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), + new ModalLensGraphMouse(new LensMagnificationGraphMousePlugin(1.f, 6.f, .2f))); + magnifyLayoutSupport = + new LayoutLensSupport( + vv, + new MagnifyTransformer( + vv, vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT)), + new ModalLensGraphMouse(new LensMagnificationGraphMousePlugin(1.f, 6.f, .2f))); + hyperbolicLayoutSupport + .getLensTransformer() + .setLensShape(hyperbolicViewSupport.getLensTransformer().getLensShape()); + magnifyViewSupport + .getLensTransformer() + .setLensShape(hyperbolicLayoutSupport.getLensTransformer().getLensShape()); + magnifyLayoutSupport + .getLensTransformer() + .setLensShape(magnifyViewSupport.getLensTransformer().getLensShape()); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - - ButtonGroup radio = new ButtonGroup(); - JRadioButton normal = new JRadioButton("None"); - normal.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - if(hyperbolicViewSupport != null) { - hyperbolicViewSupport.deactivate(); - } - if(hyperbolicLayoutSupport != null) { - hyperbolicLayoutSupport.deactivate(); - } - if(magnifyViewSupport != null) { - magnifyViewSupport.deactivate(); - } - if(magnifyLayoutSupport != null) { - magnifyLayoutSupport.deactivate(); - } - } + + ButtonGroup radio = new ButtonGroup(); + JRadioButton normal = new JRadioButton("None"); + normal.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + if (hyperbolicViewSupport != null) { + hyperbolicViewSupport.deactivate(); + } + if (hyperbolicLayoutSupport != null) { + hyperbolicLayoutSupport.deactivate(); + } + if (magnifyViewSupport != null) { + magnifyViewSupport.deactivate(); + } + if (magnifyLayoutSupport != null) { + magnifyLayoutSupport.deactivate(); + } } + } }); - final JRadioButton hyperView = new JRadioButton("Hyperbolic View"); - hyperView.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - hyperbolicViewSupport.activate(e.getStateChange() == ItemEvent.SELECTED); - } + final JRadioButton hyperView = new JRadioButton("Hyperbolic View"); + hyperView.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + hyperbolicViewSupport.activate(e.getStateChange() == ItemEvent.SELECTED); + } }); - final JRadioButton hyperModel = new JRadioButton("Hyperbolic Layout"); - hyperModel.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - hyperbolicLayoutSupport.activate(e.getStateChange() == ItemEvent.SELECTED); - } + final JRadioButton hyperModel = new JRadioButton("Hyperbolic Layout"); + hyperModel.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + hyperbolicLayoutSupport.activate(e.getStateChange() == ItemEvent.SELECTED); + } }); - final JRadioButton magnifyView = new JRadioButton("Magnified View"); - magnifyView.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - magnifyViewSupport.activate(e.getStateChange() == ItemEvent.SELECTED); + final JRadioButton magnifyView = new JRadioButton("Magnified View"); + magnifyView.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + magnifyViewSupport.activate(e.getStateChange() == ItemEvent.SELECTED); + } + }); + final JRadioButton magnifyModel = new JRadioButton("Magnified Layout"); + magnifyModel.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + magnifyLayoutSupport.activate(e.getStateChange() == ItemEvent.SELECTED); + } + }); + JLabel modeLabel = new JLabel(" Mode Menu >>"); + modeLabel.setUI(new VerticalLabelUI(false)); + radio.add(normal); + radio.add(hyperModel); + radio.add(hyperView); + radio.add(magnifyModel); + radio.add(magnifyView); + normal.setSelected(true); + + graphMouse.addItemListener(hyperbolicLayoutSupport.getGraphMouse().getModeListener()); + graphMouse.addItemListener(hyperbolicViewSupport.getGraphMouse().getModeListener()); + graphMouse.addItemListener(magnifyLayoutSupport.getGraphMouse().getModeListener()); + graphMouse.addItemListener(magnifyViewSupport.getGraphMouse().getModeListener()); + + ButtonGroup graphRadio = new ButtonGroup(); + JRadioButton graphButton = new JRadioButton("Graph"); + graphButton.setSelected(true); + graphButton.addItemListener( + new ItemListener() { + + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + visualizationModel.setGraphLayout(graphLayout); + vv.getRenderContext().setVertexShapeTransformer(ovals); + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + vv.repaint(); } + } }); - final JRadioButton magnifyModel = new JRadioButton("Magnified Layout"); - magnifyModel.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - magnifyLayoutSupport.activate(e.getStateChange() == ItemEvent.SELECTED); + JRadioButton gridButton = new JRadioButton("Grid"); + gridButton.addItemListener( + new ItemListener() { + + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + visualizationModel.setGraphLayout(gridLayout); + vv.getRenderContext().setVertexShapeTransformer(squares); + vv.getRenderContext().setVertexLabelTransformer(Functions.constant(null)); + vv.repaint(); } + } }); - JLabel modeLabel = new JLabel(" Mode Menu >>"); - modeLabel.setUI(new VerticalLabelUI(false)); - radio.add(normal); - radio.add(hyperModel); - radio.add(hyperView); - radio.add(magnifyModel); - radio.add(magnifyView); - normal.setSelected(true); - - graphMouse.addItemListener(hyperbolicLayoutSupport.getGraphMouse().getModeListener()); - graphMouse.addItemListener(hyperbolicViewSupport.getGraphMouse().getModeListener()); - graphMouse.addItemListener(magnifyLayoutSupport.getGraphMouse().getModeListener()); - graphMouse.addItemListener(magnifyViewSupport.getGraphMouse().getModeListener()); - - ButtonGroup graphRadio = new ButtonGroup(); - JRadioButton graphButton = new JRadioButton("Graph"); - graphButton.setSelected(true); - graphButton.addItemListener(new ItemListener() { - - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - visualizationModel.setGraphLayout(graphLayout); - vv.getRenderContext().setVertexShapeTransformer(ovals); - vv.getRenderContext().setVertexLabelTransformer( - new ToStringLabeller()); - vv.repaint(); - } - }}); - JRadioButton gridButton = new JRadioButton("Grid"); - gridButton.addItemListener(new ItemListener() { - - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - visualizationModel.setGraphLayout(gridLayout); - vv.getRenderContext().setVertexShapeTransformer(squares); - vv.getRenderContext().setVertexLabelTransformer(Functions.constant(null)); - vv.repaint(); - } - }}); - graphRadio.add(graphButton); - graphRadio.add(gridButton); - - JPanel modePanel = new JPanel(new GridLayout(3,1)); - modePanel.setBorder(BorderFactory.createTitledBorder("Display")); - modePanel.add(graphButton); - modePanel.add(gridButton); - - JMenuBar menubar = new JMenuBar(); - menubar.add(graphMouse.getModeMenu()); - gzsp.setCorner(menubar); - - - Box controls = Box.createHorizontalBox(); - JPanel zoomControls = new JPanel(new GridLayout(2,1)); - zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom")); - JPanel hyperControls = new JPanel(new GridLayout(3,2)); - hyperControls.setBorder(BorderFactory.createTitledBorder("Examiner Lens")); - zoomControls.add(plus); - zoomControls.add(minus); - - hyperControls.add(normal); - hyperControls.add(new JLabel()); - - hyperControls.add(hyperModel); - hyperControls.add(magnifyModel); - - hyperControls.add(hyperView); - hyperControls.add(magnifyView); - - controls.add(zoomControls); - controls.add(hyperControls); - controls.add(modePanel); - controls.add(modeLabel); - content.add(controls, BorderLayout.SOUTH); + graphRadio.add(graphButton); + graphRadio.add(gridButton); + + JPanel modePanel = new JPanel(new GridLayout(3, 1)); + modePanel.setBorder(BorderFactory.createTitledBorder("Display")); + modePanel.add(graphButton); + modePanel.add(gridButton); + + JMenuBar menubar = new JMenuBar(); + menubar.add(graphMouse.getModeMenu()); + gzsp.setCorner(menubar); + + Box controls = Box.createHorizontalBox(); + JPanel zoomControls = new JPanel(new GridLayout(2, 1)); + zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom")); + JPanel hyperControls = new JPanel(new GridLayout(3, 2)); + hyperControls.setBorder(BorderFactory.createTitledBorder("Examiner Lens")); + zoomControls.add(plus); + zoomControls.add(minus); + + hyperControls.add(normal); + hyperControls.add(new JLabel()); + + hyperControls.add(hyperModel); + hyperControls.add(magnifyModel); + + hyperControls.add(hyperView); + hyperControls.add(magnifyView); + + controls.add(zoomControls); + controls.add(hyperControls); + controls.add(modePanel); + controls.add(modeLabel); + content.add(controls, BorderLayout.SOUTH); + } + + private Network generateVertexGrid( + Map vlf, Dimension d, int interval) { + int count = d.width / interval * d.height / interval; + MutableNetwork graph = NetworkBuilder.directed().build(); + for (int i = 0; i < count; i++) { + int x = interval * i; + int y = x / d.width * interval; + x %= d.width; + + Point2D location = new Point2D.Float(x, y); + String vertex = "v" + i; + vlf.put(vertex, location); + graph.addNode(vertex); } + return graph; + } - private Network generateVertexGrid(Map vlf, - Dimension d, int interval) { - int count = d.width/interval * d.height/interval; - MutableNetwork graph = NetworkBuilder.directed().build(); - for(int i=0; iThe images used in this demo (courtesy of slashdot.org) are rectangular but with a transparent + * background. When vertices are represented by these images, it looks better if the actual shape of + * the opaque part of the image is computed so that the edge arrowheads follow the visual shape of + * the image. This demo uses the FourPassImageShaper class to compute the Shape from an image with + * transparent background. + * * @author Tom Nelson - * */ public class LensVertexImageShaperDemo extends JApplet { - /** - * - */ - private static final long serialVersionUID = 5432239991020505763L; + /** */ + private static final long serialVersionUID = 5432239991020505763L; - /** - * the graph - */ - Network graph; + /** the graph */ + Network graph; - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - /** - * some icon names to use - */ - String[] iconNames = { - "apple", - "os", - "x", - "linux", - "inputdevices", - "wireless", - "graphics3", - "gamespcgames", - "humor", - "music", - "privacy" - }; - - LensSupport viewSupport; - LensSupport modelSupport; - LensSupport magnifyLayoutSupport; - LensSupport magnifyViewSupport; - /** - * create an instance of a simple graph with controls to - * demo the zoom features. - * - */ - public LensVertexImageShaperDemo() { - - // create a simple graph for the demo - graph = createGraph(); - - // Maps for the labels and icons - Map map = new HashMap(); - Map iconMap = new HashMap(); - for (Number node : graph.nodes()) { - int i = node.intValue(); - map.put(node, iconNames[i % iconNames.length]); - - String name = "/images/topic" + iconNames[i] + ".gif"; - try { - Icon icon = new LayeredIcon( - new ImageIcon(LensVertexImageShaperDemo.class.getResource(name)).getImage()); - iconMap.put(node, icon); - } catch (Exception ex) { - System.err.println( - "You need slashdoticons.jar in your classpath to see the image " + name); - } - } - - FRLayout layout = new FRLayout(graph.asGraph()); - layout.setMaxIterations(100); - vv = new VisualizationViewer(graph, layout, new Dimension(600,600)); - - Function vpf = - new PickableVertexPaintTransformer(vv.getPickedVertexState(), Color.white, Color.yellow); - vv.getRenderContext().setVertexFillPaintTransformer(vpf); - vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(vv.getPickedEdgeState(), Color.black, Color.cyan)); - - vv.setBackground(Color.white); - - final Function vertexStringerImpl = - new VertexStringerImpl(map); - vv.getRenderContext().setVertexLabelTransformer(vertexStringerImpl); - vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan)); - vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan)); - - - // features on and off. For a real application, use VertexIconAndShapeFunction instead. - final VertexIconShapeTransformer vertexImageShapeFunction = - new VertexIconShapeTransformer(new EllipseVertexShapeTransformer()); - - final Function vertexIconFunction = Functions.forMap(iconMap); - - vertexImageShapeFunction.setIconMap(iconMap); - - vv.getRenderContext().setVertexShapeTransformer(vertexImageShapeFunction); - vv.getRenderContext().setVertexIconTransformer(vertexIconFunction); - - - // Get the pickedState and add a listener that will decorate the - // Vertex images with a checkmark icon when they are picked - PickedState ps = vv.getPickedVertexState(); - ps.addItemListener(new PickWithIconListener(vertexIconFunction)); - - vv.addPostRenderPaintable(new VisualizationViewer.Paintable(){ - int x; - int y; - Font font; - FontMetrics metrics; - int swidth; - int sheight; - String str = "Thank You, slashdot.org, for the images!"; - - public void paint(Graphics g) { - Dimension d = vv.getSize(); - if(font == null) { - font = new Font(g.getFont().getName(), Font.BOLD, 20); - metrics = g.getFontMetrics(font); - swidth = metrics.stringWidth(str); - sheight = metrics.getMaxAscent()+metrics.getMaxDescent(); - x = (d.width-swidth)/2; - y = (int)(d.height-sheight*1.5); - } - g.setFont(font); - Color oldColor = g.getColor(); - g.setColor(Color.lightGray); - g.drawString(str, x, y); - g.setColor(oldColor); - } - public boolean useTransform() { - return false; + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + /** some icon names to use */ + String[] iconNames = { + "apple", + "os", + "x", + "linux", + "inputdevices", + "wireless", + "graphics3", + "gamespcgames", + "humor", + "music", + "privacy" + }; + + LensSupport viewSupport; + LensSupport modelSupport; + LensSupport magnifyLayoutSupport; + LensSupport magnifyViewSupport; + /** create an instance of a simple graph with controls to demo the zoom features. */ + public LensVertexImageShaperDemo() { + + // create a simple graph for the demo + graph = createGraph(); + + // Maps for the labels and icons + Map map = new HashMap(); + Map iconMap = new HashMap(); + for (Number node : graph.nodes()) { + int i = node.intValue(); + map.put(node, iconNames[i % iconNames.length]); + + String name = "/images/topic" + iconNames[i] + ".gif"; + try { + Icon icon = + new LayeredIcon( + new ImageIcon(LensVertexImageShaperDemo.class.getResource(name)).getImage()); + iconMap.put(node, icon); + } catch (Exception ex) { + System.err.println("You need slashdoticons.jar in your classpath to see the image " + name); + } + } + + FRLayout layout = new FRLayout(graph.asGraph()); + layout.setMaxIterations(100); + vv = new VisualizationViewer(graph, layout, new Dimension(600, 600)); + + Function vpf = + new PickableVertexPaintTransformer( + vv.getPickedVertexState(), Color.white, Color.yellow); + vv.getRenderContext().setVertexFillPaintTransformer(vpf); + vv.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv.getPickedEdgeState(), Color.black, Color.cyan)); + + vv.setBackground(Color.white); + + final Function vertexStringerImpl = new VertexStringerImpl(map); + vv.getRenderContext().setVertexLabelTransformer(vertexStringerImpl); + vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan)); + vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan)); + + // features on and off. For a real application, use VertexIconAndShapeFunction instead. + final VertexIconShapeTransformer vertexImageShapeFunction = + new VertexIconShapeTransformer(new EllipseVertexShapeTransformer()); + + final Function vertexIconFunction = Functions.forMap(iconMap); + + vertexImageShapeFunction.setIconMap(iconMap); + + vv.getRenderContext().setVertexShapeTransformer(vertexImageShapeFunction); + vv.getRenderContext().setVertexIconTransformer(vertexIconFunction); + + // Get the pickedState and add a listener that will decorate the + // Vertex images with a checkmark icon when they are picked + PickedState ps = vv.getPickedVertexState(); + ps.addItemListener(new PickWithIconListener(vertexIconFunction)); + + vv.addPostRenderPaintable( + new VisualizationViewer.Paintable() { + int x; + int y; + Font font; + FontMetrics metrics; + int swidth; + int sheight; + String str = "Thank You, slashdot.org, for the images!"; + + public void paint(Graphics g) { + Dimension d = vv.getSize(); + if (font == null) { + font = new Font(g.getFont().getName(), Font.BOLD, 20); + metrics = g.getFontMetrics(font); + swidth = metrics.stringWidth(str); + sheight = metrics.getMaxAscent() + metrics.getMaxDescent(); + x = (d.width - swidth) / 2; + y = (int) (d.height - sheight * 1.5); } + g.setFont(font); + Color oldColor = g.getColor(); + g.setColor(Color.lightGray); + g.drawString(str, x, y); + g.setColor(oldColor); + } + + public boolean useTransform() { + return false; + } }); - // add a listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - - Container content = getContentPane(); - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - content.add(panel); - - final DefaultModalGraphMouse graphMouse - = new DefaultModalGraphMouse(); - vv.setGraphMouse(graphMouse); - - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + // add a listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + + Container content = getContentPane(); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + content.add(panel); + + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + vv.setGraphMouse(graphMouse); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - - JComboBox modeBox = graphMouse.getModeComboBox(); - JPanel modePanel = new JPanel(); - modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); - modePanel.add(modeBox); - - JPanel scaleGrid = new JPanel(new GridLayout(1,0)); - scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); - JPanel controls = new JPanel(); - scaleGrid.add(plus); - scaleGrid.add(minus); - controls.add(scaleGrid); - - controls.add(modePanel); - content.add(controls, BorderLayout.SOUTH); - - this.viewSupport = new MagnifyImageLensSupport(vv); -// new ViewLensSupport(vv, new HyperbolicShapeTransformer(vv, -// vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), -// new ModalLensGraphMouse()); - - this.modelSupport = new LayoutLensSupport(vv); - - graphMouse.addItemListener(modelSupport.getGraphMouse().getModeListener()); - graphMouse.addItemListener(viewSupport.getGraphMouse().getModeListener()); - - ButtonGroup radio = new ButtonGroup(); - JRadioButton none = new JRadioButton("None"); - none.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - if(viewSupport != null) { - viewSupport.deactivate(); - } - if(modelSupport != null) { - modelSupport.deactivate(); - } + + JComboBox modeBox = graphMouse.getModeComboBox(); + JPanel modePanel = new JPanel(); + modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); + modePanel.add(modeBox); + + JPanel scaleGrid = new JPanel(new GridLayout(1, 0)); + scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); + JPanel controls = new JPanel(); + scaleGrid.add(plus); + scaleGrid.add(minus); + controls.add(scaleGrid); + + controls.add(modePanel); + content.add(controls, BorderLayout.SOUTH); + + this.viewSupport = new MagnifyImageLensSupport(vv); + // new ViewLensSupport(vv, new HyperbolicShapeTransformer(vv, + // vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), + // new ModalLensGraphMouse()); + + this.modelSupport = new LayoutLensSupport(vv); + + graphMouse.addItemListener(modelSupport.getGraphMouse().getModeListener()); + graphMouse.addItemListener(viewSupport.getGraphMouse().getModeListener()); + + ButtonGroup radio = new ButtonGroup(); + JRadioButton none = new JRadioButton("None"); + none.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (viewSupport != null) { + viewSupport.deactivate(); + } + if (modelSupport != null) { + modelSupport.deactivate(); } + } }); - none.setSelected(true); + none.setSelected(true); - JRadioButton hyperView = new JRadioButton("View"); - hyperView.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - viewSupport.activate(e.getStateChange() == ItemEvent.SELECTED); - } + JRadioButton hyperView = new JRadioButton("View"); + hyperView.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + viewSupport.activate(e.getStateChange() == ItemEvent.SELECTED); + } }); - JRadioButton hyperModel = new JRadioButton("Layout"); - hyperModel.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - modelSupport.activate(e.getStateChange() == ItemEvent.SELECTED); - } + JRadioButton hyperModel = new JRadioButton("Layout"); + hyperModel.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + modelSupport.activate(e.getStateChange() == ItemEvent.SELECTED); + } }); - radio.add(none); - radio.add(hyperView); - radio.add(hyperModel); - - JMenuBar menubar = new JMenuBar(); - JMenu modeMenu = graphMouse.getModeMenu(); - menubar.add(modeMenu); - - JPanel lensPanel = new JPanel(new GridLayout(2,0)); - lensPanel.setBorder(BorderFactory.createTitledBorder("Lens")); - lensPanel.add(none); - lensPanel.add(hyperView); - lensPanel.add(hyperModel); - controls.add(lensPanel); + radio.add(none); + radio.add(hyperView); + radio.add(hyperModel); + + JMenuBar menubar = new JMenuBar(); + JMenu modeMenu = graphMouse.getModeMenu(); + menubar.add(modeMenu); + + JPanel lensPanel = new JPanel(new GridLayout(2, 0)); + lensPanel.setBorder(BorderFactory.createTitledBorder("Lens")); + lensPanel.add(none); + lensPanel.add(hyperView); + lensPanel.add(hyperModel); + controls.add(lensPanel); + } + + /** + * A simple implementation of VertexStringer that gets Vertex labels from a Map + * + * @author Tom Nelson + */ + class VertexStringerImpl implements Function { + + Map map = new HashMap(); + + boolean enabled = true; + + public VertexStringerImpl(Map map) { + this.map = map; } - + /** - * A simple implementation of VertexStringer that - * gets Vertex labels from a Map - * - * @author Tom Nelson - * - * + * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.Vertex) */ - class VertexStringerImpl implements Function { - - Map map = new HashMap(); - - boolean enabled = true; - - public VertexStringerImpl(Map map) { - this.map = map; - } - - /** - * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.Vertex) - */ - public String apply(V v) { - if(isEnabled()) { - return map.get(v); - } else { - return ""; - } - } - - /** - * @return Returns the enabled. - */ - public boolean isEnabled() { - return enabled; - } + public String apply(V v) { + if (isEnabled()) { + return map.get(v); + } else { + return ""; + } + } - /** - * @param enabled The enabled to set. - */ - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } + /** @return Returns the enabled. */ + public boolean isEnabled() { + return enabled; } - - Network createGraph() { - MutableNetwork graph = NetworkBuilder.directed().build(); - graph.addEdge(0, 1, new Double(Math.random())); - graph.addEdge(3, 0, new Double(Math.random())); - graph.addEdge(0, 4, new Double(Math.random())); - graph.addEdge(4, 5, new Double(Math.random())); - graph.addEdge(5, 3, new Double(Math.random())); - graph.addEdge(2, 1, new Double(Math.random())); - graph.addEdge(4, 1, new Double(Math.random())); - graph.addEdge(8, 2, new Double(Math.random())); - graph.addEdge(3, 8, new Double(Math.random())); - graph.addEdge(6, 7, new Double(Math.random())); - graph.addEdge(7, 5, new Double(Math.random())); - graph.addEdge(0, 9, new Double(Math.random())); - graph.addEdge(9, 8, new Double(Math.random())); - graph.addEdge(7, 6, new Double(Math.random())); - graph.addEdge(6, 5, new Double(Math.random())); - graph.addEdge(4, 2, new Double(Math.random())); - graph.addEdge(5, 4, new Double(Math.random())); - graph.addEdge(4, 10, new Double(Math.random())); - graph.addEdge(10, 4, new Double(Math.random())); - - return graph; + + /** @param enabled The enabled to set. */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; } + } - public static class PickWithIconListener implements ItemListener { - Function imager; - Icon checked; - - public PickWithIconListener(Function imager) { - this.imager = imager; - checked = new Checkmark(Color.red); - } + Network createGraph() { + MutableNetwork graph = NetworkBuilder.directed().build(); + graph.addEdge(0, 1, new Double(Math.random())); + graph.addEdge(3, 0, new Double(Math.random())); + graph.addEdge(0, 4, new Double(Math.random())); + graph.addEdge(4, 5, new Double(Math.random())); + graph.addEdge(5, 3, new Double(Math.random())); + graph.addEdge(2, 1, new Double(Math.random())); + graph.addEdge(4, 1, new Double(Math.random())); + graph.addEdge(8, 2, new Double(Math.random())); + graph.addEdge(3, 8, new Double(Math.random())); + graph.addEdge(6, 7, new Double(Math.random())); + graph.addEdge(7, 5, new Double(Math.random())); + graph.addEdge(0, 9, new Double(Math.random())); + graph.addEdge(9, 8, new Double(Math.random())); + graph.addEdge(7, 6, new Double(Math.random())); + graph.addEdge(6, 5, new Double(Math.random())); + graph.addEdge(4, 2, new Double(Math.random())); + graph.addEdge(5, 4, new Double(Math.random())); + graph.addEdge(4, 10, new Double(Math.random())); + graph.addEdge(10, 4, new Double(Math.random())); - public void itemStateChanged(ItemEvent e) { - Icon icon = imager.apply((Number)e.getItem()); - if(icon != null && icon instanceof LayeredIcon) { - if(e.getStateChange() == ItemEvent.SELECTED) { - ((LayeredIcon)icon).add(checked); - } else { - ((LayeredIcon)icon).remove(checked); - } - } + return graph; + } + + public static class PickWithIconListener implements ItemListener { + Function imager; + Icon checked; + + public PickWithIconListener(Function imager) { + this.imager = imager; + checked = new Checkmark(Color.red); + } + + public void itemStateChanged(ItemEvent e) { + Icon icon = imager.apply((Number) e.getItem()); + if (icon != null && icon instanceof LayeredIcon) { + if (e.getStateChange() == ItemEvent.SELECTED) { + ((LayeredIcon) icon).add(checked); + } else { + ((LayeredIcon) icon).remove(checked); } + } } + } - public static void main(String[] args) { - JFrame frame = new JFrame(); - Container content = frame.getContentPane(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + public static void main(String[] args) { + JFrame frame = new JFrame(); + Container content = frame.getContentPane(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - content.add(new LensVertexImageShaperDemo()); - frame.pack(); - frame.setVisible(true); - } + content.add(new LensVertexImageShaperDemo()); + frame.pack(); + frame.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/MinimumSpanningTreeDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/MinimumSpanningTreeDemo.java index a12862e4..a2035eec 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/MinimumSpanningTreeDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/MinimumSpanningTreeDemo.java @@ -1,31 +1,14 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JApplet; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; - import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.KKLayout; import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.algorithms.layout.StaticLayout; @@ -47,210 +30,226 @@ import edu.uci.ics.jung.visualization.picking.PickedState; import edu.uci.ics.jung.visualization.renderers.Renderer; import edu.uci.ics.jung.visualization.transform.MutableTransformer; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.BorderFactory; +import javax.swing.JApplet; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; /** - * Demonstrates a single graph with 3 layouts in 3 views. - * The first view is an undirected graph using KKLayout - * The second view show a TreeLayout view of a MinimumSpanningTree - * of the first graph. The third view shows the complete graph - * of the first view, using the layout positions of the - * MinimumSpanningTree tree view. - * + * Demonstrates a single graph with 3 layouts in 3 views. The first view is an undirected graph + * using KKLayout The second view show a TreeLayout view of a MinimumSpanningTree of the first + * graph. The third view shows the complete graph of the first view, using the layout positions of + * the MinimumSpanningTree tree view. + * * @author Tom Nelson - * */ @SuppressWarnings("serial") public class MinimumSpanningTreeDemo extends JApplet { - /** - * the graph - */ - Network graph; - Network tree; - - /** - * the visual components and renderers for the graph - */ - VisualizationViewer vv0; - VisualizationViewer vv1; - VisualizationViewer vv2; - - /** - * the normal Function - */ - MutableTransformer layoutTransformer; - - Dimension preferredSize = new Dimension(300,300); - Dimension preferredLayoutSize = new Dimension(400,400); - Dimension preferredSizeRect = new Dimension(500,250); - - /** - * create an instance of a simple graph in two views with controls to - * demo the zoom features. - * - */ - public MinimumSpanningTreeDemo() { - - // create a simple graph for the demo - // both models will share one graph - graph = TestGraphs.getDemoGraph(); - - tree = MinimumSpanningTree.extractFrom(graph, e -> 1.0); - - // create two layouts for the one graph, one layout for each model - Layout layout0 = new KKLayout(graph.asGraph()); - layout0.setSize(preferredLayoutSize); - Layout layout1 = new TreeLayout(tree.asGraph()); - Layout layout2 = new StaticLayout(graph.asGraph(), layout1); - - // create the two models, each with a different layout - VisualizationModel vm0 = - new DefaultVisualizationModel(graph, layout0, preferredSize); - VisualizationModel vm1 = - new DefaultVisualizationModel(tree, layout1, preferredSizeRect); - VisualizationModel vm2 = - new DefaultVisualizationModel(graph, layout2, preferredSizeRect); - - // create the two views, one for each model - // they share the same renderer - vv0 = new VisualizationViewer(vm0, preferredSize); - vv1 = new VisualizationViewer(vm1, preferredSizeRect); - vv2 = new VisualizationViewer(vm2, preferredSizeRect); - - vv1.getRenderContext().setMultiLayerTransformer(vv0.getRenderContext().getMultiLayerTransformer()); - vv2.getRenderContext().setMultiLayerTransformer(vv0.getRenderContext().getMultiLayerTransformer()); - - vv1.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); - - vv0.addChangeListener(vv1); - vv1.addChangeListener(vv2); - - vv0.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - vv2.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - - Color back = Color.decode("0xffffbb"); - vv0.setBackground(back); - vv1.setBackground(back); - vv2.setBackground(back); - - vv0.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR); - vv0.setForeground(Color.darkGray); - vv1.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR); - vv1.setForeground(Color.darkGray); - vv2.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR); - vv2.setForeground(Color.darkGray); - - // share one PickedState between the two views - PickedState ps = new MultiPickedState(); - vv0.setPickedVertexState(ps); - vv1.setPickedVertexState(ps); - vv2.setPickedVertexState(ps); - - PickedState pes = new MultiPickedState(); - vv0.setPickedEdgeState(pes); - vv1.setPickedEdgeState(pes); - vv2.setPickedEdgeState(pes); - - - // set an edge paint function that will show picking for edges - vv0.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(vv0.getPickedEdgeState(), Color.black, Color.red)); - vv0.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(vv0.getPickedVertexState(), - Color.red, Color.yellow)); - vv1.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(vv1.getPickedEdgeState(), Color.black, Color.red)); - vv1.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(vv1.getPickedVertexState(), - Color.red, Color.yellow)); - - // add default listeners for ToolTips - vv0.setVertexToolTipTransformer(new ToStringLabeller()); - vv1.setVertexToolTipTransformer(new ToStringLabeller()); - vv2.setVertexToolTipTransformer(new ToStringLabeller()); - - vv0.setLayout(new BorderLayout()); - vv1.setLayout(new BorderLayout()); - vv2.setLayout(new BorderLayout()); - - Font font = vv0.getFont().deriveFont(Font.BOLD, 16); - JLabel vv0Label = new JLabel("Original Network

        using KKLayout"); - vv0Label.setFont(font); - JLabel vv1Label = new JLabel("Minimum Spanning Trees"); - vv1Label.setFont(font); - JLabel vv2Label = new JLabel("Original Graph using TreeLayout"); - vv2Label.setFont(font); - JPanel flow0 = new JPanel(); - flow0.setOpaque(false); - JPanel flow1 = new JPanel(); - flow1.setOpaque(false); - JPanel flow2 = new JPanel(); - flow2.setOpaque(false); - flow0.add(vv0Label); - flow1.add(vv1Label); - flow2.add(vv2Label); - vv0.add(flow0, BorderLayout.NORTH); - vv1.add(flow1, BorderLayout.NORTH); - vv2.add(flow2, BorderLayout.NORTH); - - Container content = getContentPane(); - JPanel grid = new JPanel(new GridLayout(0,1)); - JPanel panel = new JPanel(new BorderLayout()); - panel.add(new GraphZoomScrollPane(vv0), BorderLayout.WEST); - grid.add(new GraphZoomScrollPane(vv1)); - grid.add(new GraphZoomScrollPane(vv2)); - panel.add(grid); - - content.add(panel); - - // create a GraphMouse for each view - DefaultModalGraphMouse gm0 = new DefaultModalGraphMouse(); - DefaultModalGraphMouse gm1 = new DefaultModalGraphMouse(); - DefaultModalGraphMouse gm2 = new DefaultModalGraphMouse(); - - vv0.setGraphMouse(gm0); - vv1.setGraphMouse(gm1); - vv2.setGraphMouse(gm2); - - // create zoom buttons for scaling the Function that is - // shared between the two models. - final ScalingControl scaler = new CrossoverScalingControl(); - - vv0.scaleToLayout(scaler); - vv1.scaleToLayout(scaler); - vv2.scaleToLayout(scaler); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv1, 1.1f, vv1.getCenter()); - } + /** the graph */ + Network graph; + + Network tree; + + /** the visual components and renderers for the graph */ + VisualizationViewer vv0; + + VisualizationViewer vv1; + VisualizationViewer vv2; + + /** the normal Function */ + MutableTransformer layoutTransformer; + + Dimension preferredSize = new Dimension(300, 300); + Dimension preferredLayoutSize = new Dimension(400, 400); + Dimension preferredSizeRect = new Dimension(500, 250); + + /** create an instance of a simple graph in two views with controls to demo the zoom features. */ + public MinimumSpanningTreeDemo() { + + // create a simple graph for the demo + // both models will share one graph + graph = TestGraphs.getDemoGraph(); + + tree = MinimumSpanningTree.extractFrom(graph, e -> 1.0); + + // create two layouts for the one graph, one layout for each model + Layout layout0 = new KKLayout(graph.asGraph()); + layout0.setSize(preferredLayoutSize); + Layout layout1 = new TreeLayout(tree.asGraph()); + Layout layout2 = new StaticLayout(graph.asGraph(), layout1); + + // create the two models, each with a different layout + VisualizationModel vm0 = + new DefaultVisualizationModel(graph, layout0, preferredSize); + VisualizationModel vm1 = + new DefaultVisualizationModel(tree, layout1, preferredSizeRect); + VisualizationModel vm2 = + new DefaultVisualizationModel(graph, layout2, preferredSizeRect); + + // create the two views, one for each model + // they share the same renderer + vv0 = new VisualizationViewer(vm0, preferredSize); + vv1 = new VisualizationViewer(vm1, preferredSizeRect); + vv2 = new VisualizationViewer(vm2, preferredSizeRect); + + vv1.getRenderContext() + .setMultiLayerTransformer(vv0.getRenderContext().getMultiLayerTransformer()); + vv2.getRenderContext() + .setMultiLayerTransformer(vv0.getRenderContext().getMultiLayerTransformer()); + + vv1.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); + + vv0.addChangeListener(vv1); + vv1.addChangeListener(vv2); + + vv0.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + vv2.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + + Color back = Color.decode("0xffffbb"); + vv0.setBackground(back); + vv1.setBackground(back); + vv2.setBackground(back); + + vv0.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR); + vv0.setForeground(Color.darkGray); + vv1.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR); + vv1.setForeground(Color.darkGray); + vv2.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR); + vv2.setForeground(Color.darkGray); + + // share one PickedState between the two views + PickedState ps = new MultiPickedState(); + vv0.setPickedVertexState(ps); + vv1.setPickedVertexState(ps); + vv2.setPickedVertexState(ps); + + PickedState pes = new MultiPickedState(); + vv0.setPickedEdgeState(pes); + vv1.setPickedEdgeState(pes); + vv2.setPickedEdgeState(pes); + + // set an edge paint function that will show picking for edges + vv0.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv0.getPickedEdgeState(), Color.black, Color.red)); + vv0.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer( + vv0.getPickedVertexState(), Color.red, Color.yellow)); + vv1.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv1.getPickedEdgeState(), Color.black, Color.red)); + vv1.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer( + vv1.getPickedVertexState(), Color.red, Color.yellow)); + + // add default listeners for ToolTips + vv0.setVertexToolTipTransformer(new ToStringLabeller()); + vv1.setVertexToolTipTransformer(new ToStringLabeller()); + vv2.setVertexToolTipTransformer(new ToStringLabeller()); + + vv0.setLayout(new BorderLayout()); + vv1.setLayout(new BorderLayout()); + vv2.setLayout(new BorderLayout()); + + Font font = vv0.getFont().deriveFont(Font.BOLD, 16); + JLabel vv0Label = new JLabel("Original Network

        using KKLayout"); + vv0Label.setFont(font); + JLabel vv1Label = new JLabel("Minimum Spanning Trees"); + vv1Label.setFont(font); + JLabel vv2Label = new JLabel("Original Graph using TreeLayout"); + vv2Label.setFont(font); + JPanel flow0 = new JPanel(); + flow0.setOpaque(false); + JPanel flow1 = new JPanel(); + flow1.setOpaque(false); + JPanel flow2 = new JPanel(); + flow2.setOpaque(false); + flow0.add(vv0Label); + flow1.add(vv1Label); + flow2.add(vv2Label); + vv0.add(flow0, BorderLayout.NORTH); + vv1.add(flow1, BorderLayout.NORTH); + vv2.add(flow2, BorderLayout.NORTH); + + Container content = getContentPane(); + JPanel grid = new JPanel(new GridLayout(0, 1)); + JPanel panel = new JPanel(new BorderLayout()); + panel.add(new GraphZoomScrollPane(vv0), BorderLayout.WEST); + grid.add(new GraphZoomScrollPane(vv1)); + grid.add(new GraphZoomScrollPane(vv2)); + panel.add(grid); + + content.add(panel); + + // create a GraphMouse for each view + DefaultModalGraphMouse gm0 = new DefaultModalGraphMouse(); + DefaultModalGraphMouse gm1 = new DefaultModalGraphMouse(); + DefaultModalGraphMouse gm2 = new DefaultModalGraphMouse(); + + vv0.setGraphMouse(gm0); + vv1.setGraphMouse(gm1); + vv2.setGraphMouse(gm2); + + // create zoom buttons for scaling the Function that is + // shared between the two models. + final ScalingControl scaler = new CrossoverScalingControl(); + + vv0.scaleToLayout(scaler); + vv1.scaleToLayout(scaler); + vv2.scaleToLayout(scaler); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv1, 1.1f, vv1.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv1, 1/1.1f, vv1.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv1, 1 / 1.1f, vv1.getCenter()); + } }); - - JPanel zoomPanel = new JPanel(new GridLayout(1,2)); - zoomPanel.setBorder(BorderFactory.createTitledBorder("Zoom")); - - JPanel modePanel = new JPanel(); - modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); - gm1.getModeComboBox().addItemListener(gm2.getModeListener()); - gm1.getModeComboBox().addItemListener(gm0.getModeListener()); - modePanel.add(gm1.getModeComboBox()); - - JPanel controls = new JPanel(); - zoomPanel.add(plus); - zoomPanel.add(minus); - controls.add(zoomPanel); - controls.add(modePanel); - content.add(controls, BorderLayout.SOUTH); - } - - public static void main(String[] args) { - JFrame f = new JFrame(); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - f.getContentPane().add(new MinimumSpanningTreeDemo()); - f.pack(); - f.setVisible(true); - } + + JPanel zoomPanel = new JPanel(new GridLayout(1, 2)); + zoomPanel.setBorder(BorderFactory.createTitledBorder("Zoom")); + + JPanel modePanel = new JPanel(); + modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); + gm1.getModeComboBox().addItemListener(gm2.getModeListener()); + gm1.getModeComboBox().addItemListener(gm0.getModeListener()); + modePanel.add(gm1.getModeComboBox()); + + JPanel controls = new JPanel(); + zoomPanel.add(plus); + zoomPanel.add(minus); + controls.add(zoomPanel); + controls.add(modePanel); + content.add(controls, BorderLayout.SOUTH); + } + + public static void main(String[] args) { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.getContentPane().add(new MinimumSpanningTreeDemo()); + f.pack(); + f.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/MultiViewDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/MultiViewDemo.java index fd4f2792..d6302fb9 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/MultiViewDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/MultiViewDemo.java @@ -1,38 +1,15 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.GridLayout; -import java.awt.Shape; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.InputEvent; -import java.awt.geom.Rectangle2D; - -import javax.swing.JApplet; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; - import com.google.common.base.Functions; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.FRLayout; import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; import edu.uci.ics.jung.graph.util.TestGraphs; @@ -56,280 +33,314 @@ import edu.uci.ics.jung.visualization.picking.MultiPickedState; import edu.uci.ics.jung.visualization.picking.PickedState; import edu.uci.ics.jung.visualization.picking.ShapePickSupport; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.geom.Rectangle2D; +import javax.swing.JApplet; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; /** - * Demonstrates 3 views of one graph in one model with one layout. - * Each view uses a different scaling graph mouse. - * - * @author Tom Nelson - * + * Demonstrates 3 views of one graph in one model with one layout. Each view uses a different + * scaling graph mouse. + * + * @author Tom Nelson */ @SuppressWarnings("serial") public class MultiViewDemo extends JApplet { - /** - * the graph - */ - Network graph; - - /** - * the visual components and renderers for the graph - */ - VisualizationViewer vv1; - VisualizationViewer vv2; - VisualizationViewer vv3; - - /** - * the normal Function - */ -// MutableTransformer Function; - - Dimension preferredSize = new Dimension(300,300); - - final String messageOne = "The mouse wheel will scale the model's layout when activated"+ - " in View 1. Since all three views share the same layout Function, all three views will"+ - " show the same scaling of the layout."; - - final String messageTwo = "The mouse wheel will scale the view when activated in"+ - " View 2. Since all three views share the same view Function, all three views will be affected."; - - final String messageThree = " The mouse wheel uses a 'crossover' feature in View 3."+ - " When the combined layout and view scale is greater than '1', the model's layout will be scaled."+ - " Since all three views share the same layout Function, all three views will show the same "+ - " scaling of the layout.\n When the combined scale is less than '1', the scaling function"+ - " crosses over to the view, and then, since all three views share the same view Function,"+ - " all three views will show the same scaling."; - - JTextArea textArea; - JScrollPane scrollPane; - - /** - * create an instance of a simple graph in two views with controls to - * demo the zoom features. - * - */ - public MultiViewDemo() { - - // create a simple graph for the demo - graph = TestGraphs.getOneComponentGraph(); - - // create one layout for the graph - FRLayout layout = new FRLayout(graph.asGraph()); - layout.setMaxIterations(1000); - - // create one model that all 3 views will share - VisualizationModel visualizationModel = - new DefaultVisualizationModel(graph, layout, preferredSize); - - // create 3 views that share the same model - vv1 = new VisualizationViewer(visualizationModel, preferredSize); - vv2 = new VisualizationViewer(visualizationModel, preferredSize); - vv3 = new VisualizationViewer(visualizationModel, preferredSize); - - vv1.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); - vv2.getRenderContext().setVertexShapeTransformer( - Functions.constant(new Rectangle2D.Float(-6,-6,12,12))); - - vv2.getRenderContext().setEdgeShapeTransformer(EdgeShape.quadCurve(graph)); - - vv3.getRenderContext().setEdgeShapeTransformer(EdgeShape.cubicCurve(graph)); - -// Function = vv1.getLayoutTransformer(); -// vv2.setLayoutTransformer(Function); -// vv3.setLayoutTransformer(Function); -// -// vv2.setViewTransformer(vv1.getViewTransformer()); -// vv3.setViewTransformer(vv1.getViewTransformer()); - - vv2.getRenderContext().setMultiLayerTransformer(vv1.getRenderContext().getMultiLayerTransformer()); - vv3.getRenderContext().setMultiLayerTransformer(vv1.getRenderContext().getMultiLayerTransformer()); - - vv1.getRenderContext().getMultiLayerTransformer().addChangeListener(vv1); - vv2.getRenderContext().getMultiLayerTransformer().addChangeListener(vv2); - vv3.getRenderContext().getMultiLayerTransformer().addChangeListener(vv3); - - - vv1.setBackground(Color.white); - vv2.setBackground(Color.white); - vv3.setBackground(Color.white); - - // create one pick support for all 3 views to share - NetworkElementAccessor pickSupport = new ShapePickSupport(vv1); - vv1.setPickSupport(pickSupport); - vv2.setPickSupport(pickSupport); - vv3.setPickSupport(pickSupport); - - // create one picked state for all 3 views to share - PickedState pes = new MultiPickedState(); - PickedState pvs = new MultiPickedState(); - vv1.setPickedVertexState(pvs); - vv2.setPickedVertexState(pvs); - vv3.setPickedVertexState(pvs); - vv1.setPickedEdgeState(pes); - vv2.setPickedEdgeState(pes); - vv3.setPickedEdgeState(pes); - - // set an edge paint function that shows picked edges - vv1.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(pes, Color.black, Color.red)); - vv2.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(pes, Color.black, Color.red)); - vv3.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(pes, Color.black, Color.red)); - vv1.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(pvs, Color.red, Color.yellow)); - vv2.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(pvs, Color.blue, Color.cyan)); - vv3.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(pvs, Color.red, Color.yellow)); - - // add default listener for ToolTips - vv1.setVertexToolTipTransformer(new ToStringLabeller()); - vv2.setVertexToolTipTransformer(new ToStringLabeller()); - vv3.setVertexToolTipTransformer(new ToStringLabeller()); - - Container content = getContentPane(); - JPanel panel = new JPanel(new GridLayout(1,0)); - - final JPanel p1 = new JPanel(new BorderLayout()); - final JPanel p2 = new JPanel(new BorderLayout()); - final JPanel p3 = new JPanel(new BorderLayout()); - - p1.add(new GraphZoomScrollPane(vv1)); - p2.add(new GraphZoomScrollPane(vv2)); - p3.add(new GraphZoomScrollPane(vv3)); - - JButton h1 = new JButton("?"); - h1.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - textArea.setText(messageOne); - JOptionPane.showMessageDialog(p1, scrollPane, - "View 1", JOptionPane.PLAIN_MESSAGE); - }}); - JButton h2 = new JButton("?"); - h2.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - textArea.setText(messageTwo); - JOptionPane.showMessageDialog(p2, scrollPane, - "View 2", JOptionPane.PLAIN_MESSAGE); - }}); - JButton h3 = new JButton("?"); - h3.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - textArea.setText(messageThree); - textArea.setCaretPosition(0); - JOptionPane.showMessageDialog(p3, scrollPane, - "View 3", JOptionPane.PLAIN_MESSAGE); - }}); - - // create a GraphMouse for each view - // each one has a different scaling plugin - DefaultModalGraphMouse gm1 = new DefaultModalGraphMouse() { - protected void loadPlugins() { - pickingPlugin = new PickingGraphMousePlugin(); - animatedPickingPlugin = new AnimatedPickingGraphMousePlugin(); - translatingPlugin = new TranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); - scalingPlugin = new ScalingGraphMousePlugin(new LayoutScalingControl(), 0); - rotatingPlugin = new RotatingGraphMousePlugin(); - shearingPlugin = new ShearingGraphMousePlugin(); - - add(scalingPlugin); - setMode(Mode.TRANSFORMING); - } + /** the graph */ + Network graph; + + /** the visual components and renderers for the graph */ + VisualizationViewer vv1; + + VisualizationViewer vv2; + VisualizationViewer vv3; + + /** the normal Function */ + // MutableTransformer Function; + + Dimension preferredSize = new Dimension(300, 300); + + final String messageOne = + "The mouse wheel will scale the model's layout when activated" + + " in View 1. Since all three views share the same layout Function, all three views will" + + " show the same scaling of the layout."; + + final String messageTwo = + "The mouse wheel will scale the view when activated in" + + " View 2. Since all three views share the same view Function, all three views will be affected."; + + final String messageThree = + " The mouse wheel uses a 'crossover' feature in View 3." + + " When the combined layout and view scale is greater than '1', the model's layout will be scaled." + + " Since all three views share the same layout Function, all three views will show the same " + + " scaling of the layout.\n When the combined scale is less than '1', the scaling function" + + " crosses over to the view, and then, since all three views share the same view Function," + + " all three views will show the same scaling."; + + JTextArea textArea; + JScrollPane scrollPane; + + /** create an instance of a simple graph in two views with controls to demo the zoom features. */ + public MultiViewDemo() { + + // create a simple graph for the demo + graph = TestGraphs.getOneComponentGraph(); + + // create one layout for the graph + FRLayout layout = new FRLayout(graph.asGraph()); + layout.setMaxIterations(1000); + + // create one model that all 3 views will share + VisualizationModel visualizationModel = + new DefaultVisualizationModel(graph, layout, preferredSize); + + // create 3 views that share the same model + vv1 = new VisualizationViewer(visualizationModel, preferredSize); + vv2 = new VisualizationViewer(visualizationModel, preferredSize); + vv3 = new VisualizationViewer(visualizationModel, preferredSize); + + vv1.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); + vv2.getRenderContext() + .setVertexShapeTransformer( + Functions.constant(new Rectangle2D.Float(-6, -6, 12, 12))); + + vv2.getRenderContext().setEdgeShapeTransformer(EdgeShape.quadCurve(graph)); + + vv3.getRenderContext().setEdgeShapeTransformer(EdgeShape.cubicCurve(graph)); + + // Function = vv1.getLayoutTransformer(); + // vv2.setLayoutTransformer(Function); + // vv3.setLayoutTransformer(Function); + // + // vv2.setViewTransformer(vv1.getViewTransformer()); + // vv3.setViewTransformer(vv1.getViewTransformer()); + + vv2.getRenderContext() + .setMultiLayerTransformer(vv1.getRenderContext().getMultiLayerTransformer()); + vv3.getRenderContext() + .setMultiLayerTransformer(vv1.getRenderContext().getMultiLayerTransformer()); + + vv1.getRenderContext().getMultiLayerTransformer().addChangeListener(vv1); + vv2.getRenderContext().getMultiLayerTransformer().addChangeListener(vv2); + vv3.getRenderContext().getMultiLayerTransformer().addChangeListener(vv3); + + vv1.setBackground(Color.white); + vv2.setBackground(Color.white); + vv3.setBackground(Color.white); + + // create one pick support for all 3 views to share + NetworkElementAccessor pickSupport = new ShapePickSupport(vv1); + vv1.setPickSupport(pickSupport); + vv2.setPickSupport(pickSupport); + vv3.setPickSupport(pickSupport); + + // create one picked state for all 3 views to share + PickedState pes = new MultiPickedState(); + PickedState pvs = new MultiPickedState(); + vv1.setPickedVertexState(pvs); + vv2.setPickedVertexState(pvs); + vv3.setPickedVertexState(pvs); + vv1.setPickedEdgeState(pes); + vv2.setPickedEdgeState(pes); + vv3.setPickedEdgeState(pes); + + // set an edge paint function that shows picked edges + vv1.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer(pes, Color.black, Color.red)); + vv2.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer(pes, Color.black, Color.red)); + vv3.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer(pes, Color.black, Color.red)); + vv1.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer(pvs, Color.red, Color.yellow)); + vv2.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer(pvs, Color.blue, Color.cyan)); + vv3.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer(pvs, Color.red, Color.yellow)); + + // add default listener for ToolTips + vv1.setVertexToolTipTransformer(new ToStringLabeller()); + vv2.setVertexToolTipTransformer(new ToStringLabeller()); + vv3.setVertexToolTipTransformer(new ToStringLabeller()); + + Container content = getContentPane(); + JPanel panel = new JPanel(new GridLayout(1, 0)); + + final JPanel p1 = new JPanel(new BorderLayout()); + final JPanel p2 = new JPanel(new BorderLayout()); + final JPanel p3 = new JPanel(new BorderLayout()); + + p1.add(new GraphZoomScrollPane(vv1)); + p2.add(new GraphZoomScrollPane(vv2)); + p3.add(new GraphZoomScrollPane(vv3)); + + JButton h1 = new JButton("?"); + h1.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + textArea.setText(messageOne); + JOptionPane.showMessageDialog(p1, scrollPane, "View 1", JOptionPane.PLAIN_MESSAGE); + } + }); + JButton h2 = new JButton("?"); + h2.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + textArea.setText(messageTwo); + JOptionPane.showMessageDialog(p2, scrollPane, "View 2", JOptionPane.PLAIN_MESSAGE); + } + }); + JButton h3 = new JButton("?"); + h3.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + textArea.setText(messageThree); + textArea.setCaretPosition(0); + JOptionPane.showMessageDialog(p3, scrollPane, "View 3", JOptionPane.PLAIN_MESSAGE); + } + }); + + // create a GraphMouse for each view + // each one has a different scaling plugin + DefaultModalGraphMouse gm1 = + new DefaultModalGraphMouse() { + protected void loadPlugins() { + pickingPlugin = new PickingGraphMousePlugin(); + animatedPickingPlugin = new AnimatedPickingGraphMousePlugin(); + translatingPlugin = new TranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); + scalingPlugin = new ScalingGraphMousePlugin(new LayoutScalingControl(), 0); + rotatingPlugin = new RotatingGraphMousePlugin(); + shearingPlugin = new ShearingGraphMousePlugin(); + + add(scalingPlugin); + setMode(Mode.TRANSFORMING); + } }; - DefaultModalGraphMouse gm2 = new DefaultModalGraphMouse() { - protected void loadPlugins() { - pickingPlugin = new PickingGraphMousePlugin(); - animatedPickingPlugin = new AnimatedPickingGraphMousePlugin(); - translatingPlugin = new TranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); - scalingPlugin = new ScalingGraphMousePlugin(new ViewScalingControl(), 0); - rotatingPlugin = new RotatingGraphMousePlugin(); - shearingPlugin = new ShearingGraphMousePlugin(); - - add(scalingPlugin); - setMode(Mode.TRANSFORMING); - } - + DefaultModalGraphMouse gm2 = + new DefaultModalGraphMouse() { + protected void loadPlugins() { + pickingPlugin = new PickingGraphMousePlugin(); + animatedPickingPlugin = new AnimatedPickingGraphMousePlugin(); + translatingPlugin = new TranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); + scalingPlugin = new ScalingGraphMousePlugin(new ViewScalingControl(), 0); + rotatingPlugin = new RotatingGraphMousePlugin(); + shearingPlugin = new ShearingGraphMousePlugin(); + + add(scalingPlugin); + setMode(Mode.TRANSFORMING); + } }; - DefaultModalGraphMouse gm3 = new DefaultModalGraphMouse() {}; - - vv1.setGraphMouse(gm1); - vv2.setGraphMouse(gm2); - vv3.setGraphMouse(gm3); - - vv1.setToolTipText("

        MouseWheel Scales Layout
        "); - vv2.setToolTipText("
        MouseWheel Scales View
        "); - vv3.setToolTipText("
        MouseWheel Scales Layout and

        crosses over to view

        ctrl+MouseWheel scales view

        "); - - vv1.addPostRenderPaintable(new BannerLabel(vv1, "View 1")); - vv2.addPostRenderPaintable(new BannerLabel(vv2, "View 2")); - vv3.addPostRenderPaintable(new BannerLabel(vv3, "View 3")); - - textArea = new JTextArea(6,30); - scrollPane = new JScrollPane(textArea, - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); - textArea.setLineWrap(true); - textArea.setWrapStyleWord(true); - textArea.setEditable(false); - - JPanel flow = new JPanel(); - flow.add(h1); - flow.add(gm1.getModeComboBox()); - p1.add(flow, BorderLayout.SOUTH); - flow = new JPanel(); - flow.add(h2); - flow.add(gm2.getModeComboBox()); - p2.add(flow, BorderLayout.SOUTH); - flow = new JPanel(); - flow.add(h3); - flow.add(gm3.getModeComboBox()); - p3.add(flow, BorderLayout.SOUTH); - - panel.add(p1); - panel.add(p2); - panel.add(p3); - content.add(panel); - + DefaultModalGraphMouse gm3 = new DefaultModalGraphMouse() {}; + + vv1.setGraphMouse(gm1); + vv2.setGraphMouse(gm2); + vv3.setGraphMouse(gm3); + + vv1.setToolTipText("
        MouseWheel Scales Layout
        "); + vv2.setToolTipText("
        MouseWheel Scales View
        "); + vv3.setToolTipText( + "
        MouseWheel Scales Layout and

        crosses over to view

        ctrl+MouseWheel scales view

        "); + + vv1.addPostRenderPaintable(new BannerLabel(vv1, "View 1")); + vv2.addPostRenderPaintable(new BannerLabel(vv2, "View 2")); + vv3.addPostRenderPaintable(new BannerLabel(vv3, "View 3")); + + textArea = new JTextArea(6, 30); + scrollPane = + new JScrollPane( + textArea, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + textArea.setLineWrap(true); + textArea.setWrapStyleWord(true); + textArea.setEditable(false); + JPanel flow = new JPanel(); + flow.add(h1); + flow.add(gm1.getModeComboBox()); + p1.add(flow, BorderLayout.SOUTH); + flow = new JPanel(); + flow.add(h2); + flow.add(gm2.getModeComboBox()); + p2.add(flow, BorderLayout.SOUTH); + flow = new JPanel(); + flow.add(h3); + flow.add(gm3.getModeComboBox()); + p3.add(flow, BorderLayout.SOUTH); + + panel.add(p1); + panel.add(p2); + panel.add(p3); + content.add(panel); + } + + class BannerLabel implements VisualizationViewer.Paintable { + int x; + int y; + Font font; + FontMetrics metrics; + int swidth; + int sheight; + String str; + VisualizationViewer vv; + + public BannerLabel(VisualizationViewer vv, String label) { + this.vv = vv; + this.str = label; } - - class BannerLabel implements VisualizationViewer.Paintable { - int x; - int y; - Font font; - FontMetrics metrics; - int swidth; - int sheight; - String str; - VisualizationViewer vv; - - public BannerLabel(VisualizationViewer vv, String label) { - this.vv = vv; - this.str = label; - } - - public void paint(Graphics g) { - Dimension d = vv.getSize(); - if(font == null) { - font = new Font(g.getFont().getName(), Font.BOLD, 30); - metrics = g.getFontMetrics(font); - swidth = metrics.stringWidth(str); - sheight = metrics.getMaxAscent()+metrics.getMaxDescent(); - x = (3*d.width/2-swidth)/2; - y = d.height-sheight; - } - g.setFont(font); - Color oldColor = g.getColor(); - g.setColor(Color.gray); - g.drawString(str, x, y); - g.setColor(oldColor); - } - public boolean useTransform() { - return false; - } + + public void paint(Graphics g) { + Dimension d = vv.getSize(); + if (font == null) { + font = new Font(g.getFont().getName(), Font.BOLD, 30); + metrics = g.getFontMetrics(font); + swidth = metrics.stringWidth(str); + sheight = metrics.getMaxAscent() + metrics.getMaxDescent(); + x = (3 * d.width / 2 - swidth) / 2; + y = d.height - sheight; + } + g.setFont(font); + Color oldColor = g.getColor(); + g.setColor(Color.gray); + g.drawString(str, x, y); + g.setColor(oldColor); } - public static void main(String[] args) { - JFrame f = new JFrame(); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - f.getContentPane().add(new MultiViewDemo()); - f.pack(); - f.setVisible(true); + public boolean useTransform() { + return false; } + } + + public static void main(String[] args) { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.getContentPane().add(new MultiViewDemo()); + f.pack(); + f.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/PluggableRendererDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/PluggableRendererDemo.java index b0a2caa9..228f6dbb 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/PluggableRendererDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/PluggableRendererDemo.java @@ -1,15 +1,49 @@ /* * Copyright (c) 2004, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * * * Created on Nov 7, 2004 */ package edu.uci.ics.jung.samples; +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.Network; +import com.google.common.graph.NetworkBuilder; +import edu.uci.ics.jung.algorithms.generators.random.BarabasiAlbertGenerator; +import edu.uci.ics.jung.algorithms.layout.FRLayout; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; +import edu.uci.ics.jung.algorithms.scoring.VoltageScorer; +import edu.uci.ics.jung.algorithms.scoring.util.VertexScoreTransformer; +import edu.uci.ics.jung.graph.util.Graphs; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.AbstractPopupGraphMousePlugin; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.decorators.AbstractVertexShapeTransformer; +import edu.uci.ics.jung.visualization.decorators.EdgeShape; +import edu.uci.ics.jung.visualization.decorators.GradientEdgePaintTransformer; +import edu.uci.ics.jung.visualization.decorators.NumberFormattingTransformer; +import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; +import edu.uci.ics.jung.visualization.picking.PickedInfo; +import edu.uci.ics.jung.visualization.picking.PickedState; +import edu.uci.ics.jung.visualization.renderers.BasicEdgeArrowRenderingSupport; +import edu.uci.ics.jung.visualization.renderers.CachingEdgeRenderer; +import edu.uci.ics.jung.visualization.renderers.CachingVertexRenderer; +import edu.uci.ics.jung.visualization.renderers.CenterEdgeArrowRenderingSupport; +import edu.uci.ics.jung.visualization.renderers.Renderer; +import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; @@ -33,7 +67,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - import javax.swing.AbstractAction; import javax.swing.AbstractButton; import javax.swing.BorderFactory; @@ -48,1099 +81,934 @@ import javax.swing.JPopupMenu; import javax.swing.JRadioButton; -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.base.Predicate; -import com.google.common.base.Supplier; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.Network; -import com.google.common.graph.NetworkBuilder; - -import edu.uci.ics.jung.algorithms.generators.random.BarabasiAlbertGenerator; -import edu.uci.ics.jung.algorithms.layout.FRLayout; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; -import edu.uci.ics.jung.algorithms.scoring.VoltageScorer; -import edu.uci.ics.jung.algorithms.scoring.util.VertexScoreTransformer; -import edu.uci.ics.jung.graph.util.Graphs; -import edu.uci.ics.jung.visualization.GraphZoomScrollPane; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.AbstractPopupGraphMousePlugin; -import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; -import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ScalingControl; -import edu.uci.ics.jung.visualization.decorators.AbstractVertexShapeTransformer; -import edu.uci.ics.jung.visualization.decorators.EdgeShape; -import edu.uci.ics.jung.visualization.decorators.GradientEdgePaintTransformer; -import edu.uci.ics.jung.visualization.decorators.NumberFormattingTransformer; -import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; -import edu.uci.ics.jung.visualization.picking.PickedInfo; -import edu.uci.ics.jung.visualization.picking.PickedState; -import edu.uci.ics.jung.visualization.renderers.BasicEdgeArrowRenderingSupport; -import edu.uci.ics.jung.visualization.renderers.CachingEdgeRenderer; -import edu.uci.ics.jung.visualization.renderers.CachingVertexRenderer; -import edu.uci.ics.jung.visualization.renderers.CenterEdgeArrowRenderingSupport; -import edu.uci.ics.jung.visualization.renderers.Renderer; -import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position; - - /** - * Shows off some of the capabilities of PluggableRenderer. - * This code provides examples of different ways to provide and - * change the various functions that provide property information + * Shows off some of the capabilities of PluggableRenderer. This code provides examples + * of different ways to provide and change the various functions that provide property information * to the renderer. - * - *

        This demo creates a random graph with random edge weights. - * It then runs VoltageRanker on this graph, using half - * of the "seed" vertices from the random graph generation as + * + *

        This demo creates a random graph with random edge weights. It then runs VoltageRanker + * on this graph, using half of the "seed" vertices from the random graph generation as * voltage sources, and half of them as voltage sinks. - * + * *

        What the controls do: + * *

          - *
        • Mouse controls: - *
            - *
          • If your mouse has a scroll wheel, scrolling forward zooms out and - * scrolling backward zooms in. - *
          • Left-clicking on a vertex or edge selects it, and unselects all others. - *
          • Middle-clicking on a vertex or edge toggles its selection state. - *
          • Right-clicking on a vertex brings up a pop-up menu that allows you to - * increase or decrease that vertex's transparency. - *
          • Left-clicking on the background allows you to drag the image around. - *
          • Hovering over a vertex tells you what its voltage is; hovering over an - * edge shows its identity; hovering over the background shows an informational - * message. - *
          - *
        • Vertex stuff: - *
            - *
          • "vertex seed coloring": if checked, the seed vertices are colored blue, - * and all other vertices are colored red. Otherwise, all vertices are colored - * a slightly transparent red (except the currently "picked" vertex, which is - * colored transparent purple). - *
          • "vertex selection stroke highlighting": if checked, the picked vertex - * and its neighbors are all drawn with heavy borders. Otherwise, all vertices - * are drawn with light borders. - *
          • "show vertex ranks (voltages)": if checked, each vertex is labeled with its - * calculated 'voltage'. Otherwise, vertices are unlabeled. - *
          • "vertex degree shapes": if checked, vertices are drawn with a polygon with - * number of sides proportional to its degree. Otherwise, vertices are drawn - * as ellipses. - *
          • "vertex voltage size": if checked, vertices are drawn with a size - * proportional to their voltage ranking. Otherwise, all vertices are drawn - * at the same size. - *
          • "vertex degree ratio stretch": if checked, vertices are drawn with an - * aspect ratio (height/width ratio) proportional to the ratio of their indegree to - * their outdegree. Otherwise, vertices are drawn with an aspect ratio of 1. - *
          • "filter vertices of degree < 4": if checked, does not display any vertices - * (or their incident edges) whose degree in the original graph is less than 4; - * otherwise, all vertices are drawn. - *
          - *
        • Edge stuff: - *
            - *
          • "edge shape": selects between lines, wedges, quadratic curves, and cubic curves - * for drawing edges. - *
          • "fill edge shapes": if checked, fills the edge shapes. This will have no effect - * if "line" is selected. - *
          • "edge paint": selects between solid colored edges, and gradient-painted edges. - * Gradient painted edges are darkest in the middle for undirected edges, and darkest - * at the destination for directed edges. - *
          • "show edges": only edges of the checked types are drawn. - *
          • "show arrows": only arrows whose edges are of the checked types are drawn. - *
          • "edge weight highlighting": if checked, edges with weight greater than - * a threshold value are drawn using thick solid lines, and other edges are drawn - * using thin gray dotted lines. (This combines edge stroke and paint.) Otherwise, - * all edges are drawn with thin solid lines. - *
          • "show edge weights": if checked, edges are labeled with their weights. - * Otherwise, edges are not labeled. - *
          - *
        • Miscellaneous (center panel) - *
            - *
          • "bold text": if checked, all vertex and edge labels are drawn using a - * boldface font. Otherwise, a normal-weight font is used. (Has no effect if - * no labels are currently visible.) - *
          • zoom controls: - *
              - *
            • "+" zooms in, "-" zooms out - *
            • "zoom at mouse (wheel only)": if checked, zooming (using the mouse - * scroll wheel) is centered on the location of the mouse pointer; otherwise, - * it is centered on the center of the visualization pane. - *
            + *
          • Mouse controls: + *
              + *
            • If your mouse has a scroll wheel, scrolling forward zooms out and scrolling backward + * zooms in. + *
            • Left-clicking on a vertex or edge selects it, and unselects all others. + *
            • Middle-clicking on a vertex or edge toggles its selection state. + *
            • Right-clicking on a vertex brings up a pop-up menu that allows you to increase or + * decrease that vertex's transparency. + *
            • Left-clicking on the background allows you to drag the image around. + *
            • Hovering over a vertex tells you what its voltage is; hovering over an edge shows its + * identity; hovering over the background shows an informational message. + *
            + *
          • Vertex stuff: + *
              + *
            • "vertex seed coloring": if checked, the seed vertices are colored blue, and all other + * vertices are colored red. Otherwise, all vertices are colored a slightly transparent + * red (except the currently "picked" vertex, which is colored transparent purple). + *
            • "vertex selection stroke highlighting": if checked, the picked vertex and its + * neighbors are all drawn with heavy borders. Otherwise, all vertices are drawn with + * light borders. + *
            • "show vertex ranks (voltages)": if checked, each vertex is labeled with its + * calculated 'voltage'. Otherwise, vertices are unlabeled. + *
            • "vertex degree shapes": if checked, vertices are drawn with a polygon with number of + * sides proportional to its degree. Otherwise, vertices are drawn as ellipses. + *
            • "vertex voltage size": if checked, vertices are drawn with a size proportional to + * their voltage ranking. Otherwise, all vertices are drawn at the same size. + *
            • "vertex degree ratio stretch": if checked, vertices are drawn with an aspect ratio + * (height/width ratio) proportional to the ratio of their indegree to their outdegree. + * Otherwise, vertices are drawn with an aspect ratio of 1. + *
            • "filter vertices of degree < 4": if checked, does not display any vertices (or + * their incident edges) whose degree in the original graph is less than 4; otherwise, + * all vertices are drawn. + *
            + *
          • Edge stuff: + *
              + *
            • "edge shape": selects between lines, wedges, quadratic curves, and cubic curves for + * drawing edges. + *
            • "fill edge shapes": if checked, fills the edge shapes. This will have no effect if + * "line" is selected. + *
            • "edge paint": selects between solid colored edges, and gradient-painted edges. + * Gradient painted edges are darkest in the middle for undirected edges, and darkest at + * the destination for directed edges. + *
            • "show edges": only edges of the checked types are drawn. + *
            • "show arrows": only arrows whose edges are of the checked types are drawn. + *
            • "edge weight highlighting": if checked, edges with weight greater than a threshold + * value are drawn using thick solid lines, and other edges are drawn using thin gray + * dotted lines. (This combines edge stroke and paint.) Otherwise, all edges are drawn + * with thin solid lines. + *
            • "show edge weights": if checked, edges are labeled with their weights. Otherwise, + * edges are not labeled. + *
            + *
          • Miscellaneous (center panel) + *
              + *
            • "bold text": if checked, all vertex and edge labels are drawn using a boldface font. + * Otherwise, a normal-weight font is used. (Has no effect if no labels are currently + * visible.) + *
            • zoom controls: + *
                + *
              • "+" zooms in, "-" zooms out + *
              • "zoom at mouse (wheel only)": if checked, zooming (using the mouse scroll + * wheel) is centered on the location of the mouse pointer; otherwise, it is + * centered on the center of the visualization pane. + *
              + *
            *
          - *
        - * - * + * * @author Danyel Fisher, Joshua O'Madadhain, Tom Nelson */ @SuppressWarnings("serial") -public class PluggableRendererDemo extends JApplet implements ActionListener -{ - protected JCheckBox v_color; - protected JCheckBox e_color; - protected JCheckBox v_stroke; - protected JCheckBox e_arrow_centered; - protected JCheckBox v_shape; - protected JCheckBox v_size; - protected JCheckBox v_aspect; - protected JCheckBox v_labels; - protected JRadioButton e_line; - protected JRadioButton e_bent; - protected JRadioButton e_wedge; - protected JRadioButton e_quad; - protected JRadioButton e_ortho; - protected JRadioButton e_cubic; - protected JCheckBox e_labels; - protected JCheckBox font; - protected JCheckBox e_filter_small; - protected JCheckBox e_show_u; - protected JCheckBox v_small; - protected JCheckBox zoom_at_mouse; - protected JCheckBox fill_edges; - - protected JRadioButton no_gradient; - protected JRadioButton gradient_relative; - - protected static final int GRADIENT_NONE = 0; - protected static final int GRADIENT_RELATIVE = 1; - protected static int gradient_level = GRADIENT_NONE; - - protected SeedFillColor seedFillColor; - protected SeedDrawColor seedDrawColor; - protected EdgeWeightStrokeFunction ewcs; - protected VertexStrokeHighlight vsh; - protected Function vs; - protected Function vs_none; - protected Function es; - protected Function es_none; - protected VertexFontTransformer vff; - protected EdgeFontTransformer eff; - protected VertexShapeSizeAspect vssa; - protected VertexDisplayPredicate show_vertex; - protected EdgeDisplayPredicate show_edge; - protected Predicate self_loop; - protected GradientPickedEdgePaintFunction edgeDrawPaint; - protected GradientPickedEdgePaintFunction edgeFillPaint; - protected final static Object VOLTAGE_KEY = "voltages"; - protected final static Object TRANSPARENCY = "transparency"; - - protected Map edge_weight = new HashMap(); - protected Function voltages; - protected Map transparency = new HashMap(); - - protected VisualizationViewer vv; - protected DefaultModalGraphMouse gm; - protected Set seedVertices = new HashSet(); - - private Network graph; - - public void start() - { - getContentPane().add( startFunction() ); - } - - public static void main(String[] s ) - { - JFrame jf = new JFrame(); - jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - JPanel jp = new PluggableRendererDemo().startFunction(); - jf.getContentPane().add(jp); - jf.pack(); - jf.setVisible(true); - } - - - public JPanel startFunction() { - this.graph = buildGraph(); - - Layout layout = new FRLayout(graph.asGraph()); - vv = new VisualizationViewer(graph, layout); - - vv.getRenderer().setVertexRenderer(new CachingVertexRenderer(vv)); - vv.getRenderer().setEdgeRenderer(new CachingEdgeRenderer(vv)); - - PickedState picked_state = vv.getPickedVertexState(); - - self_loop = (e) -> { - return Graphs.isSelfLoop(graph, e); +public class PluggableRendererDemo extends JApplet implements ActionListener { + protected JCheckBox v_color; + protected JCheckBox e_color; + protected JCheckBox v_stroke; + protected JCheckBox e_arrow_centered; + protected JCheckBox v_shape; + protected JCheckBox v_size; + protected JCheckBox v_aspect; + protected JCheckBox v_labels; + protected JRadioButton e_line; + protected JRadioButton e_bent; + protected JRadioButton e_wedge; + protected JRadioButton e_quad; + protected JRadioButton e_ortho; + protected JRadioButton e_cubic; + protected JCheckBox e_labels; + protected JCheckBox font; + protected JCheckBox e_filter_small; + protected JCheckBox e_show_u; + protected JCheckBox v_small; + protected JCheckBox zoom_at_mouse; + protected JCheckBox fill_edges; + + protected JRadioButton no_gradient; + protected JRadioButton gradient_relative; + + protected static final int GRADIENT_NONE = 0; + protected static final int GRADIENT_RELATIVE = 1; + protected static int gradient_level = GRADIENT_NONE; + + protected SeedFillColor seedFillColor; + protected SeedDrawColor seedDrawColor; + protected EdgeWeightStrokeFunction ewcs; + protected VertexStrokeHighlight vsh; + protected Function vs; + protected Function vs_none; + protected Function es; + protected Function es_none; + protected VertexFontTransformer vff; + protected EdgeFontTransformer eff; + protected VertexShapeSizeAspect vssa; + protected VertexDisplayPredicate show_vertex; + protected EdgeDisplayPredicate show_edge; + protected Predicate self_loop; + protected GradientPickedEdgePaintFunction edgeDrawPaint; + protected GradientPickedEdgePaintFunction edgeFillPaint; + protected static final Object VOLTAGE_KEY = "voltages"; + protected static final Object TRANSPARENCY = "transparency"; + + protected Map edge_weight = new HashMap(); + protected Function voltages; + protected Map transparency = new HashMap(); + + protected VisualizationViewer vv; + protected DefaultModalGraphMouse gm; + protected Set seedVertices = new HashSet(); + + private Network graph; + + public void start() { + getContentPane().add(startFunction()); + } + + public static void main(String[] s) { + JFrame jf = new JFrame(); + jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + JPanel jp = new PluggableRendererDemo().startFunction(); + jf.getContentPane().add(jp); + jf.pack(); + jf.setVisible(true); + } + + public JPanel startFunction() { + this.graph = buildGraph(); + + Layout layout = new FRLayout(graph.asGraph()); + vv = new VisualizationViewer(graph, layout); + + vv.getRenderer().setVertexRenderer(new CachingVertexRenderer(vv)); + vv.getRenderer().setEdgeRenderer(new CachingEdgeRenderer(vv)); + + PickedState picked_state = vv.getPickedVertexState(); + + self_loop = + (e) -> { + return Graphs.isSelfLoop(graph, e); }; - // create decorators - seedFillColor = new SeedFillColor(picked_state); - seedDrawColor = new SeedDrawColor(); - ewcs = - new EdgeWeightStrokeFunction(edge_weight); - vsh = new VertexStrokeHighlight(graph, picked_state); - vff = new VertexFontTransformer(); - eff = new EdgeFontTransformer(); - vs_none = Functions.constant(null); - es_none = Functions.constant(null); - vssa = new VertexShapeSizeAspect(graph, voltages); - show_vertex = new VertexDisplayPredicate(graph, false); - show_edge = new EdgeDisplayPredicate(edge_weight, false); - - // uses a gradient edge if unpicked, otherwise uses picked selection - edgeDrawPaint = - new GradientPickedEdgePaintFunction( - new PickableEdgePaintTransformer( - vv.getPickedEdgeState(),Color.black,Color.cyan), vv); - edgeFillPaint = - new GradientPickedEdgePaintFunction( - new PickableEdgePaintTransformer( - vv.getPickedEdgeState(),Color.black,Color.cyan), vv); - - vv.getRenderContext().setVertexFillPaintTransformer(seedFillColor); - vv.getRenderContext().setVertexDrawPaintTransformer(seedDrawColor); - vv.getRenderContext().setVertexStrokeTransformer(vsh); - vv.getRenderContext().setVertexLabelTransformer(vs_none); - vv.getRenderContext().setVertexFontTransformer(vff); - vv.getRenderContext().setVertexShapeTransformer(vssa); - vv.getRenderContext().setVertexIncludePredicate(show_vertex); - - vv.getRenderContext().setEdgeDrawPaintTransformer(edgeDrawPaint); - vv.getRenderContext().setEdgeLabelTransformer(es_none); - vv.getRenderContext().setEdgeFontTransformer(eff); - vv.getRenderContext().setEdgeStrokeTransformer(ewcs); - vv.getRenderContext().setEdgeIncludePredicate(show_edge); - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); - - vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); - vv.getRenderContext().setArrowDrawPaintTransformer(Functions.constant(Color.black)); - JPanel jp = new JPanel(); - jp.setLayout(new BorderLayout()); - - vv.setBackground(Color.white); - GraphZoomScrollPane scrollPane = new GraphZoomScrollPane(vv); - jp.add(scrollPane); - gm = new DefaultModalGraphMouse(); - vv.setGraphMouse(gm); - gm.add(new PopupGraphMousePlugin()); - - addBottomControls( jp ); - vssa.setScaling(true); - - vv.setVertexToolTipTransformer(new VoltageTips()); - vv.setToolTipText("
        Use the mouse wheel to zoom

        Click and Drag the mouse to pan

        Shift-click and Drag to Rotate

        "); - - return jp; + // create decorators + seedFillColor = new SeedFillColor(picked_state); + seedDrawColor = new SeedDrawColor(); + ewcs = new EdgeWeightStrokeFunction(edge_weight); + vsh = new VertexStrokeHighlight(graph, picked_state); + vff = new VertexFontTransformer(); + eff = new EdgeFontTransformer(); + vs_none = Functions.constant(null); + es_none = Functions.constant(null); + vssa = new VertexShapeSizeAspect(graph, voltages); + show_vertex = new VertexDisplayPredicate(graph, false); + show_edge = new EdgeDisplayPredicate(edge_weight, false); + + // uses a gradient edge if unpicked, otherwise uses picked selection + edgeDrawPaint = + new GradientPickedEdgePaintFunction( + new PickableEdgePaintTransformer( + vv.getPickedEdgeState(), Color.black, Color.cyan), + vv); + edgeFillPaint = + new GradientPickedEdgePaintFunction( + new PickableEdgePaintTransformer( + vv.getPickedEdgeState(), Color.black, Color.cyan), + vv); + + vv.getRenderContext().setVertexFillPaintTransformer(seedFillColor); + vv.getRenderContext().setVertexDrawPaintTransformer(seedDrawColor); + vv.getRenderContext().setVertexStrokeTransformer(vsh); + vv.getRenderContext().setVertexLabelTransformer(vs_none); + vv.getRenderContext().setVertexFontTransformer(vff); + vv.getRenderContext().setVertexShapeTransformer(vssa); + vv.getRenderContext().setVertexIncludePredicate(show_vertex); + + vv.getRenderContext().setEdgeDrawPaintTransformer(edgeDrawPaint); + vv.getRenderContext().setEdgeLabelTransformer(es_none); + vv.getRenderContext().setEdgeFontTransformer(eff); + vv.getRenderContext().setEdgeStrokeTransformer(ewcs); + vv.getRenderContext().setEdgeIncludePredicate(show_edge); + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); + + vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); + vv.getRenderContext().setArrowDrawPaintTransformer(Functions.constant(Color.black)); + JPanel jp = new JPanel(); + jp.setLayout(new BorderLayout()); + + vv.setBackground(Color.white); + GraphZoomScrollPane scrollPane = new GraphZoomScrollPane(vv); + jp.add(scrollPane); + gm = new DefaultModalGraphMouse(); + vv.setGraphMouse(gm); + gm.add(new PopupGraphMousePlugin()); + + addBottomControls(jp); + vssa.setScaling(true); + + vv.setVertexToolTipTransformer(new VoltageTips()); + vv.setToolTipText( + "
        Use the mouse wheel to zoom

        Click and Drag the mouse to pan

        Shift-click and Drag to Rotate

        "); + + return jp; + } + + /** + * Generates a random graph, runs VoltageRanker on it, and returns the resultant graph. + * + * @return the generated graph + */ + public Network buildGraph() { + Supplier vertexFactory = + new Supplier() { + int count; + + public Integer get() { + return count++; + } + }; + Supplier edgeFactory = + new Supplier() { + int count; + + public Number get() { + return count++; + } + }; + BarabasiAlbertGenerator generator = + new BarabasiAlbertGenerator( + NetworkBuilder.directed().allowsSelfLoops(true).allowsParallelEdges(true), + vertexFactory, + edgeFactory, + 4, + 3); + generator.evolveGraph(20); + MutableNetwork g = generator.get(); + for (Number e : g.edges()) { + edge_weight.put(e, Math.random()); } - - /** - * Generates a random graph, runs VoltageRanker on it, and - * returns the resultant graph. - * @return the generated graph - */ - public Network buildGraph() { - Supplier vertexFactory = - new Supplier() { - int count; - public Integer get() { - return count++; - }}; - Supplier edgeFactory = - new Supplier() { - int count; - public Number get() { - return count++; - }}; - BarabasiAlbertGenerator generator = - new BarabasiAlbertGenerator( - NetworkBuilder.directed().allowsSelfLoops(true).allowsParallelEdges(true), - vertexFactory, edgeFactory, 4, 3); - generator.evolveGraph(20); - MutableNetwork g = generator.get(); - for(Number e : g.edges()) { - edge_weight.put(e, Math.random()); - } - es = new NumberFormattingTransformer(Functions.forMap(edge_weight)); - - // collect the seeds used to define the random graph - seedVertices = generator.seedNodes(); - - if (seedVertices.size() < 2) - System.out.println("need at least 2 seeds (one source, one sink)"); - - // use these seeds as source and sink vertices, run VoltageRanker - boolean source = true; - Set sources = new HashSet(); - Set sinks = new HashSet(); - for(Integer v : seedVertices) - { - if (source) - sources.add(v); - else - sinks.add(v); - source = !source; - } - VoltageScorer voltage_scores = - new VoltageScorer(g, - Functions.forMap(edge_weight), sources, sinks); - voltage_scores.evaluate(); - voltages = new VertexScoreTransformer(voltage_scores); - vs = new NumberFormattingTransformer(voltages); - - Collection verts = g.nodes(); - - // assign a transparency value of 0.9 to all vertices - for(Integer v : verts) { - transparency.put(v, new Double(0.9)); - } + es = new NumberFormattingTransformer(Functions.forMap(edge_weight)); + + // collect the seeds used to define the random graph + seedVertices = generator.seedNodes(); + + if (seedVertices.size() < 2) System.out.println("need at least 2 seeds (one source, one sink)"); - // add a couple of self-loops (sanity check on rendering) - Integer v = verts.iterator().next(); - Number e = new Float(Math.random()); - edge_weight.put(e, e); - g.addEdge(v, v, e); - e = new Float(Math.random()); - edge_weight.put(e, e); - g.addEdge(v, v, e); - return g; + // use these seeds as source and sink vertices, run VoltageRanker + boolean source = true; + Set sources = new HashSet(); + Set sinks = new HashSet(); + for (Integer v : seedVertices) { + if (source) sources.add(v); + else sinks.add(v); + source = !source; } - - /** - * @param jp panel to which controls will be added - */ - protected void addBottomControls(final JPanel jp) - { - final JPanel control_panel = new JPanel(); - jp.add(control_panel, BorderLayout.EAST); - control_panel.setLayout(new BorderLayout()); - final Box vertex_panel = Box.createVerticalBox(); - vertex_panel.setBorder(BorderFactory.createTitledBorder("Vertices")); - final Box edge_panel = Box.createVerticalBox(); - edge_panel.setBorder(BorderFactory.createTitledBorder("Edges")); - final Box both_panel = Box.createVerticalBox(); - - control_panel.add(vertex_panel, BorderLayout.NORTH); - control_panel.add(edge_panel, BorderLayout.SOUTH); - control_panel.add(both_panel, BorderLayout.CENTER); - - // set up vertex controls - v_color = new JCheckBox("seed highlight"); - v_color.addActionListener(this); - v_stroke = new JCheckBox("stroke highlight on selection"); - v_stroke.addActionListener(this); - v_labels = new JCheckBox("show voltage values"); - v_labels.addActionListener(this); - v_shape = new JCheckBox("shape by degree"); - v_shape.addActionListener(this); - v_size = new JCheckBox("size by voltage"); - v_size.addActionListener(this); - v_size.setSelected(true); - v_aspect = new JCheckBox("stretch by degree ratio"); - v_aspect.addActionListener(this); - v_small = new JCheckBox("filter when degree < " + VertexDisplayPredicate.MIN_DEGREE); - v_small.addActionListener(this); - - vertex_panel.add(v_color); - vertex_panel.add(v_stroke); - vertex_panel.add(v_labels); - vertex_panel.add(v_shape); - vertex_panel.add(v_size); - vertex_panel.add(v_aspect); - vertex_panel.add(v_small); - - // set up edge controls - JPanel gradient_panel = new JPanel(new GridLayout(1, 0)); - gradient_panel.setBorder(BorderFactory.createTitledBorder("Edge paint")); - no_gradient = new JRadioButton("Solid color"); - no_gradient.addActionListener(this); - no_gradient.setSelected(true); -// gradient_absolute = new JRadioButton("Absolute gradient"); -// gradient_absolute.addActionListener(this); - gradient_relative = new JRadioButton("Gradient"); - gradient_relative.addActionListener(this); - ButtonGroup bg_grad = new ButtonGroup(); - bg_grad.add(no_gradient); - bg_grad.add(gradient_relative); - //bg_grad.add(gradient_absolute); - gradient_panel.add(no_gradient); - //gradientGrid.add(gradient_absolute); - gradient_panel.add(gradient_relative); - - JPanel shape_panel = new JPanel(new GridLayout(3,2)); - shape_panel.setBorder(BorderFactory.createTitledBorder("Edge shape")); - e_line = new JRadioButton("line"); - e_line.addActionListener(this); - e_line.setSelected(true); -// e_bent = new JRadioButton("bent line"); -// e_bent.addActionListener(this); - e_wedge = new JRadioButton("wedge"); - e_wedge.addActionListener(this); - e_quad = new JRadioButton("quad curve"); - e_quad.addActionListener(this); - e_cubic = new JRadioButton("cubic curve"); - e_cubic.addActionListener(this); - e_ortho = new JRadioButton("orthogonal"); - e_ortho.addActionListener(this); - ButtonGroup bg_shape = new ButtonGroup(); - bg_shape.add(e_line); -// bg.add(e_bent); - bg_shape.add(e_wedge); - bg_shape.add(e_quad); - bg_shape.add(e_ortho); - bg_shape.add(e_cubic); - shape_panel.add(e_line); -// shape_panel.add(e_bent); - shape_panel.add(e_wedge); - shape_panel.add(e_quad); - shape_panel.add(e_cubic); - shape_panel.add(e_ortho); - fill_edges = new JCheckBox("fill edge shapes"); - fill_edges.setSelected(false); - fill_edges.addActionListener(this); - shape_panel.add(fill_edges); - shape_panel.setOpaque(true); - e_color = new JCheckBox("highlight edge weights"); - e_color.addActionListener(this); - e_labels = new JCheckBox("show edge weight values"); - e_labels.addActionListener(this); - e_arrow_centered = new JCheckBox("centered"); - e_arrow_centered.addActionListener(this); - JPanel arrow_panel = new JPanel(new GridLayout(1,0)); - arrow_panel.setBorder(BorderFactory.createTitledBorder("Show arrows")); - arrow_panel.add(e_arrow_centered); - - e_filter_small = new JCheckBox("filter small-weight edges"); - e_filter_small.addActionListener(this); - e_filter_small.setSelected(true); - JPanel show_edge_panel = new JPanel(new GridLayout(1,0)); - show_edge_panel.setBorder(BorderFactory.createTitledBorder("Show edges")); - show_edge_panel.add(e_filter_small); - - shape_panel.setAlignmentX(Component.LEFT_ALIGNMENT); - edge_panel.add(shape_panel); - gradient_panel.setAlignmentX(Component.LEFT_ALIGNMENT); - edge_panel.add(gradient_panel); - show_edge_panel.setAlignmentX(Component.LEFT_ALIGNMENT); - edge_panel.add(show_edge_panel); - arrow_panel.setAlignmentX(Component.LEFT_ALIGNMENT); - edge_panel.add(arrow_panel); - - e_color.setAlignmentX(Component.LEFT_ALIGNMENT); - edge_panel.add(e_color); - e_labels.setAlignmentX(Component.LEFT_ALIGNMENT); - edge_panel.add(e_labels); - - // set up zoom controls - zoom_at_mouse = new JCheckBox("
        zoom at mouse

        (wheel only)

        "); - zoom_at_mouse.addActionListener(this); - zoom_at_mouse.setSelected(true); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + VoltageScorer voltage_scores = + new VoltageScorer(g, Functions.forMap(edge_weight), sources, sinks); + voltage_scores.evaluate(); + voltages = new VertexScoreTransformer(voltage_scores); + vs = new NumberFormattingTransformer(voltages); + + Collection verts = g.nodes(); + + // assign a transparency value of 0.9 to all vertices + for (Integer v : verts) { + transparency.put(v, new Double(0.9)); + } + + // add a couple of self-loops (sanity check on rendering) + Integer v = verts.iterator().next(); + Number e = new Float(Math.random()); + edge_weight.put(e, e); + g.addEdge(v, v, e); + e = new Float(Math.random()); + edge_weight.put(e, e); + g.addEdge(v, v, e); + return g; + } + + /** @param jp panel to which controls will be added */ + protected void addBottomControls(final JPanel jp) { + final JPanel control_panel = new JPanel(); + jp.add(control_panel, BorderLayout.EAST); + control_panel.setLayout(new BorderLayout()); + final Box vertex_panel = Box.createVerticalBox(); + vertex_panel.setBorder(BorderFactory.createTitledBorder("Vertices")); + final Box edge_panel = Box.createVerticalBox(); + edge_panel.setBorder(BorderFactory.createTitledBorder("Edges")); + final Box both_panel = Box.createVerticalBox(); + + control_panel.add(vertex_panel, BorderLayout.NORTH); + control_panel.add(edge_panel, BorderLayout.SOUTH); + control_panel.add(both_panel, BorderLayout.CENTER); + + // set up vertex controls + v_color = new JCheckBox("seed highlight"); + v_color.addActionListener(this); + v_stroke = new JCheckBox("stroke highlight on selection"); + v_stroke.addActionListener(this); + v_labels = new JCheckBox("show voltage values"); + v_labels.addActionListener(this); + v_shape = new JCheckBox("shape by degree"); + v_shape.addActionListener(this); + v_size = new JCheckBox("size by voltage"); + v_size.addActionListener(this); + v_size.setSelected(true); + v_aspect = new JCheckBox("stretch by degree ratio"); + v_aspect.addActionListener(this); + v_small = new JCheckBox("filter when degree < " + VertexDisplayPredicate.MIN_DEGREE); + v_small.addActionListener(this); + + vertex_panel.add(v_color); + vertex_panel.add(v_stroke); + vertex_panel.add(v_labels); + vertex_panel.add(v_shape); + vertex_panel.add(v_size); + vertex_panel.add(v_aspect); + vertex_panel.add(v_small); + + // set up edge controls + JPanel gradient_panel = new JPanel(new GridLayout(1, 0)); + gradient_panel.setBorder(BorderFactory.createTitledBorder("Edge paint")); + no_gradient = new JRadioButton("Solid color"); + no_gradient.addActionListener(this); + no_gradient.setSelected(true); + // gradient_absolute = new JRadioButton("Absolute gradient"); + // gradient_absolute.addActionListener(this); + gradient_relative = new JRadioButton("Gradient"); + gradient_relative.addActionListener(this); + ButtonGroup bg_grad = new ButtonGroup(); + bg_grad.add(no_gradient); + bg_grad.add(gradient_relative); + //bg_grad.add(gradient_absolute); + gradient_panel.add(no_gradient); + //gradientGrid.add(gradient_absolute); + gradient_panel.add(gradient_relative); + + JPanel shape_panel = new JPanel(new GridLayout(3, 2)); + shape_panel.setBorder(BorderFactory.createTitledBorder("Edge shape")); + e_line = new JRadioButton("line"); + e_line.addActionListener(this); + e_line.setSelected(true); + // e_bent = new JRadioButton("bent line"); + // e_bent.addActionListener(this); + e_wedge = new JRadioButton("wedge"); + e_wedge.addActionListener(this); + e_quad = new JRadioButton("quad curve"); + e_quad.addActionListener(this); + e_cubic = new JRadioButton("cubic curve"); + e_cubic.addActionListener(this); + e_ortho = new JRadioButton("orthogonal"); + e_ortho.addActionListener(this); + ButtonGroup bg_shape = new ButtonGroup(); + bg_shape.add(e_line); + // bg.add(e_bent); + bg_shape.add(e_wedge); + bg_shape.add(e_quad); + bg_shape.add(e_ortho); + bg_shape.add(e_cubic); + shape_panel.add(e_line); + // shape_panel.add(e_bent); + shape_panel.add(e_wedge); + shape_panel.add(e_quad); + shape_panel.add(e_cubic); + shape_panel.add(e_ortho); + fill_edges = new JCheckBox("fill edge shapes"); + fill_edges.setSelected(false); + fill_edges.addActionListener(this); + shape_panel.add(fill_edges); + shape_panel.setOpaque(true); + e_color = new JCheckBox("highlight edge weights"); + e_color.addActionListener(this); + e_labels = new JCheckBox("show edge weight values"); + e_labels.addActionListener(this); + e_arrow_centered = new JCheckBox("centered"); + e_arrow_centered.addActionListener(this); + JPanel arrow_panel = new JPanel(new GridLayout(1, 0)); + arrow_panel.setBorder(BorderFactory.createTitledBorder("Show arrows")); + arrow_panel.add(e_arrow_centered); + + e_filter_small = new JCheckBox("filter small-weight edges"); + e_filter_small.addActionListener(this); + e_filter_small.setSelected(true); + JPanel show_edge_panel = new JPanel(new GridLayout(1, 0)); + show_edge_panel.setBorder(BorderFactory.createTitledBorder("Show edges")); + show_edge_panel.add(e_filter_small); + + shape_panel.setAlignmentX(Component.LEFT_ALIGNMENT); + edge_panel.add(shape_panel); + gradient_panel.setAlignmentX(Component.LEFT_ALIGNMENT); + edge_panel.add(gradient_panel); + show_edge_panel.setAlignmentX(Component.LEFT_ALIGNMENT); + edge_panel.add(show_edge_panel); + arrow_panel.setAlignmentX(Component.LEFT_ALIGNMENT); + edge_panel.add(arrow_panel); + + e_color.setAlignmentX(Component.LEFT_ALIGNMENT); + edge_panel.add(e_color); + e_labels.setAlignmentX(Component.LEFT_ALIGNMENT); + edge_panel.add(e_labels); + + // set up zoom controls + zoom_at_mouse = new JCheckBox("
        zoom at mouse

        (wheel only)

        "); + zoom_at_mouse.addActionListener(this); + zoom_at_mouse.setSelected(true); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - JPanel zoomPanel = new JPanel(); - zoomPanel.setBorder(BorderFactory.createTitledBorder("Zoom")); - plus.setAlignmentX(Component.CENTER_ALIGNMENT); - zoomPanel.add(plus); - minus.setAlignmentX(Component.CENTER_ALIGNMENT); - zoomPanel.add(minus); - zoom_at_mouse.setAlignmentX(Component.CENTER_ALIGNMENT); - zoomPanel.add(zoom_at_mouse); - - JPanel fontPanel = new JPanel(); - // add font and zoom controls to center panel - font = new JCheckBox("bold text"); - font.addActionListener(this); - font.setAlignmentX(Component.CENTER_ALIGNMENT); - fontPanel.add(font); - - both_panel.add(zoomPanel); - both_panel.add(fontPanel); - - JComboBox modeBox = gm.getModeComboBox(); - modeBox.setAlignmentX(Component.CENTER_ALIGNMENT); - JPanel modePanel = new JPanel(new BorderLayout()) { - public Dimension getMaximumSize() { - return getPreferredSize(); - } + JPanel zoomPanel = new JPanel(); + zoomPanel.setBorder(BorderFactory.createTitledBorder("Zoom")); + plus.setAlignmentX(Component.CENTER_ALIGNMENT); + zoomPanel.add(plus); + minus.setAlignmentX(Component.CENTER_ALIGNMENT); + zoomPanel.add(minus); + zoom_at_mouse.setAlignmentX(Component.CENTER_ALIGNMENT); + zoomPanel.add(zoom_at_mouse); + + JPanel fontPanel = new JPanel(); + // add font and zoom controls to center panel + font = new JCheckBox("bold text"); + font.addActionListener(this); + font.setAlignmentX(Component.CENTER_ALIGNMENT); + fontPanel.add(font); + + both_panel.add(zoomPanel); + both_panel.add(fontPanel); + + JComboBox modeBox = gm.getModeComboBox(); + modeBox.setAlignmentX(Component.CENTER_ALIGNMENT); + JPanel modePanel = + new JPanel(new BorderLayout()) { + public Dimension getMaximumSize() { + return getPreferredSize(); + } }; - modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); - modePanel.add(modeBox); - JPanel comboGrid = new JPanel(new GridLayout(0,1)); - comboGrid.add(modePanel); - fontPanel.add(comboGrid); - - - JComboBox cb = new JComboBox(); - cb.addItem(Renderer.VertexLabel.Position.N); - cb.addItem(Renderer.VertexLabel.Position.NE); - cb.addItem(Renderer.VertexLabel.Position.E); - cb.addItem(Renderer.VertexLabel.Position.SE); - cb.addItem(Renderer.VertexLabel.Position.S); - cb.addItem(Renderer.VertexLabel.Position.SW); - cb.addItem(Renderer.VertexLabel.Position.W); - cb.addItem(Renderer.VertexLabel.Position.NW); - cb.addItem(Renderer.VertexLabel.Position.N); - cb.addItem(Renderer.VertexLabel.Position.CNTR); - cb.addItem(Renderer.VertexLabel.Position.AUTO); - cb.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - Renderer.VertexLabel.Position position = - (Renderer.VertexLabel.Position)e.getItem(); - vv.getRenderer().getVertexLabelRenderer().setPosition(position); - vv.repaint(); - }}); - cb.setSelectedItem(Renderer.VertexLabel.Position.SE); - JPanel positionPanel = new JPanel(); - positionPanel.setBorder(BorderFactory.createTitledBorder("Label Position")); - positionPanel.add(cb); - - comboGrid.add(positionPanel); + modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); + modePanel.add(modeBox); + JPanel comboGrid = new JPanel(new GridLayout(0, 1)); + comboGrid.add(modePanel); + fontPanel.add(comboGrid); + + JComboBox cb = new JComboBox(); + cb.addItem(Renderer.VertexLabel.Position.N); + cb.addItem(Renderer.VertexLabel.Position.NE); + cb.addItem(Renderer.VertexLabel.Position.E); + cb.addItem(Renderer.VertexLabel.Position.SE); + cb.addItem(Renderer.VertexLabel.Position.S); + cb.addItem(Renderer.VertexLabel.Position.SW); + cb.addItem(Renderer.VertexLabel.Position.W); + cb.addItem(Renderer.VertexLabel.Position.NW); + cb.addItem(Renderer.VertexLabel.Position.N); + cb.addItem(Renderer.VertexLabel.Position.CNTR); + cb.addItem(Renderer.VertexLabel.Position.AUTO); + cb.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + Renderer.VertexLabel.Position position = (Renderer.VertexLabel.Position) e.getItem(); + vv.getRenderer().getVertexLabelRenderer().setPosition(position); + vv.repaint(); + } + }); + cb.setSelectedItem(Renderer.VertexLabel.Position.SE); + JPanel positionPanel = new JPanel(); + positionPanel.setBorder(BorderFactory.createTitledBorder("Label Position")); + positionPanel.add(cb); + + comboGrid.add(positionPanel); + } + + public void actionPerformed(ActionEvent e) { + AbstractButton source = (AbstractButton) e.getSource(); + if (source == v_color) { + seedFillColor.setSeedColoring(source.isSelected()); + } else if (source == e_color) { + ewcs.setWeighted(source.isSelected()); + } else if (source == v_stroke) { + vsh.setHighlight(source.isSelected()); + } else if (source == v_labels) { + if (source.isSelected()) vv.getRenderContext().setVertexLabelTransformer(vs); + else vv.getRenderContext().setVertexLabelTransformer(vs_none); + } else if (source == e_labels) { + if (source.isSelected()) vv.getRenderContext().setEdgeLabelTransformer(es); + else vv.getRenderContext().setEdgeLabelTransformer(es_none); + } else if (source == e_arrow_centered) { + if (source.isSelected()) { + vv.getRenderer() + .getEdgeRenderer() + .setEdgeArrowRenderingSupport(new CenterEdgeArrowRenderingSupport()); + } else { + vv.getRenderer() + .getEdgeRenderer() + .setEdgeArrowRenderingSupport(new BasicEdgeArrowRenderingSupport()); + } + } else if (source == font) { + vff.setBold(source.isSelected()); + eff.setBold(source.isSelected()); + } else if (source == v_shape) { + vssa.useFunnyShapes(source.isSelected()); + } else if (source == v_size) { + vssa.setScaling(source.isSelected()); + } else if (source == v_aspect) { + vssa.setStretching(source.isSelected()); + } else if (source == e_line) { + if (source.isSelected()) { + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); + } + } else if (source == e_ortho) { + if (source.isSelected()) + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.orthogonal(graph)); + } else if (source == e_wedge) { + if (source.isSelected()) + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.wedge(graph, 10)); + } + // else if (source == e_bent) + // { + // if(source.isSelected()) + // { + // vv.getRenderContext().setEdgeShapeFunction(new EdgeShape.BentLine()); + // } + // } + else if (source == e_quad) { + if (source.isSelected()) { + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.quadCurve(graph)); + } + } else if (source == e_cubic) { + if (source.isSelected()) { + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.cubicCurve(graph)); + } + } else if (source == e_filter_small) { + show_edge.filterSmall(source.isSelected()); + } else if (source == v_small) { + show_vertex.filterSmall(source.isSelected()); + } else if (source == zoom_at_mouse) { + gm.setZoomAtMouse(source.isSelected()); + } else if (source == no_gradient) { + if (source.isSelected()) { + gradient_level = GRADIENT_NONE; + } + } else if (source == gradient_relative) { + if (source.isSelected()) { + gradient_level = GRADIENT_RELATIVE; + } + } else if (source == fill_edges) { + if (source.isSelected()) { + vv.getRenderContext().setEdgeFillPaintTransformer(edgeFillPaint); + } else { + vv.getRenderContext().setEdgeFillPaintTransformer(Functions.constant(null)); + } + } + vv.repaint(); + } + private final class SeedDrawColor implements Function { + public Paint apply(V v) { + return Color.BLACK; } - - public void actionPerformed(ActionEvent e) - { - AbstractButton source = (AbstractButton)e.getSource(); - if (source == v_color) - { - seedFillColor.setSeedColoring(source.isSelected()); - } - else if (source == e_color) - { - ewcs.setWeighted(source.isSelected()); - } - else if (source == v_stroke) - { - vsh.setHighlight(source.isSelected()); - } - else if (source == v_labels) - { - if (source.isSelected()) - vv.getRenderContext().setVertexLabelTransformer(vs); - else - vv.getRenderContext().setVertexLabelTransformer(vs_none); - } - else if (source == e_labels) - { - if (source.isSelected()) - vv.getRenderContext().setEdgeLabelTransformer(es); - else - vv.getRenderContext().setEdgeLabelTransformer(es_none); - } - else if (source == e_arrow_centered) - { - if(source.isSelected()) - { - vv.getRenderer().getEdgeRenderer().setEdgeArrowRenderingSupport( - new CenterEdgeArrowRenderingSupport()); - } - else - { - vv.getRenderer().getEdgeRenderer().setEdgeArrowRenderingSupport( - new BasicEdgeArrowRenderingSupport()); - } - } - else if (source == font) - { - vff.setBold(source.isSelected()); - eff.setBold(source.isSelected()); - } - else if (source == v_shape) - { - vssa.useFunnyShapes(source.isSelected()); - } - else if (source == v_size) - { - vssa.setScaling(source.isSelected()); - } - else if (source == v_aspect) - { - vssa.setStretching(source.isSelected()); - } - else if (source == e_line) - { - if(source.isSelected()) - { - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); - } - } - else if (source == e_ortho) - { - if (source.isSelected()) - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.orthogonal(graph)); - } - else if (source == e_wedge) - { - if (source.isSelected()) - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.wedge(graph, 10)); - } -// else if (source == e_bent) -// { -// if(source.isSelected()) -// { -// vv.getRenderContext().setEdgeShapeFunction(new EdgeShape.BentLine()); -// } -// } - else if (source == e_quad) - { - if(source.isSelected()) - { - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.quadCurve(graph)); - } - } - else if (source == e_cubic) - { - if(source.isSelected()) - { - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.cubicCurve(graph)); - } - } - else if (source == e_filter_small) - { - show_edge.filterSmall(source.isSelected()); - } - else if (source == v_small) - { - show_vertex.filterSmall(source.isSelected()); - } - else if(source == zoom_at_mouse) - { - gm.setZoomAtMouse(source.isSelected()); - } - else if (source == no_gradient) { - if (source.isSelected()) { - gradient_level = GRADIENT_NONE; - } - } - else if (source == gradient_relative) { - if (source.isSelected()) { - gradient_level = GRADIENT_RELATIVE; - } - } - else if (source == fill_edges) - { - if(source.isSelected()) { - vv.getRenderContext().setEdgeFillPaintTransformer( edgeFillPaint ); - } else { - vv.getRenderContext().setEdgeFillPaintTransformer( Functions.constant(null) ); - } - } - vv.repaint(); + } + + private final class SeedFillColor implements Function { + protected PickedInfo pi; + protected static final float dark_value = 0.8f; + protected static final float light_value = 0.2f; + protected boolean seed_coloring; + + public SeedFillColor(PickedInfo pi) { + this.pi = pi; + seed_coloring = false; } - - private final class SeedDrawColor implements Function - { - public Paint apply(V v) - { - return Color.BLACK; - } + + public void setSeedColoring(boolean b) { + this.seed_coloring = b; + } + + public Paint apply(V v) { + float alpha = transparency.get(v).floatValue(); + if (pi.isPicked(v)) { + return new Color(1f, 1f, 0, alpha); + } else { + if (seed_coloring && seedVertices.contains(v)) { + Color dark = new Color(0, 0, dark_value, alpha); + Color light = new Color(0, 0, light_value, alpha); + return new GradientPaint(0, 0, dark, 10, 0, light, true); + } else return new Color(1f, 0, 0, alpha); + } + } + } + + private static final class EdgeWeightStrokeFunction implements Function { + protected static final Stroke basic = new BasicStroke(1); + protected static final Stroke heavy = new BasicStroke(2); + protected static final Stroke dotted = RenderContext.DOTTED; + + protected boolean weighted = false; + protected Map edge_weight; + + public EdgeWeightStrokeFunction(Map edge_weight) { + this.edge_weight = edge_weight; + } + + public void setWeighted(boolean weighted) { + this.weighted = weighted; } - - private final class SeedFillColor implements Function - { - protected PickedInfo pi; - protected final static float dark_value = 0.8f; - protected final static float light_value = 0.2f; - protected boolean seed_coloring; - - public SeedFillColor(PickedInfo pi) - { - this.pi = pi; - seed_coloring = false; - } - public void setSeedColoring(boolean b) - { - this.seed_coloring = b; + public Stroke apply(E e) { + if (weighted) { + if (drawHeavy(e)) return heavy; + else return dotted; + } else return basic; + } + + protected boolean drawHeavy(E e) { + double value = edge_weight.get(e).doubleValue(); + if (value > 0.7) return true; + else return false; + } + } + + private static final class VertexStrokeHighlight implements Function { + protected boolean highlight = false; + protected Stroke heavy = new BasicStroke(5); + protected Stroke medium = new BasicStroke(3); + protected Stroke light = new BasicStroke(1); + protected PickedInfo pi; + protected Network graph; + + public VertexStrokeHighlight(Network graph, PickedInfo pi) { + this.graph = graph; + this.pi = pi; + } + + public void setHighlight(boolean highlight) { + this.highlight = highlight; + } + + public Stroke apply(V v) { + if (highlight) { + if (pi.isPicked(v)) return heavy; + else { + for (V w : graph.adjacentNodes(v)) { + if (pi.isPicked(w)) return medium; + } + return light; } - - public Paint apply(V v) - { - float alpha = transparency.get(v).floatValue(); - if (pi.isPicked(v)) - { - return new Color(1f, 1f, 0, alpha); + } else return light; + } + } + + private static final class VertexFontTransformer implements Function { + protected boolean bold = false; + Font f = new Font("Helvetica", Font.PLAIN, 12); + Font b = new Font("Helvetica", Font.BOLD, 12); + + public void setBold(boolean bold) { + this.bold = bold; + } + + public Font apply(V v) { + if (bold) return b; + else return f; + } + } + + private static final class EdgeFontTransformer implements Function { + protected boolean bold = false; + Font f = new Font("Helvetica", Font.PLAIN, 12); + Font b = new Font("Helvetica", Font.BOLD, 12); + + public void setBold(boolean bold) { + this.bold = bold; + } + + public Font apply(E e) { + if (bold) return b; + else return f; + } + } + + private static final class VertexDisplayPredicate implements Predicate { + protected boolean filter_small; + protected static final int MIN_DEGREE = 4; + protected final Network graph; + + public VertexDisplayPredicate(Network graph, boolean filter) { + this.graph = graph; + this.filter_small = filter; + } + + public void filterSmall(boolean b) { + filter_small = b; + } + + public boolean apply(V node) { + return filter_small ? graph.degree(node) >= MIN_DEGREE : true; + } + } + + private static final class EdgeDisplayPredicate implements Predicate { + protected boolean filter_small; + protected final double MIN_WEIGHT = 0.5; + protected final Map edge_weights; + + public EdgeDisplayPredicate(Map edge_weights, boolean filter) { + this.edge_weights = edge_weights; + this.filter_small = filter; + } + + public void filterSmall(boolean b) { + filter_small = b; + } + + public boolean apply(E edge) { + return filter_small ? edge_weights.get(edge).doubleValue() >= MIN_WEIGHT : true; + } + } + + /** + * Controls the shape, size, and aspect ratio for each vertex. + * + * @author Joshua O'Madadhain + */ + private static final class VertexShapeSizeAspect extends AbstractVertexShapeTransformer + implements Function { + + protected boolean stretch = false; + protected boolean scale = false; + protected boolean funny_shapes = false; + protected Function voltages; + protected Network graph; + // protected AffineTransform scaleTransform = new AffineTransform(); + + public VertexShapeSizeAspect(Network graphIn, Function voltagesIn) { + this.graph = graphIn; + this.voltages = voltagesIn; + setSizeTransformer( + new Function() { + + public Integer apply(V v) { + if (scale) return (int) (voltages.apply(v) * 30) + 20; + else return 20; } - else - { - if (seed_coloring && seedVertices.contains(v)) - { - Color dark = new Color(0, 0, dark_value, alpha); - Color light = new Color(0, 0, light_value, alpha); - return new GradientPaint( 0, 0, dark, 10, 0, light, true); - } - else - return new Color(1f, 0, 0, alpha); + }); + setAspectRatioTransformer( + new Function() { + + public Float apply(V v) { + if (stretch) { + return (float) (graph.inDegree(v) + 1) / (graph.outDegree(v) + 1); + } else { + return 1.0f; + } } - - } + }); } - private final static class EdgeWeightStrokeFunction - implements Function - { - protected static final Stroke basic = new BasicStroke(1); - protected static final Stroke heavy = new BasicStroke(2); - protected static final Stroke dotted = RenderContext.DOTTED; - - protected boolean weighted = false; - protected Map edge_weight; - - public EdgeWeightStrokeFunction(Map edge_weight) - { - this.edge_weight = edge_weight; - } - - public void setWeighted(boolean weighted) - { - this.weighted = weighted; - } - - public Stroke apply(E e) - { - if (weighted) - { - if (drawHeavy(e)) - return heavy; - else - return dotted; - } - else - return basic; - } - - protected boolean drawHeavy(E e) - { - double value = edge_weight.get(e).doubleValue(); - if (value > 0.7) - return true; - else - return false; - } - + public void setStretching(boolean stretch) { + this.stretch = stretch; } - - private final static class VertexStrokeHighlight implements - Function - { - protected boolean highlight = false; - protected Stroke heavy = new BasicStroke(5); - protected Stroke medium = new BasicStroke(3); - protected Stroke light = new BasicStroke(1); - protected PickedInfo pi; - protected Network graph; - - public VertexStrokeHighlight(Network graph, PickedInfo pi) - { - this.graph = graph; - this.pi = pi; - } - - public void setHighlight(boolean highlight) - { - this.highlight = highlight; - } - - public Stroke apply(V v) - { - if (highlight) - { - if (pi.isPicked(v)) - return heavy; - else - { - for(V w : graph.adjacentNodes(v)) { - if (pi.isPicked(w)) - return medium; - } - return light; - } - } - else - return light; - } + public void setScaling(boolean scale) { + this.scale = scale; } - - private final static class VertexFontTransformer - implements Function - { - protected boolean bold = false; - Font f = new Font("Helvetica", Font.PLAIN, 12); - Font b = new Font("Helvetica", Font.BOLD, 12); - - public void setBold(boolean bold) - { - this.bold = bold; - } - - public Font apply(V v) - { - if (bold) - return b; - else - return f; - } + + public void useFunnyShapes(boolean use) { + this.funny_shapes = use; } - private final static class EdgeFontTransformer implements Function { - protected boolean bold = false; - Font f = new Font("Helvetica", Font.PLAIN, 12); - Font b = new Font("Helvetica", Font.BOLD, 12); - - public void setBold(boolean bold) { - this.bold = bold; - } - - public Font apply(E e) { - if (bold) - return b; - else - return f; - } - } - - private final static class VertexDisplayPredicate implements Predicate - { - protected boolean filter_small; - protected final static int MIN_DEGREE = 4; - protected final Network graph; - - public VertexDisplayPredicate(Network graph, boolean filter) - { - this.graph = graph; - this.filter_small = filter; - } - - public void filterSmall(boolean b) - { - filter_small = b; - } - - public boolean apply(V node) { - return filter_small - ? graph.degree(node) >= MIN_DEGREE - : true; - } + public Shape apply(V v) { + if (funny_shapes) { + if (graph.degree(v) < 5) { + int sides = Math.max(graph.degree(v), 3); + return factory.getRegularPolygon(v, sides); + } else return factory.getRegularStar(v, graph.degree(v)); + } else return factory.getEllipse(v); } - - private final static class EdgeDisplayPredicate implements Predicate - { - protected boolean filter_small; - protected final double MIN_WEIGHT = 0.5; - protected final Map edge_weights; - - public EdgeDisplayPredicate(Map edge_weights, boolean filter) - { - this.edge_weights = edge_weights; - this.filter_small = filter; - } - - public void filterSmall(boolean b) - { - filter_small = b; - } - - public boolean apply(E edge) { - return filter_small - ? edge_weights.get(edge).doubleValue() >= MIN_WEIGHT - : true; - } + } + + /** a GraphMousePlugin that offers popup menu support */ + protected class PopupGraphMousePlugin extends AbstractPopupGraphMousePlugin + implements MouseListener { + + public PopupGraphMousePlugin() { + this(MouseEvent.BUTTON3_MASK); } - - /** - * Controls the shape, size, and aspect ratio for each vertex. - * - * @author Joshua O'Madadhain - */ - private final static class VertexShapeSizeAspect - extends AbstractVertexShapeTransformer - implements Function { - - protected boolean stretch = false; - protected boolean scale = false; - protected boolean funny_shapes = false; - protected Function voltages; - protected Network graph; -// protected AffineTransform scaleTransform = new AffineTransform(); - - public VertexShapeSizeAspect(Network graphIn, Function voltagesIn) - { - this.graph = graphIn; - this.voltages = voltagesIn; - setSizeTransformer(new Function() { - - public Integer apply(V v) { - if (scale) - return (int)(voltages.apply(v) * 30) + 20; - else - return 20; - - }}); - setAspectRatioTransformer(new Function() { - - public Float apply(V v) { - if (stretch) { - return (float)(graph.inDegree(v) + 1) / - (graph.outDegree(v) + 1); - } else { - return 1.0f; - } - }}); - } - - public void setStretching(boolean stretch) - { - this.stretch = stretch; - } - - public void setScaling(boolean scale) - { - this.scale = scale; - } - - public void useFunnyShapes(boolean use) - { - this.funny_shapes = use; - } - - public Shape apply(V v) - { - if (funny_shapes) - { - if (graph.degree(v) < 5) - { - int sides = Math.max(graph.degree(v), 3); - return factory.getRegularPolygon(v, sides); - } - else - return factory.getRegularStar(v, graph.degree(v)); - } - else - return factory.getEllipse(v); - } + + public PopupGraphMousePlugin(int modifiers) { + super(modifiers); } - + /** - * a GraphMousePlugin that offers popup - * menu support + * If this event is over a Vertex, pop up a menu to allow the user to increase/decrease the + * voltage attribute of this Vertex + * + * @param e the event to be handled */ - protected class PopupGraphMousePlugin extends AbstractPopupGraphMousePlugin - implements MouseListener { - - public PopupGraphMousePlugin() { - this(MouseEvent.BUTTON3_MASK); - } - public PopupGraphMousePlugin(int modifiers) { - super(modifiers); - } - - /** - * If this event is over a Vertex, pop up a menu to - * allow the user to increase/decrease the voltage - * attribute of this Vertex - * @param e the event to be handled - */ - @SuppressWarnings("unchecked") - protected void handlePopup(MouseEvent e) { - final VisualizationViewer vv = - (VisualizationViewer)e.getSource(); - Point2D p = e.getPoint();//vv.getRenderContext().getBasicTransformer().inverseViewTransform(e.getPoint()); - - NetworkElementAccessor pickSupport = vv.getPickSupport(); - if(pickSupport != null) { - final Integer v = pickSupport.getNode(p.getX(), p.getY()); - if(v != null) { - JPopupMenu popup = new JPopupMenu(); - popup.add(new AbstractAction("Decrease Transparency") { - public void actionPerformed(ActionEvent e) { - Double value = Math.min(1, - transparency.get(v).doubleValue()+0.1); - transparency.put(v, value); -// transparency.put(v, )transparency.get(v); -// MutableDouble value = (MutableDouble)transparency.getNumber(v); -// value.setDoubleValue(Math.min(1, value.doubleValue() + 0.1)); - vv.repaint(); - } - }); - popup.add(new AbstractAction("Increase Transparency"){ - public void actionPerformed(ActionEvent e) { - Double value = Math.max(0, - transparency.get(v).doubleValue()-0.1); - transparency.put(v, value); -// MutableDouble value = (MutableDouble)transparency.getNumber(v); -// value.setDoubleValue(Math.max(0, value.doubleValue() - 0.1)); - vv.repaint(); - } - }); - popup.show(vv, e.getX(), e.getY()); - } else { - final Number edge = pickSupport.getEdge(p.getX(), p.getY()); - if(edge != null) { - JPopupMenu popup = new JPopupMenu(); - popup.add(new AbstractAction(edge.toString()) { - public void actionPerformed(ActionEvent e) { - System.err.println("got "+edge); - } - }); - popup.show(vv, e.getX(), e.getY()); - - } + @SuppressWarnings("unchecked") + protected void handlePopup(MouseEvent e) { + final VisualizationViewer vv = + (VisualizationViewer) e.getSource(); + Point2D p = + e + .getPoint(); //vv.getRenderContext().getBasicTransformer().inverseViewTransform(e.getPoint()); + + NetworkElementAccessor pickSupport = vv.getPickSupport(); + if (pickSupport != null) { + final Integer v = pickSupport.getNode(p.getX(), p.getY()); + if (v != null) { + JPopupMenu popup = new JPopupMenu(); + popup.add( + new AbstractAction("Decrease Transparency") { + public void actionPerformed(ActionEvent e) { + Double value = Math.min(1, transparency.get(v).doubleValue() + 0.1); + transparency.put(v, value); + // transparency.put(v, )transparency.get(v); + // MutableDouble value = (MutableDouble)transparency.getNumber(v); + // value.setDoubleValue(Math.min(1, value.doubleValue() + 0.1)); + vv.repaint(); } - } - } + }); + popup.add( + new AbstractAction("Increase Transparency") { + public void actionPerformed(ActionEvent e) { + Double value = Math.max(0, transparency.get(v).doubleValue() - 0.1); + transparency.put(v, value); + // MutableDouble value = (MutableDouble)transparency.getNumber(v); + // value.setDoubleValue(Math.max(0, value.doubleValue() - 0.1)); + vv.repaint(); + } + }); + popup.show(vv, e.getX(), e.getY()); + } else { + final Number edge = pickSupport.getEdge(p.getX(), p.getY()); + if (edge != null) { + JPopupMenu popup = new JPopupMenu(); + popup.add( + new AbstractAction(edge.toString()) { + public void actionPerformed(ActionEvent e) { + System.err.println("got " + edge); + } + }); + popup.show(vv, e.getX(), e.getY()); + } + } + } } - - public class VoltageTips - implements Function { - - public String apply(Integer vertex) { - return "Voltage:"+voltages.apply(vertex); - } + } + + public class VoltageTips implements Function { + + public String apply(Integer vertex) { + return "Voltage:" + voltages.apply(vertex); } - - public class GradientPickedEdgePaintFunction extends GradientEdgePaintTransformer - { - private Function defaultFunc; - protected boolean fill_edge = false; - private VisualizationViewer vv; - - public GradientPickedEdgePaintFunction(Function defaultEdgePaintFunction, - VisualizationViewer vv) - { - super(Color.WHITE, Color.BLACK, vv); - this.vv = vv; - this.defaultFunc = defaultEdgePaintFunction; - } - - public void useFill(boolean b) - { - fill_edge = b; - } - - public Paint apply(E e) { - if (gradient_level == GRADIENT_NONE) { - return defaultFunc.apply(e); - } else { - return super.apply(e); - } - } - - protected Color getColor2(E e) - { - return this.vv.getPickedEdgeState().isPicked(e)? Color.CYAN : c2; - } - -// public Paint getFillPaint(E e) -// { -// if (selfLoop.evaluateEdge(vv.getGraphLayout().getGraph(), e) || !fill_edge) -// return null; -// else -// return getDrawPaint(e); -// } - + } + + public class GradientPickedEdgePaintFunction extends GradientEdgePaintTransformer { + private Function defaultFunc; + protected boolean fill_edge = false; + private VisualizationViewer vv; + + public GradientPickedEdgePaintFunction( + Function defaultEdgePaintFunction, VisualizationViewer vv) { + super(Color.WHITE, Color.BLACK, vv); + this.vv = vv; + this.defaultFunc = defaultEdgePaintFunction; + } + + public void useFill(boolean b) { + fill_edge = b; + } + + public Paint apply(E e) { + if (gradient_level == GRADIENT_NONE) { + return defaultFunc.apply(e); + } else { + return super.apply(e); + } } -} + protected Color getColor2(E e) { + return this.vv.getPickedEdgeState().isPicked(e) ? Color.CYAN : c2; + } + + // public Paint getFillPaint(E e) + // { + // if (selfLoop.evaluateEdge(vv.getGraphLayout().getGraph(), e) || !fill_edge) + // return null; + // else + // return getDrawPaint(e); + // } + + } +} diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/RadialTreeLensDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/RadialTreeLensDemo.java index 144041f2..f6ba53ff 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/RadialTreeLensDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/RadialTreeLensDemo.java @@ -1,13 +1,37 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; +import edu.uci.ics.jung.algorithms.layout.PolarPoint; +import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout; +import edu.uci.ics.jung.algorithms.layout.TreeLayout; +import edu.uci.ics.jung.graph.CTreeNetwork; +import edu.uci.ics.jung.graph.MutableCTreeNetwork; +import edu.uci.ics.jung.graph.TreeNetworkBuilder; +import edu.uci.ics.jung.visualization.DefaultVisualizationModel; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationModel; +import edu.uci.ics.jung.visualization.VisualizationServer; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ModalLensGraphMouse; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.decorators.EdgeShape; +import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; +import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; +import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; +import edu.uci.ics.jung.visualization.picking.PickedState; +import edu.uci.ics.jung.visualization.transform.LensSupport; +import edu.uci.ics.jung.visualization.transform.shape.HyperbolicShapeTransformer; +import edu.uci.ics.jung.visualization.transform.shape.ViewLensSupport; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; @@ -26,7 +50,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - import javax.swing.BorderFactory; import javax.swing.JApplet; import javax.swing.JButton; @@ -35,240 +58,213 @@ import javax.swing.JPanel; import javax.swing.JRadioButton; -import edu.uci.ics.jung.algorithms.layout.PolarPoint; -import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout; -import edu.uci.ics.jung.algorithms.layout.TreeLayout; -import edu.uci.ics.jung.graph.CTreeNetwork; -import edu.uci.ics.jung.graph.MutableCTreeNetwork; -import edu.uci.ics.jung.graph.TreeNetworkBuilder; -import edu.uci.ics.jung.visualization.DefaultVisualizationModel; -import edu.uci.ics.jung.visualization.GraphZoomScrollPane; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationModel; -import edu.uci.ics.jung.visualization.VisualizationServer; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; -import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ModalLensGraphMouse; -import edu.uci.ics.jung.visualization.control.ScalingControl; -import edu.uci.ics.jung.visualization.decorators.EdgeShape; -import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; -import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; -import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; -import edu.uci.ics.jung.visualization.picking.PickedState; -import edu.uci.ics.jung.visualization.transform.LensSupport; -import edu.uci.ics.jung.visualization.transform.shape.HyperbolicShapeTransformer; -import edu.uci.ics.jung.visualization.transform.shape.ViewLensSupport; - /** - * Shows a RadialTreeLayout view of a Forest. - * A hyperbolic projection lens may also be applied - * to the view - * + * Shows a RadialTreeLayout view of a Forest. A hyperbolic projection lens may also be applied to + * the view + * * @author Tom Nelson - * */ @SuppressWarnings("serial") public class RadialTreeLensDemo extends JApplet { - - CTreeNetwork graph; - - VisualizationServer.Paintable rings; - - String root; - - TreeLayout layout; - - RadialTreeLayout radialLayout; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - /** - * provides a Hyperbolic lens for the view - */ - LensSupport hyperbolicViewSupport; - - ScalingControl scaler; - - /** - * create an instance of a simple graph with controls to - * demo the zoomand hyperbolic features. - * - */ - public RadialTreeLensDemo() { - - // create a simple graph for the demo - graph = createTree(); - - layout = new TreeLayout(graph.asGraph()); - radialLayout = new RadialTreeLayout(graph.asGraph()); - radialLayout.setSize(new Dimension(600,600)); - - Dimension preferredSize = new Dimension(600,600); - - final VisualizationModel visualizationModel = - new DefaultVisualizationModel(graph, radialLayout, preferredSize); - vv = new VisualizationViewer(visualizationModel, preferredSize); - - PickedState ps = vv.getPickedVertexState(); - PickedState pes = vv.getPickedEdgeState(); - vv.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(ps, Color.red, Color.yellow)); - vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(pes, Color.black, Color.cyan)); - vv.setBackground(Color.white); - - vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); - - // add a listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - - Container content = getContentPane(); - GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv); - content.add(gzsp); - - final DefaultModalGraphMouse graphMouse - = new DefaultModalGraphMouse(); - - vv.setGraphMouse(graphMouse); - vv.addKeyListener(graphMouse.getModeKeyListener()); - rings = new Rings(); - vv.addPreRenderPaintable(rings); - - hyperbolicViewSupport = - new ViewLensSupport(vv, new HyperbolicShapeTransformer(vv, - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), - new ModalLensGraphMouse()); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + + CTreeNetwork graph; + + VisualizationServer.Paintable rings; + + String root; + + TreeLayout layout; + + RadialTreeLayout radialLayout; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + /** provides a Hyperbolic lens for the view */ + LensSupport hyperbolicViewSupport; + + ScalingControl scaler; + + /** create an instance of a simple graph with controls to demo the zoomand hyperbolic features. */ + public RadialTreeLensDemo() { + + // create a simple graph for the demo + graph = createTree(); + + layout = new TreeLayout(graph.asGraph()); + radialLayout = new RadialTreeLayout(graph.asGraph()); + radialLayout.setSize(new Dimension(600, 600)); + + Dimension preferredSize = new Dimension(600, 600); + + final VisualizationModel visualizationModel = + new DefaultVisualizationModel(graph, radialLayout, preferredSize); + vv = new VisualizationViewer(visualizationModel, preferredSize); + + PickedState ps = vv.getPickedVertexState(); + PickedState pes = vv.getPickedEdgeState(); + vv.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer(ps, Color.red, Color.yellow)); + vv.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer(pes, Color.black, Color.cyan)); + vv.setBackground(Color.white); + + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); + + // add a listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + + Container content = getContentPane(); + GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv); + content.add(gzsp); + + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + + vv.setGraphMouse(graphMouse); + vv.addKeyListener(graphMouse.getModeKeyListener()); + rings = new Rings(); + vv.addPreRenderPaintable(rings); + + hyperbolicViewSupport = + new ViewLensSupport( + vv, + new HyperbolicShapeTransformer( + vv, vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), + new ModalLensGraphMouse()); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - - final JRadioButton hyperView = new JRadioButton("Hyperbolic View"); - hyperView.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - hyperbolicViewSupport.activate(e.getStateChange() == ItemEvent.SELECTED); - } + + final JRadioButton hyperView = new JRadioButton("Hyperbolic View"); + hyperView.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + hyperbolicViewSupport.activate(e.getStateChange() == ItemEvent.SELECTED); + } }); - graphMouse.addItemListener(hyperbolicViewSupport.getGraphMouse().getModeListener()); - - JMenuBar menubar = new JMenuBar(); - menubar.add(graphMouse.getModeMenu()); - gzsp.setCorner(menubar); - - JPanel controls = new JPanel(); - JPanel zoomControls = new JPanel(new GridLayout(2,1)); - zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom")); - JPanel hyperControls = new JPanel(new GridLayout(3,2)); - hyperControls.setBorder(BorderFactory.createTitledBorder("Examiner Lens")); - zoomControls.add(plus); - zoomControls.add(minus); - JPanel modeControls = new JPanel(new BorderLayout()); - modeControls.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); - modeControls.add(graphMouse.getModeComboBox()); - hyperControls.add(hyperView); - - controls.add(zoomControls); - controls.add(hyperControls); - controls.add(modeControls); - content.add(controls, BorderLayout.SOUTH); + graphMouse.addItemListener(hyperbolicViewSupport.getGraphMouse().getModeListener()); + + JMenuBar menubar = new JMenuBar(); + menubar.add(graphMouse.getModeMenu()); + gzsp.setCorner(menubar); + + JPanel controls = new JPanel(); + JPanel zoomControls = new JPanel(new GridLayout(2, 1)); + zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom")); + JPanel hyperControls = new JPanel(new GridLayout(3, 2)); + hyperControls.setBorder(BorderFactory.createTitledBorder("Examiner Lens")); + zoomControls.add(plus); + zoomControls.add(minus); + JPanel modeControls = new JPanel(new BorderLayout()); + modeControls.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); + modeControls.add(graphMouse.getModeComboBox()); + hyperControls.add(hyperView); + + controls.add(zoomControls); + controls.add(hyperControls); + controls.add(modeControls); + content.add(controls, BorderLayout.SOUTH); + } + + private CTreeNetwork createTree() { + MutableCTreeNetwork tree = + TreeNetworkBuilder.builder().expectedNodeCount(27).build(); + + tree.addNode("root"); + + int edgeId = 0; + tree.addEdge("root", "V0", edgeId++); + tree.addEdge("V0", "V1", edgeId++); + tree.addEdge("V0", "V2", edgeId++); + tree.addEdge("V1", "V4", edgeId++); + tree.addEdge("V2", "V3", edgeId++); + tree.addEdge("V2", "V5", edgeId++); + tree.addEdge("V4", "V6", edgeId++); + tree.addEdge("V4", "V7", edgeId++); + tree.addEdge("V3", "V8", edgeId++); + tree.addEdge("V6", "V9", edgeId++); + tree.addEdge("V4", "V10", edgeId++); + + tree.addEdge("root", "A0", edgeId++); + tree.addEdge("A0", "A1", edgeId++); + tree.addEdge("A0", "A2", edgeId++); + tree.addEdge("A0", "A3", edgeId++); + + tree.addEdge("root", "B0", edgeId++); + tree.addEdge("B0", "B1", edgeId++); + tree.addEdge("B0", "B2", edgeId++); + tree.addEdge("B1", "B4", edgeId++); + tree.addEdge("B2", "B3", edgeId++); + tree.addEdge("B2", "B5", edgeId++); + tree.addEdge("B4", "B6", edgeId++); + tree.addEdge("B4", "B7", edgeId++); + tree.addEdge("B3", "B8", edgeId++); + tree.addEdge("B6", "B9", edgeId++); + + return tree; + } + + class Rings implements VisualizationServer.Paintable { + + Collection depths; + + public Rings() { + depths = getDepths(); } - private CTreeNetwork createTree() { - MutableCTreeNetwork tree = - TreeNetworkBuilder.builder().expectedNodeCount(27).build(); - - tree.addNode("root"); - - int edgeId = 0; - tree.addEdge("root", "V0", edgeId++); - tree.addEdge("V0", "V1", edgeId++); - tree.addEdge("V0", "V2", edgeId++); - tree.addEdge("V1", "V4", edgeId++); - tree.addEdge("V2", "V3", edgeId++); - tree.addEdge("V2", "V5", edgeId++); - tree.addEdge("V4", "V6", edgeId++); - tree.addEdge("V4", "V7", edgeId++); - tree.addEdge("V3", "V8", edgeId++); - tree.addEdge("V6", "V9", edgeId++); - tree.addEdge("V4", "V10", edgeId++); - - tree.addEdge("root", "A0", edgeId++); - tree.addEdge("A0", "A1", edgeId++); - tree.addEdge("A0", "A2", edgeId++); - tree.addEdge("A0", "A3", edgeId++); - - tree.addEdge("root", "B0", edgeId++); - tree.addEdge("B0", "B1", edgeId++); - tree.addEdge("B0", "B2", edgeId++); - tree.addEdge("B1", "B4", edgeId++); - tree.addEdge("B2", "B3", edgeId++); - tree.addEdge("B2", "B5", edgeId++); - tree.addEdge("B4", "B6", edgeId++); - tree.addEdge("B4", "B7", edgeId++); - tree.addEdge("B3", "B8", edgeId++); - tree.addEdge("B6", "B9", edgeId++); - - return tree; + private Collection getDepths() { + Set depths = new HashSet(); + Map polarLocations = radialLayout.getPolarLocations(); + for (String v : graph.nodes()) { + PolarPoint pp = polarLocations.get(v); + depths.add(pp.getRadius()); + } + return depths; } - class Rings implements VisualizationServer.Paintable { - - Collection depths; - - public Rings() { - depths = getDepths(); - } - - private Collection getDepths() { - Set depths = new HashSet(); - Map polarLocations = radialLayout.getPolarLocations(); - for(String v : graph.nodes()) { - PolarPoint pp = polarLocations.get(v); - depths.add(pp.getRadius()); - } - return depths; - } - - public void paint(Graphics g) { - g.setColor(Color.gray); - Graphics2D g2d = (Graphics2D)g; - Point2D center = radialLayout.getCenter(); - - Ellipse2D ellipse = new Ellipse2D.Double(); - for(double d : depths) { - ellipse.setFrameFromDiagonal(center.getX()-d, center.getY()-d, - center.getX()+d, center.getY()+d); - Shape shape = - vv.getRenderContext().getMultiLayerTransformer().transform(ellipse); - g2d.draw(shape); - } - } - - public boolean useTransform() { - return true; - } + public void paint(Graphics g) { + g.setColor(Color.gray); + Graphics2D g2d = (Graphics2D) g; + Point2D center = radialLayout.getCenter(); + + Ellipse2D ellipse = new Ellipse2D.Double(); + for (double d : depths) { + ellipse.setFrameFromDiagonal( + center.getX() - d, center.getY() - d, center.getX() + d, center.getY() + d); + Shape shape = vv.getRenderContext().getMultiLayerTransformer().transform(ellipse); + g2d.draw(shape); + } } - public static void main(String[] args) { - JFrame f = new JFrame(); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - f.getContentPane().add(new RadialTreeLensDemo()); - f.pack(); - f.setVisible(true); + public boolean useTransform() { + return true; } + } + + public static void main(String[] args) { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.getContentPane().add(new RadialTreeLensDemo()); + f.pack(); + f.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/SatelliteViewDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/SatelliteViewDemo.java index 16cc9bf8..a7809f48 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/SatelliteViewDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/SatelliteViewDemo.java @@ -1,13 +1,32 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.layout.FRLayout; +import edu.uci.ics.jung.graph.util.TestGraphs; +import edu.uci.ics.jung.visualization.DefaultVisualizationModel; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationModel; +import edu.uci.ics.jung.visualization.VisualizationServer.Paintable; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; +import edu.uci.ics.jung.visualization.control.SatelliteVisualizationViewer; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; +import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; +import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; +import edu.uci.ics.jung.visualization.renderers.GradientVertexRenderer; +import edu.uci.ics.jung.visualization.renderers.Renderer; +import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; @@ -23,7 +42,6 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.geom.GeneralPath; - import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JCheckBox; @@ -34,266 +52,255 @@ import javax.swing.JPanel; import javax.swing.ToolTipManager; -import com.google.common.graph.Network; - -import edu.uci.ics.jung.algorithms.layout.FRLayout; -import edu.uci.ics.jung.graph.util.TestGraphs; -import edu.uci.ics.jung.visualization.DefaultVisualizationModel; -import edu.uci.ics.jung.visualization.GraphZoomScrollPane; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationModel; -import edu.uci.ics.jung.visualization.VisualizationServer.Paintable; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; -import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; -import edu.uci.ics.jung.visualization.control.SatelliteVisualizationViewer; -import edu.uci.ics.jung.visualization.control.ScalingControl; -import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; -import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; -import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; -import edu.uci.ics.jung.visualization.renderers.GradientVertexRenderer; -import edu.uci.ics.jung.visualization.renderers.Renderer; -import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer; - /** - * Demonstrates the construction of a graph visualization with a main and - * a satellite view. The satellite - * view is smaller, always contains the entire graph, and contains - * a lens shape that shows the boundaries of the visible part of the - * graph in the main view. Using the mouse, you can pick, translate, - * layout-scale, view-scale, rotate, shear, and region-select in either - * view. Using the mouse in either window affects only the main view - * and the lens shape in the satellite view. - * + * Demonstrates the construction of a graph visualization with a main and a satellite view. The + * satellite view is smaller, always contains the entire graph, and contains a lens shape that shows + * the boundaries of the visible part of the graph in the main view. Using the mouse, you can pick, + * translate, layout-scale, view-scale, rotate, shear, and region-select in either view. Using the + * mouse in either window affects only the main view and the lens shape in the satellite view. + * * @author Tom Nelson - * */ @SuppressWarnings("serial") public class SatelliteViewDemo extends JApplet { - static final String instructions = - ""+ - "

        Instructions for Mouse Listeners

        "+ - "

        There are two modes, Transforming and Picking."+ - "

        The modes are selected with a combo box."+ - - "

        Transforming Mode:"+ - "

          "+ - "
        • Mouse1+drag pans the graph"+ - "
        • Mouse1+Shift+drag rotates the graph"+ - "
        • Mouse1+CTRL(or Command)+drag shears the graph"+ - "
        "+ - - "Picking Mode:"+ - "
          "+ - "
        • Mouse1 on a Vertex selects the vertex"+ - "
        • Mouse1 elsewhere unselects all Vertices"+ - "
        • Mouse1+Shift on a Vertex adds/removes Vertex selection"+ - "
        • Mouse1+drag on a Vertex moves all selected Vertices"+ - "
        • Mouse1+drag elsewhere selects Vertices in a region"+ - "
        • Mouse1+Shift+drag adds selection of Vertices in a new region"+ - "
        • Mouse1+CTRL on a Vertex selects the vertex and centers the display on it"+ - "
        "+ - "Both Modes:"+ - "
          "+ - "
        • Mousewheel scales with a crossover value of 1.0.

          "+ - " - scales the graph layout when the combined scale is greater than 1

          "+ - " - scales the graph view when the combined scale is less than 1"; - - JDialog helpDialog; - - Paintable viewGrid; - - /** - * create an instance of a simple graph in two views with controls to - * demo the features. - * - */ - public SatelliteViewDemo() { - - // create a simple graph for the demo - Network graph = TestGraphs.getOneComponentGraph(); - - // the preferred sizes for the two views - Dimension preferredSize1 = new Dimension(600,600); - Dimension preferredSize2 = new Dimension(300, 300); - - // create one layout for the graph - FRLayout layout = new FRLayout(graph.asGraph()); - layout.setMaxIterations(500); - - // create one model that both views will share - VisualizationModel vm = - new DefaultVisualizationModel(graph, layout, preferredSize1); - - // create 2 views that share the same model - final VisualizationViewer vv1 = - new VisualizationViewer(vm, preferredSize1); - final SatelliteVisualizationViewer vv2 = - new SatelliteVisualizationViewer(vv1, preferredSize2); - vv1.setBackground(Color.white); - vv1.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(vv1.getPickedEdgeState(), Color.black, Color.cyan)); - vv1.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(vv1.getPickedVertexState(), Color.red, Color.yellow)); - vv2.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(vv2.getPickedEdgeState(), Color.black, Color.cyan)); - vv2.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(vv2.getPickedVertexState(), Color.red, Color.yellow)); - vv1.getRenderer().setVertexRenderer(new GradientVertexRenderer(vv1, Color.red, Color.white, true)); - vv1.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - vv1.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR); - - ScalingControl vv2Scaler = new CrossoverScalingControl(); - vv2.scaleToLayout(vv2Scaler); - - viewGrid = new ViewGrid(vv2, vv1); - - // add default listener for ToolTips - vv1.setVertexToolTipTransformer(new ToStringLabeller()); - vv2.setVertexToolTipTransformer(new ToStringLabeller()); - - vv2.getRenderContext().setVertexLabelTransformer(vv1.getRenderContext().getVertexLabelTransformer()); - - - ToolTipManager.sharedInstance().setDismissDelay(10000); - - Container content = getContentPane(); - Container panel = new JPanel(new BorderLayout()); - Container rightPanel = new JPanel(new GridLayout(2,1)); - - GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv1); - panel.add(gzsp); - rightPanel.add(new JPanel()); - rightPanel.add(vv2); - panel.add(rightPanel, BorderLayout.EAST); - - helpDialog = new JDialog(); - helpDialog.getContentPane().add(new JLabel(instructions)); - - // create a GraphMouse for the main view - final DefaultModalGraphMouse graphMouse - = new DefaultModalGraphMouse(); - vv1.setGraphMouse(graphMouse); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv1, 1.1f, vv1.getCenter()); - } + static final String instructions = + "" + + "

          Instructions for Mouse Listeners

          " + + "

          There are two modes, Transforming and Picking." + + "

          The modes are selected with a combo box." + + "

          Transforming Mode:" + + "

            " + + "
          • Mouse1+drag pans the graph" + + "
          • Mouse1+Shift+drag rotates the graph" + + "
          • Mouse1+CTRL(or Command)+drag shears the graph" + + "
          " + + "Picking Mode:" + + "
            " + + "
          • Mouse1 on a Vertex selects the vertex" + + "
          • Mouse1 elsewhere unselects all Vertices" + + "
          • Mouse1+Shift on a Vertex adds/removes Vertex selection" + + "
          • Mouse1+drag on a Vertex moves all selected Vertices" + + "
          • Mouse1+drag elsewhere selects Vertices in a region" + + "
          • Mouse1+Shift+drag adds selection of Vertices in a new region" + + "
          • Mouse1+CTRL on a Vertex selects the vertex and centers the display on it" + + "
          " + + "Both Modes:" + + "
            " + + "
          • Mousewheel scales with a crossover value of 1.0.

            " + + " - scales the graph layout when the combined scale is greater than 1

            " + + " - scales the graph view when the combined scale is less than 1"; + + JDialog helpDialog; + + Paintable viewGrid; + + /** create an instance of a simple graph in two views with controls to demo the features. */ + public SatelliteViewDemo() { + + // create a simple graph for the demo + Network graph = TestGraphs.getOneComponentGraph(); + + // the preferred sizes for the two views + Dimension preferredSize1 = new Dimension(600, 600); + Dimension preferredSize2 = new Dimension(300, 300); + + // create one layout for the graph + FRLayout layout = new FRLayout(graph.asGraph()); + layout.setMaxIterations(500); + + // create one model that both views will share + VisualizationModel vm = + new DefaultVisualizationModel(graph, layout, preferredSize1); + + // create 2 views that share the same model + final VisualizationViewer vv1 = + new VisualizationViewer(vm, preferredSize1); + final SatelliteVisualizationViewer vv2 = + new SatelliteVisualizationViewer(vv1, preferredSize2); + vv1.setBackground(Color.white); + vv1.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv1.getPickedEdgeState(), Color.black, Color.cyan)); + vv1.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer( + vv1.getPickedVertexState(), Color.red, Color.yellow)); + vv2.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv2.getPickedEdgeState(), Color.black, Color.cyan)); + vv2.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer( + vv2.getPickedVertexState(), Color.red, Color.yellow)); + vv1.getRenderer() + .setVertexRenderer(new GradientVertexRenderer(vv1, Color.red, Color.white, true)); + vv1.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + vv1.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR); + + ScalingControl vv2Scaler = new CrossoverScalingControl(); + vv2.scaleToLayout(vv2Scaler); + + viewGrid = new ViewGrid(vv2, vv1); + + // add default listener for ToolTips + vv1.setVertexToolTipTransformer(new ToStringLabeller()); + vv2.setVertexToolTipTransformer(new ToStringLabeller()); + + vv2.getRenderContext() + .setVertexLabelTransformer(vv1.getRenderContext().getVertexLabelTransformer()); + + ToolTipManager.sharedInstance().setDismissDelay(10000); + + Container content = getContentPane(); + Container panel = new JPanel(new BorderLayout()); + Container rightPanel = new JPanel(new GridLayout(2, 1)); + + GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv1); + panel.add(gzsp); + rightPanel.add(new JPanel()); + rightPanel.add(vv2); + panel.add(rightPanel, BorderLayout.EAST); + + helpDialog = new JDialog(); + helpDialog.getContentPane().add(new JLabel(instructions)); + + // create a GraphMouse for the main view + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + vv1.setGraphMouse(graphMouse); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv1, 1.1f, vv1.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv1, 1/1.1f, vv1.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv1, 1 / 1.1f, vv1.getCenter()); + } }); - - JComboBox modeBox = graphMouse.getModeComboBox(); - modeBox.addItemListener(((DefaultModalGraphMouse)vv2.getGraphMouse()) - .getModeListener()); - - JCheckBox gridBox = new JCheckBox("Show Grid"); - gridBox.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - showGrid(vv2, e.getStateChange() == ItemEvent.SELECTED); - }}); - JButton help = new JButton("Help"); - help.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - helpDialog.pack(); - helpDialog.setVisible(true); - } + + JComboBox modeBox = graphMouse.getModeComboBox(); + modeBox.addItemListener(((DefaultModalGraphMouse) vv2.getGraphMouse()).getModeListener()); + + JCheckBox gridBox = new JCheckBox("Show Grid"); + gridBox.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + showGrid(vv2, e.getStateChange() == ItemEvent.SELECTED); + } }); + JButton help = new JButton("Help"); + help.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + helpDialog.pack(); + helpDialog.setVisible(true); + } + }); + + JPanel controls = new JPanel(); + controls.add(plus); + controls.add(minus); + controls.add(modeBox); + controls.add(gridBox); + controls.add(help); + content.add(panel); + content.add(controls, BorderLayout.SOUTH); + } - JPanel controls = new JPanel(); - controls.add(plus); - controls.add(minus); - controls.add(modeBox); - controls.add(gridBox); - controls.add(help); - content.add(panel); - content.add(controls, BorderLayout.SOUTH); + protected void showGrid(VisualizationViewer vv, boolean state) { + if (state == true) { + vv.addPreRenderPaintable(viewGrid); + } else { + vv.removePreRenderPaintable(viewGrid); } - - protected void showGrid(VisualizationViewer vv, boolean state) { - if(state == true) { - vv.addPreRenderPaintable(viewGrid); - } else { - vv.removePreRenderPaintable(viewGrid); - } - vv.repaint(); + vv.repaint(); + } + + /** + * draws a grid on the SatelliteViewer's lens + * + * @author Tom Nelson + */ + static class ViewGrid implements Paintable { + + VisualizationViewer master; + VisualizationViewer vv; + + public ViewGrid(VisualizationViewer vv, VisualizationViewer master) { + this.vv = vv; + this.master = master; } - - /** - * draws a grid on the SatelliteViewer's lens - * @author Tom Nelson - * - */ - static class ViewGrid implements Paintable { - - VisualizationViewer master; - VisualizationViewer vv; - - public ViewGrid(VisualizationViewer vv, VisualizationViewer master) { - this.vv = vv; - this.master = master; - } - public void paint(Graphics g) { - ShapeTransformer masterViewTransformer = master.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); - ShapeTransformer masterLayoutTransformer = master.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - ShapeTransformer vvLayoutTransformer = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - - Rectangle rect = master.getBounds(); - GeneralPath path = new GeneralPath(); - path.moveTo(rect.x, rect.y); - path.lineTo(rect.width,rect.y); - path.lineTo(rect.width, rect.height); - path.lineTo(rect.x, rect.height); - path.lineTo(rect.x, rect.y); - - for(int i=0; i<=rect.width; i+=rect.width/10) { - path.moveTo(rect.x+i, rect.y); - path.lineTo(rect.x+i, rect.height); - } - for(int i=0; i<=rect.height; i+=rect.height/10) { - path.moveTo(rect.x, rect.y+i); - path.lineTo(rect.width, rect.y+i); - } - Shape lens = path; - lens = masterViewTransformer.inverseTransform(lens); - lens = masterLayoutTransformer.inverseTransform(lens); - lens = vvLayoutTransformer.transform(lens); - Graphics2D g2d = (Graphics2D)g; - Color old = g.getColor(); - g.setColor(Color.cyan); - g2d.draw(lens); - - path = new GeneralPath(); - path.moveTo((float)rect.getMinX(), (float)rect.getCenterY()); - path.lineTo((float)rect.getMaxX(), (float)rect.getCenterY()); - path.moveTo((float)rect.getCenterX(), (float)rect.getMinY()); - path.lineTo((float)rect.getCenterX(), (float)rect.getMaxY()); - Shape crosshairShape = path; - crosshairShape = masterViewTransformer.inverseTransform(crosshairShape); - crosshairShape = masterLayoutTransformer.inverseTransform(crosshairShape); - crosshairShape = vvLayoutTransformer.transform(crosshairShape); - g.setColor(Color.black); - g2d.setStroke(new BasicStroke(3)); - g2d.draw(crosshairShape); - - g.setColor(old); - } - - public boolean useTransform() { - return true; - } + + public void paint(Graphics g) { + ShapeTransformer masterViewTransformer = + master.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); + ShapeTransformer masterLayoutTransformer = + master.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + ShapeTransformer vvLayoutTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + + Rectangle rect = master.getBounds(); + GeneralPath path = new GeneralPath(); + path.moveTo(rect.x, rect.y); + path.lineTo(rect.width, rect.y); + path.lineTo(rect.width, rect.height); + path.lineTo(rect.x, rect.height); + path.lineTo(rect.x, rect.y); + + for (int i = 0; i <= rect.width; i += rect.width / 10) { + path.moveTo(rect.x + i, rect.y); + path.lineTo(rect.x + i, rect.height); + } + for (int i = 0; i <= rect.height; i += rect.height / 10) { + path.moveTo(rect.x, rect.y + i); + path.lineTo(rect.width, rect.y + i); + } + Shape lens = path; + lens = masterViewTransformer.inverseTransform(lens); + lens = masterLayoutTransformer.inverseTransform(lens); + lens = vvLayoutTransformer.transform(lens); + Graphics2D g2d = (Graphics2D) g; + Color old = g.getColor(); + g.setColor(Color.cyan); + g2d.draw(lens); + + path = new GeneralPath(); + path.moveTo((float) rect.getMinX(), (float) rect.getCenterY()); + path.lineTo((float) rect.getMaxX(), (float) rect.getCenterY()); + path.moveTo((float) rect.getCenterX(), (float) rect.getMinY()); + path.lineTo((float) rect.getCenterX(), (float) rect.getMaxY()); + Shape crosshairShape = path; + crosshairShape = masterViewTransformer.inverseTransform(crosshairShape); + crosshairShape = masterLayoutTransformer.inverseTransform(crosshairShape); + crosshairShape = vvLayoutTransformer.transform(crosshairShape); + g.setColor(Color.black); + g2d.setStroke(new BasicStroke(3)); + g2d.draw(crosshairShape); + + g.setColor(old); } - - public static void main(String[] args) { - JFrame f = new JFrame(); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - f.getContentPane().add(new SatelliteViewDemo()); - f.pack(); - f.setVisible(true); + public boolean useTransform() { + return true; } + } + + public static void main(String[] args) { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.getContentPane().add(new SatelliteViewDemo()); + f.pack(); + f.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/ShortestPathDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/ShortestPathDemo.java index 73a356a0..74c3cc18 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/ShortestPathDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/ShortestPathDemo.java @@ -3,6 +3,23 @@ */ package edu.uci.ics.jung.samples; +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.graph.ElementOrder; +import com.google.common.graph.EndpointPair; +import com.google.common.graph.Graph; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.Network; +import com.google.common.graph.NetworkBuilder; +import edu.uci.ics.jung.algorithms.generators.random.EppsteinPowerLawGenerator; +import edu.uci.ics.jung.algorithms.layout.FRLayout; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.shortestpath.BFSDistanceLabeler; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; +import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; +import edu.uci.ics.jung.visualization.renderers.Renderer; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; @@ -15,7 +32,6 @@ import java.awt.geom.Point2D; import java.util.HashSet; import java.util.Set; - import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JComboBox; @@ -24,274 +40,238 @@ import javax.swing.JPanel; import javax.swing.SwingConstants; -import com.google.common.base.Function; -import com.google.common.base.Supplier; -import com.google.common.graph.ElementOrder; -import com.google.common.graph.EndpointPair; -import com.google.common.graph.Graph; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.Network; -import com.google.common.graph.NetworkBuilder; - -import edu.uci.ics.jung.algorithms.generators.random.EppsteinPowerLawGenerator; -import edu.uci.ics.jung.algorithms.layout.FRLayout; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.algorithms.shortestpath.BFSDistanceLabeler; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; -import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; -import edu.uci.ics.jung.visualization.renderers.Renderer; - /** - * Demonstrates use of the shortest path algorithm and visualization of the - * results. - * + * Demonstrates use of the shortest path algorithm and visualization of the results. + * * @author danyelf */ public class ShortestPathDemo extends JPanel { - /** - * - */ - private static final long serialVersionUID = 7526217664458188502L; - - /** - * Starting vertex - */ - private String mFrom; - - /** - * Ending vertex - */ - private String mTo; - private Network mGraph; - private Set mPred; - - public ShortestPathDemo() { - - this.mGraph = getGraph(); - setBackground(Color.WHITE); - // show graph - final Layout layout = new FRLayout(mGraph.asGraph()); - final VisualizationViewer vv = new VisualizationViewer(mGraph, layout); - vv.setBackground(Color.WHITE); - - vv.getRenderContext().setVertexDrawPaintTransformer(new MyVertexDrawPaintFunction()); - vv.getRenderContext().setVertexFillPaintTransformer(new MyVertexFillPaintFunction()); - vv.getRenderContext().setEdgeDrawPaintTransformer(new MyEdgePaintFunction()); - vv.getRenderContext().setEdgeStrokeTransformer(new MyEdgeStrokeFunction()); - vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - vv.setGraphMouse(new DefaultModalGraphMouse()); - vv.addPostRenderPaintable(new VisualizationViewer.Paintable(){ - - public boolean useTransform() { - return true; - } - public void paint(Graphics g) { - if(mPred == null) return; - - // for all edges, paint edges that are in shortest path - for (Number e : mGraph.edges()) { - if (isBlessed(e)) { - EndpointPair endpoints = mGraph.incidentNodes(e); - String v1 = endpoints.nodeU(); - String v2 = endpoints.nodeV(); - Point2D p1 = layout.apply(v1); - Point2D p2 = layout.apply(v2); - p1 = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p1); - p2 = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p2); - Renderer renderer = vv.getRenderer(); - renderer.renderEdge(e); - } - } + /** */ + private static final long serialVersionUID = 7526217664458188502L; + + /** Starting vertex */ + private String mFrom; + + /** Ending vertex */ + private String mTo; + + private Network mGraph; + private Set mPred; + + public ShortestPathDemo() { + + this.mGraph = getGraph(); + setBackground(Color.WHITE); + // show graph + final Layout layout = new FRLayout(mGraph.asGraph()); + final VisualizationViewer vv = + new VisualizationViewer(mGraph, layout); + vv.setBackground(Color.WHITE); + + vv.getRenderContext().setVertexDrawPaintTransformer(new MyVertexDrawPaintFunction()); + vv.getRenderContext().setVertexFillPaintTransformer(new MyVertexFillPaintFunction()); + vv.getRenderContext().setEdgeDrawPaintTransformer(new MyEdgePaintFunction()); + vv.getRenderContext().setEdgeStrokeTransformer(new MyEdgeStrokeFunction()); + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + vv.setGraphMouse(new DefaultModalGraphMouse()); + vv.addPostRenderPaintable( + new VisualizationViewer.Paintable() { + + public boolean useTransform() { + return true; + } + + public void paint(Graphics g) { + if (mPred == null) return; + + // for all edges, paint edges that are in shortest path + for (Number e : mGraph.edges()) { + if (isBlessed(e)) { + EndpointPair endpoints = mGraph.incidentNodes(e); + String v1 = endpoints.nodeU(); + String v2 = endpoints.nodeV(); + Point2D p1 = layout.apply(v1); + Point2D p2 = layout.apply(v2); + p1 = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p1); + p2 = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p2); + Renderer renderer = vv.getRenderer(); + renderer.renderEdge(e); + } } + } }); - - setLayout(new BorderLayout()); - add(vv, BorderLayout.CENTER); - // set up controls - add(setUpControls(), BorderLayout.SOUTH); - } - - boolean isBlessed(Number e) { - EndpointPair endpoints = mGraph.incidentNodes(e); - String v1 = endpoints.nodeU(); - String v2 = endpoints.nodeV(); - return v1.equals(v2) == false && mPred.contains(v1) && mPred.contains(v2); + + setLayout(new BorderLayout()); + add(vv, BorderLayout.CENTER); + // set up controls + add(setUpControls(), BorderLayout.SOUTH); + } + + boolean isBlessed(Number e) { + EndpointPair endpoints = mGraph.incidentNodes(e); + String v1 = endpoints.nodeU(); + String v2 = endpoints.nodeV(); + return v1.equals(v2) == false && mPred.contains(v1) && mPred.contains(v2); + } + + /** @author danyelf */ + public class MyEdgePaintFunction implements Function { + + public Paint apply(Number e) { + if (mPred == null || mPred.size() == 0) return Color.BLACK; + if (isBlessed(e)) { + return new Color(0.0f, 0.0f, 1.0f, 0.5f); //Color.BLUE; + } else { + return Color.LIGHT_GRAY; + } + } + } + + public class MyEdgeStrokeFunction implements Function { + protected final Stroke THIN = new BasicStroke(1); + protected final Stroke THICK = new BasicStroke(1); + + public Stroke apply(Number e) { + if (mPred == null || mPred.size() == 0) return THIN; + if (isBlessed(e)) { + return THICK; + } else return THIN; + } + } + + /** @author danyelf */ + public class MyVertexDrawPaintFunction implements Function { + + public Paint apply(V v) { + return Color.black; } - - /** - * @author danyelf - */ - public class MyEdgePaintFunction implements Function { - - public Paint apply(Number e) { - if (mPred == null || mPred.size() == 0) return Color.BLACK; - if (isBlessed(e)) { - return new Color(0.0f, 0.0f, 1.0f, 0.5f);//Color.BLUE; - } else { - return Color.LIGHT_GRAY; - } - } - } - - public class MyEdgeStrokeFunction implements Function { - protected final Stroke THIN = new BasicStroke(1); - protected final Stroke THICK = new BasicStroke(1); - - public Stroke apply(Number e) { - if ( mPred == null || mPred.size() == 0) return THIN; - if (isBlessed( e ) ) { - return THICK; - } else - return THIN; + } + + public class MyVertexFillPaintFunction implements Function { + + public Paint apply(V v) { + if (v == mFrom) { + return Color.BLUE; + } + if (v == mTo) { + return Color.BLUE; + } + if (mPred == null) { + return Color.LIGHT_GRAY; + } else { + if (mPred.contains(v)) { + return Color.RED; + } else { + return Color.LIGHT_GRAY; } - - } - - /** - * @author danyelf - */ - public class MyVertexDrawPaintFunction implements Function { - - public Paint apply(V v) { - return Color.black; - } - - } - - public class MyVertexFillPaintFunction implements Function { - - public Paint apply( V v ) { - if ( v == mFrom) { - return Color.BLUE; - } - if ( v == mTo ) { - return Color.BLUE; - } - if ( mPred == null ) { - return Color.LIGHT_GRAY; - } else { - if ( mPred.contains(v)) { - return Color.RED; - } else { - return Color.LIGHT_GRAY; - } - } - } - - } - - /** - * - */ - private JPanel setUpControls() { - JPanel jp = new JPanel(); - jp.setBackground(Color.WHITE); - jp.setLayout(new BoxLayout(jp, BoxLayout.PAGE_AXIS)); - jp.setBorder(BorderFactory.createLineBorder(Color.black, 3)); - jp.add( - new JLabel("Select a pair of vertices for which a shortest path will be displayed")); - JPanel jp2 = new JPanel(); - jp2.add(new JLabel("vertex from", SwingConstants.LEFT)); - jp2.add(getSelectionBox(true)); - jp2.setBackground(Color.white); - JPanel jp3 = new JPanel(); - jp3.add(new JLabel("vertex to", SwingConstants.LEFT)); - jp3.add(getSelectionBox(false)); - jp3.setBackground(Color.white); - jp.add( jp2 ); - jp.add( jp3 ); - return jp; - } - - private Component getSelectionBox(final boolean from) { - String[] nodes = new String[mGraph.nodes().size()]; - int i = 0; - for (String node : mGraph.nodes()) { - nodes[i++] = node; - } - final JComboBox choices = new JComboBox(nodes); - choices.setSelectedIndex(-1); - choices.setBackground(Color.WHITE); - choices.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - String v = (String) choices.getSelectedItem(); - - if (from) { - mFrom = v; - } else { - mTo = v; - } - drawShortest(); - repaint(); - } - }); - return choices; - } - - /** - * - */ - protected void drawShortest() { - if (mFrom == null || mTo == null) { - return; - } - BFSDistanceLabeler bdl = new BFSDistanceLabeler(); - bdl.labelDistances(mGraph.asGraph(), mFrom); - mPred = new HashSet(); - - // grab a predecessor - String v = mTo; - Set prd = bdl.getPredecessors(v); - mPred.add( mTo ); - while( prd != null && prd.size() > 0) { - v = prd.iterator().next(); - mPred.add( v ); - if ( v == mFrom ) return; - prd = bdl.getPredecessors(v); - } - } - - public static void main(String[] s) { - JFrame jf = new JFrame(); - jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - jf.getContentPane().add(new ShortestPathDemo()); - jf.pack(); - jf.setVisible(true); - } - - /** - * @return the graph for this demo - */ - Network getGraph() { - Graph g = - new EppsteinPowerLawGenerator( - new VertexFactory(), 26, 50, 50).get(); - // convert this graph into a Network because the visualization system can't handle Graphs (yet) - MutableNetwork graph - = NetworkBuilder.undirected().nodeOrder(ElementOrder.natural()).build(); - EdgeFactory edgeFactory = new EdgeFactory(); - // this implicitly removes any isolated nodes, as intended - for (EndpointPair endpoints : g.edges()) { - graph.addEdge(endpoints.nodeU(), endpoints.nodeV(), edgeFactory.get()); - } - return graph; - } - - static class VertexFactory implements Supplier { - char a = 'a'; - public String get() { - return Character.toString(a++); - } - } - - static class EdgeFactory implements Supplier { - int count; - public Number get() { - return count++; - } - } + } + } + } + + /** */ + private JPanel setUpControls() { + JPanel jp = new JPanel(); + jp.setBackground(Color.WHITE); + jp.setLayout(new BoxLayout(jp, BoxLayout.PAGE_AXIS)); + jp.setBorder(BorderFactory.createLineBorder(Color.black, 3)); + jp.add(new JLabel("Select a pair of vertices for which a shortest path will be displayed")); + JPanel jp2 = new JPanel(); + jp2.add(new JLabel("vertex from", SwingConstants.LEFT)); + jp2.add(getSelectionBox(true)); + jp2.setBackground(Color.white); + JPanel jp3 = new JPanel(); + jp3.add(new JLabel("vertex to", SwingConstants.LEFT)); + jp3.add(getSelectionBox(false)); + jp3.setBackground(Color.white); + jp.add(jp2); + jp.add(jp3); + return jp; + } + + private Component getSelectionBox(final boolean from) { + String[] nodes = new String[mGraph.nodes().size()]; + int i = 0; + for (String node : mGraph.nodes()) { + nodes[i++] = node; + } + final JComboBox choices = new JComboBox(nodes); + choices.setSelectedIndex(-1); + choices.setBackground(Color.WHITE); + choices.addActionListener( + new ActionListener() { + + public void actionPerformed(ActionEvent e) { + String v = (String) choices.getSelectedItem(); + + if (from) { + mFrom = v; + } else { + mTo = v; + } + drawShortest(); + repaint(); + } + }); + return choices; + } + + /** */ + protected void drawShortest() { + if (mFrom == null || mTo == null) { + return; + } + BFSDistanceLabeler bdl = new BFSDistanceLabeler(); + bdl.labelDistances(mGraph.asGraph(), mFrom); + mPred = new HashSet(); + + // grab a predecessor + String v = mTo; + Set prd = bdl.getPredecessors(v); + mPred.add(mTo); + while (prd != null && prd.size() > 0) { + v = prd.iterator().next(); + mPred.add(v); + if (v == mFrom) return; + prd = bdl.getPredecessors(v); + } + } + + public static void main(String[] s) { + JFrame jf = new JFrame(); + jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + jf.getContentPane().add(new ShortestPathDemo()); + jf.pack(); + jf.setVisible(true); + } + + /** @return the graph for this demo */ + Network getGraph() { + Graph g = new EppsteinPowerLawGenerator(new VertexFactory(), 26, 50, 50).get(); + // convert this graph into a Network because the visualization system can't handle Graphs (yet) + MutableNetwork graph = + NetworkBuilder.undirected().nodeOrder(ElementOrder.natural()).build(); + EdgeFactory edgeFactory = new EdgeFactory(); + // this implicitly removes any isolated nodes, as intended + for (EndpointPair endpoints : g.edges()) { + graph.addEdge(endpoints.nodeU(), endpoints.nodeV(), edgeFactory.get()); + } + return graph; + } + + static class VertexFactory implements Supplier { + char a = 'a'; + + public String get() { + return Character.toString(a++); + } + } + + static class EdgeFactory implements Supplier { + int count; + + public Number get() { + return count++; + } + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/SimpleGraphDraw.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/SimpleGraphDraw.java index ced0ac13..e374eb4b 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/SimpleGraphDraw.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/SimpleGraphDraw.java @@ -1,59 +1,54 @@ /* * Copyright (c) 2008, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. */ package edu.uci.ics.jung.samples; -import java.io.IOException; - -import javax.swing.JFrame; - import com.google.common.base.Supplier; import com.google.common.graph.MutableNetwork; import com.google.common.graph.Network; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.layout.FRLayout; import edu.uci.ics.jung.io.PajekNetReader; import edu.uci.ics.jung.visualization.VisualizationViewer; +import java.io.IOException; +import javax.swing.JFrame; -/** - * A class that shows the minimal work necessary to load and visualize a graph. - */ -public class SimpleGraphDraw -{ +/** A class that shows the minimal work necessary to load and visualize a graph. */ +public class SimpleGraphDraw { + + @SuppressWarnings({"rawtypes", "unchecked"}) + public static void main(String[] args) throws IOException { + JFrame jf = new JFrame(); + Network g = getGraph(); + VisualizationViewer vv = new VisualizationViewer(g, new FRLayout(g.asGraph())); + jf.getContentPane().add(vv); + jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + jf.pack(); + jf.setVisible(true); + } + + /** + * Generates a graph: in this case, reads it from the file "samples/datasetsgraph/simple.net" + * + * @return A sample undirected graph + * @throws IOException if there is an error in reading the file + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static Network getGraph() throws IOException { + PajekNetReader pnr = + new PajekNetReader( + new Supplier() { + public Object get() { + return new Object(); + } + }); + MutableNetwork g = NetworkBuilder.undirected().build(); - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static void main(String[] args) throws IOException - { - JFrame jf = new JFrame(); - Network g = getGraph(); - VisualizationViewer vv = new VisualizationViewer(g, new FRLayout(g.asGraph())); - jf.getContentPane().add(vv); - jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - jf.pack(); - jf.setVisible(true); - } - - /** - * Generates a graph: in this case, reads it from the file - * "samples/datasetsgraph/simple.net" - * @return A sample undirected graph - * @throws IOException if there is an error in reading the file - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static Network getGraph() throws IOException - { - PajekNetReader pnr = new PajekNetReader(new Supplier(){ - public Object get() { - return new Object(); - }}); - MutableNetwork g = NetworkBuilder.undirected().build(); - - pnr.load("src/main/resources/datasets/simple.net", g); - return g; - } + pnr.load("src/main/resources/datasets/simple.net", g); + return g; + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/TreeCollapseDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/TreeCollapseDemo.java index 8b5a7aca..55afc9a8 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/TreeCollapseDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/TreeCollapseDemo.java @@ -2,13 +2,34 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ - +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.layout.PolarPoint; +import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout; +import edu.uci.ics.jung.algorithms.layout.TreeLayout; +import edu.uci.ics.jung.graph.CTreeNetwork; +import edu.uci.ics.jung.graph.MutableCTreeNetwork; +import edu.uci.ics.jung.graph.TreeNetworkBuilder; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationServer; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.decorators.EdgeShape; +import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer; +import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; +import edu.uci.ics.jung.visualization.subLayout.TreeCollapser; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; @@ -28,7 +49,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - import javax.swing.BorderFactory; import javax.swing.JApplet; import javax.swing.JButton; @@ -37,333 +57,311 @@ import javax.swing.JPanel; import javax.swing.JToggleButton; -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.graph.Network; - -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.algorithms.layout.PolarPoint; -import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout; -import edu.uci.ics.jung.algorithms.layout.TreeLayout; -import edu.uci.ics.jung.graph.CTreeNetwork; -import edu.uci.ics.jung.graph.MutableCTreeNetwork; -import edu.uci.ics.jung.graph.TreeNetworkBuilder; -import edu.uci.ics.jung.visualization.GraphZoomScrollPane; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationServer; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; -import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ScalingControl; -import edu.uci.ics.jung.visualization.decorators.EdgeShape; -import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer; -import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; -import edu.uci.ics.jung.visualization.subLayout.TreeCollapser; - /** * Demonstrates "collapsing"/"expanding" of a tree's subtrees. + * * @author Tom Nelson - * */ @SuppressWarnings("serial") public class TreeCollapseDemo extends JApplet { - /** - * the original graph - */ - MutableCTreeNetwork graph; + /** the original graph */ + MutableCTreeNetwork graph; - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; + /** the visual component and renderer for the graph */ + VisualizationViewer vv; - VisualizationServer.Paintable rings; + VisualizationServer.Paintable rings; - String root; + String root; - TreeLayout layout; -// FRLayout layout1; + TreeLayout layout; + // FRLayout layout1; -// TreeCollapser collapser; + // TreeCollapser collapser; - RadialTreeLayout radialLayout; + RadialTreeLayout radialLayout; - @SuppressWarnings("unchecked") - public TreeCollapseDemo() { + @SuppressWarnings("unchecked") + public TreeCollapseDemo() { - // create a simple graph for the demo - graph = createTree(); + // create a simple graph for the demo + graph = createTree(); - layout = new TreeLayout(graph.asGraph()); -// collapser = new TreeCollapser(); + layout = new TreeLayout(graph.asGraph()); + // collapser = new TreeCollapser(); - radialLayout = new RadialTreeLayout(graph.asGraph()); - radialLayout.setSize(new Dimension(600,600)); - vv = new VisualizationViewer(graph, layout, new Dimension(600,600)); - vv.setBackground(Color.white); - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); - vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeFunction()); - // add a listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); - rings = new Rings(); + radialLayout = new RadialTreeLayout(graph.asGraph()); + radialLayout.setSize(new Dimension(600, 600)); + vv = new VisualizationViewer(graph, layout, new Dimension(600, 600)); + vv.setBackground(Color.white); + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeFunction()); + // add a listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); + rings = new Rings(); - Container content = getContentPane(); - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - content.add(panel); + Container content = getContentPane(); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + content.add(panel); - final DefaultModalGraphMouse graphMouse - = new DefaultModalGraphMouse(); + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); - vv.setGraphMouse(graphMouse); + vv.setGraphMouse(graphMouse); - JComboBox modeBox = graphMouse.getModeComboBox(); - modeBox.addItemListener(graphMouse.getModeListener()); - graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); + JComboBox modeBox = graphMouse.getModeComboBox(); + modeBox.addItemListener(graphMouse.getModeListener()); + graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); - final ScalingControl scaler = new CrossoverScalingControl(); + final ScalingControl scaler = new CrossoverScalingControl(); - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } + }); + + JToggleButton radial = new JToggleButton("Radial"); + radial.addItemListener( + new ItemListener() { + + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + vv.setGraphLayout(radialLayout); + vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); + vv.addPreRenderPaintable(rings); + } else { + vv.setGraphLayout(layout); + vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); + vv.removePreRenderPaintable(rings); } + vv.repaint(); + } }); - JToggleButton radial = new JToggleButton("Radial"); - radial.addItemListener(new ItemListener() { - - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - vv.setGraphLayout(radialLayout); - vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); - vv.addPreRenderPaintable(rings); - } else { - vv.setGraphLayout(layout); - vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); - vv.removePreRenderPaintable(rings); - } - vv.repaint(); - }}); - - JButton collapse = new JButton("Collapse"); - collapse.addActionListener(e -> { - Set picked = vv.getPickedVertexState().getPicked(); - if(picked.size() == 1) { - Object root = picked.iterator().next(); -// Forest inGraph = (Forest)layout.getGraph(); - - - @SuppressWarnings("rawtypes") -// CTreeNetwork subTree = collapser.collapse(inGraph, root); - CTreeNetwork subTree = TreeCollapser.collapse(graph, root); - @SuppressWarnings("rawtypes") - Layout objectLayout = (Layout) vv.getGraphLayout(); - objectLayout.setLocation(subTree, (Point2D)objectLayout.apply(root)); - - vv.getPickedVertexState().clear(); - vv.repaint(); - } - }); - -// collapse.addActionListener(new ActionListener() { -// -// public void actionPerformed(ActionEvent e) { -// Set picked = vv.getPickedVertexState().getPicked(); -// if(picked.size() == 1) { -// Object root = picked.iterator().next(); -// Forest inGraph = (Forest)layout.getGraph(); -// -// @SuppressWarnings("rawtypes") -// CTreeNetwork subTree = collapser.collapse(inGraph, root); -// vv.getGraphLayout().setLocation(subTree, (Point2D)layout.apply(subRoot)); -// -// vv.getPickedVertexState().clear(); -// vv.repaint(); -// } -// }}); - - JButton expand = new JButton("Expand"); - expand.addActionListener(new ActionListener() { + JButton collapse = new JButton("Collapse"); + collapse.addActionListener( + e -> { + Set picked = vv.getPickedVertexState().getPicked(); + if (picked.size() == 1) { + Object root = picked.iterator().next(); + // Forest inGraph = (Forest)layout.getGraph(); @SuppressWarnings("rawtypes") - public void actionPerformed(ActionEvent e) { -// Collection picked = vv.getPickedVertexState().getPicked(); - for(Object v : vv.getPickedVertexState().getPicked()) { - if(v instanceof CTreeNetwork) { -// Forest inGraph -// = (Forest)layout.getGraph(); -// TreeCollapser.expand(inGraph, (Forest)v); - TreeCollapser.expand(graph, (CTreeNetwork) v); - } - vv.getPickedVertexState().clear(); - vv.repaint(); - } - }}); - - JPanel scaleGrid = new JPanel(new GridLayout(1,0)); - scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); - - JPanel controls = new JPanel(); - scaleGrid.add(plus); - scaleGrid.add(minus); - controls.add(radial); - controls.add(scaleGrid); - controls.add(modeBox); - controls.add(collapse); - controls.add(expand); - content.add(controls, BorderLayout.SOUTH); + // CTreeNetwork subTree = collapser.collapse(inGraph, root); + CTreeNetwork subTree = TreeCollapser.collapse(graph, root); + @SuppressWarnings("rawtypes") + Layout objectLayout = (Layout) vv.getGraphLayout(); + objectLayout.setLocation(subTree, (Point2D) objectLayout.apply(root)); + + vv.getPickedVertexState().clear(); + vv.repaint(); + } + }); + + // collapse.addActionListener(new ActionListener() { + // + // public void actionPerformed(ActionEvent e) { + // Set picked = vv.getPickedVertexState().getPicked(); + // if(picked.size() == 1) { + // Object root = picked.iterator().next(); + // Forest inGraph = (Forest)layout.getGraph(); + // + // @SuppressWarnings("rawtypes") + // CTreeNetwork subTree = collapser.collapse(inGraph, root); + // vv.getGraphLayout().setLocation(subTree, (Point2D)layout.apply(subRoot)); + // + // vv.getPickedVertexState().clear(); + // vv.repaint(); + // } + // }}); + + JButton expand = new JButton("Expand"); + expand.addActionListener( + new ActionListener() { + + @SuppressWarnings("rawtypes") + public void actionPerformed(ActionEvent e) { + // Collection picked = vv.getPickedVertexState().getPicked(); + for (Object v : vv.getPickedVertexState().getPicked()) { + if (v instanceof CTreeNetwork) { + // Forest inGraph + // = (Forest)layout.getGraph(); + // TreeCollapser.expand(inGraph, (Forest)v); + TreeCollapser.expand(graph, (CTreeNetwork) v); + } + vv.getPickedVertexState().clear(); + vv.repaint(); + } + } + }); + + JPanel scaleGrid = new JPanel(new GridLayout(1, 0)); + scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); + + JPanel controls = new JPanel(); + scaleGrid.add(plus); + scaleGrid.add(minus); + controls.add(radial); + controls.add(scaleGrid); + controls.add(modeBox); + controls.add(collapse); + controls.add(expand); + content.add(controls, BorderLayout.SOUTH); + } + + class Rings implements VisualizationServer.Paintable { + + Collection depths; + + public Rings() { + depths = getDepths(); } - class Rings implements VisualizationServer.Paintable { - - Collection depths; - - public Rings() { - depths = getDepths(); - } - - private Collection getDepths() { - Set depths = new HashSet(); - Map polarLocations = radialLayout.getPolarLocations(); - for(String v : graph.nodes()) { - PolarPoint pp = polarLocations.get(v); - depths.add(pp.getRadius()); - } - return depths; - } - - public void paint(Graphics g) { - g.setColor(Color.lightGray); - - Graphics2D g2d = (Graphics2D)g; - Point2D center = radialLayout.getCenter(); - - Ellipse2D ellipse = new Ellipse2D.Double(); - for(double d : depths) { - ellipse.setFrameFromDiagonal(center.getX()-d, center.getY()-d, - center.getX()+d, center.getY()+d); - Shape shape = vv.getRenderContext(). - getMultiLayerTransformer().getTransformer(Layer.LAYOUT).transform(ellipse); - g2d.draw(shape); - } - } - - public boolean useTransform() { - return true; - } + private Collection getDepths() { + Set depths = new HashSet(); + Map polarLocations = radialLayout.getPolarLocations(); + for (String v : graph.nodes()) { + PolarPoint pp = polarLocations.get(v); + depths.add(pp.getRadius()); + } + return depths; } - /** - * - */ - private MutableCTreeNetwork createTree() { - MutableCTreeNetwork tree = - TreeNetworkBuilder.builder().expectedNodeCount(27).build(); - - tree.addNode("root"); - - int edgeId = 0; - tree.addEdge("root", "V0", edgeId++); - tree.addEdge("V0", "V1", edgeId++); - tree.addEdge("V0", "V2", edgeId++); - tree.addEdge("V1", "V4", edgeId++); - tree.addEdge("V2", "V3", edgeId++); - tree.addEdge("V2", "V5", edgeId++); - tree.addEdge("V4", "V6", edgeId++); - tree.addEdge("V4", "V7", edgeId++); - tree.addEdge("V3", "V8", edgeId++); - tree.addEdge("V6", "V9", edgeId++); - tree.addEdge("V4", "V10", edgeId++); - - tree.addEdge("root", "A0", edgeId++); - tree.addEdge("A0", "A1", edgeId++); - tree.addEdge("A0", "A2", edgeId++); - tree.addEdge("A0", "A3", edgeId++); - - tree.addEdge("root", "B0", edgeId++); - tree.addEdge("B0", "B1", edgeId++); - tree.addEdge("B0", "B2", edgeId++); - tree.addEdge("B1", "B4", edgeId++); - tree.addEdge("B2", "B3", edgeId++); - tree.addEdge("B2", "B5", edgeId++); - tree.addEdge("B4", "B6", edgeId++); - tree.addEdge("B4", "B7", edgeId++); - tree.addEdge("B3", "B8", edgeId++); - tree.addEdge("B6", "B9", edgeId++); - - return tree; + public void paint(Graphics g) { + g.setColor(Color.lightGray); + + Graphics2D g2d = (Graphics2D) g; + Point2D center = radialLayout.getCenter(); + + Ellipse2D ellipse = new Ellipse2D.Double(); + for (double d : depths) { + ellipse.setFrameFromDiagonal( + center.getX() - d, center.getY() - d, center.getX() + d, center.getY() + d); + Shape shape = + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .transform(ellipse); + g2d.draw(shape); + } } - /** - * a demo class that will create a vertex shape that is either a - * polygon or star. The number of sides corresponds to the number - * of vertices that were collapsed into the vertex represented by - * this shape. - * - * @author Tom Nelson - * - * @param the vertex type - */ - class ClusterVertexShapeFunction extends EllipseVertexShapeTransformer { - - ClusterVertexShapeFunction() { - setSizeTransformer(new ClusterVertexSizeFunction(20)); - } - - @Override - public Shape apply(V v) { - if (v instanceof Network) { - @SuppressWarnings("rawtypes") - int size = ((Network) v).nodes().size(); - if (size < 8) { - int sides = Math.max(size, 3); - return factory.getRegularPolygon(v, sides); - } else { - return factory.getRegularStar(v, size); - } - } - return super.apply(v); - } - } - - /** - * A demo class that will make vertices larger if they represent - * a collapsed collection of original vertices - * @author Tom Nelson - * - * @param the vertex type - */ - class ClusterVertexSizeFunction implements Function { - int size; - public ClusterVertexSizeFunction(Integer size) { - this.size = size; - } + public boolean useTransform() { + return true; + } + } + + /** */ + private MutableCTreeNetwork createTree() { + MutableCTreeNetwork tree = + TreeNetworkBuilder.builder().expectedNodeCount(27).build(); + + tree.addNode("root"); + + int edgeId = 0; + tree.addEdge("root", "V0", edgeId++); + tree.addEdge("V0", "V1", edgeId++); + tree.addEdge("V0", "V2", edgeId++); + tree.addEdge("V1", "V4", edgeId++); + tree.addEdge("V2", "V3", edgeId++); + tree.addEdge("V2", "V5", edgeId++); + tree.addEdge("V4", "V6", edgeId++); + tree.addEdge("V4", "V7", edgeId++); + tree.addEdge("V3", "V8", edgeId++); + tree.addEdge("V6", "V9", edgeId++); + tree.addEdge("V4", "V10", edgeId++); + + tree.addEdge("root", "A0", edgeId++); + tree.addEdge("A0", "A1", edgeId++); + tree.addEdge("A0", "A2", edgeId++); + tree.addEdge("A0", "A3", edgeId++); + + tree.addEdge("root", "B0", edgeId++); + tree.addEdge("B0", "B1", edgeId++); + tree.addEdge("B0", "B2", edgeId++); + tree.addEdge("B1", "B4", edgeId++); + tree.addEdge("B2", "B3", edgeId++); + tree.addEdge("B2", "B5", edgeId++); + tree.addEdge("B4", "B6", edgeId++); + tree.addEdge("B4", "B7", edgeId++); + tree.addEdge("B3", "B8", edgeId++); + tree.addEdge("B6", "B9", edgeId++); + + return tree; + } + + /** + * a demo class that will create a vertex shape that is either a polygon or star. The number of + * sides corresponds to the number of vertices that were collapsed into the vertex represented by + * this shape. + * + * @author Tom Nelson + * @param the vertex type + */ + class ClusterVertexShapeFunction extends EllipseVertexShapeTransformer { + + ClusterVertexShapeFunction() { + setSizeTransformer(new ClusterVertexSizeFunction(20)); + } - public Integer apply(V v) { - if(v instanceof Network) { - return 30; - } - return size; + @Override + public Shape apply(V v) { + if (v instanceof Network) { + @SuppressWarnings("rawtypes") + int size = ((Network) v).nodes().size(); + if (size < 8) { + int sides = Math.max(size, 3); + return factory.getRegularPolygon(v, sides); + } else { + return factory.getRegularStar(v, size); } + } + return super.apply(v); + } + } + + /** + * A demo class that will make vertices larger if they represent a collapsed collection of + * original vertices + * + * @author Tom Nelson + * @param the vertex type + */ + class ClusterVertexSizeFunction implements Function { + int size; + + public ClusterVertexSizeFunction(Integer size) { + this.size = size; } - public static void main(String[] args) { - JFrame frame = new JFrame(); - Container content = frame.getContentPane(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - content.add(new TreeCollapseDemo()); - frame.pack(); - frame.setVisible(true); + public Integer apply(V v) { + if (v instanceof Network) { + return 30; + } + return size; } + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + Container content = frame.getContentPane(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + content.add(new TreeCollapseDemo()); + frame.pack(); + frame.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/TreeLayoutDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/TreeLayoutDemo.java index 32294976..d7973428 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/TreeLayoutDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/TreeLayoutDemo.java @@ -1,13 +1,33 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; +import com.google.common.base.Functions; +import edu.uci.ics.jung.algorithms.layout.PolarPoint; +import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout; +import edu.uci.ics.jung.algorithms.layout.TreeLayout; +import edu.uci.ics.jung.graph.CTreeNetwork; +import edu.uci.ics.jung.graph.MutableCTreeNetwork; +import edu.uci.ics.jung.graph.TreeNetworkBuilder; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationServer; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ModalGraphMouse.Mode; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.decorators.EdgeShape; +import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; +import edu.uci.ics.jung.visualization.layout.LayoutTransition; +import edu.uci.ics.jung.visualization.util.Animator; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; @@ -27,7 +47,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - import javax.swing.BorderFactory; import javax.swing.JApplet; import javax.swing.JButton; @@ -36,215 +55,199 @@ import javax.swing.JPanel; import javax.swing.JToggleButton; -import com.google.common.base.Functions; - -import edu.uci.ics.jung.algorithms.layout.PolarPoint; -import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout; -import edu.uci.ics.jung.algorithms.layout.TreeLayout; -import edu.uci.ics.jung.graph.CTreeNetwork; -import edu.uci.ics.jung.graph.MutableCTreeNetwork; -import edu.uci.ics.jung.graph.TreeNetworkBuilder; -import edu.uci.ics.jung.visualization.GraphZoomScrollPane; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationServer; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; -import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ModalGraphMouse.Mode; -import edu.uci.ics.jung.visualization.control.ScalingControl; -import edu.uci.ics.jung.visualization.decorators.EdgeShape; -import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; -import edu.uci.ics.jung.visualization.layout.LayoutTransition; -import edu.uci.ics.jung.visualization.util.Animator; - /** * Demonsrates TreeLayout and RadialTreeLayout. + * * @author Tom Nelson - * */ @SuppressWarnings("serial") public class TreeLayoutDemo extends JApplet { - CTreeNetwork graph; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - VisualizationServer.Paintable rings; - - String root; - - TreeLayout treeLayout; - - RadialTreeLayout radialLayout; - - public TreeLayoutDemo() { - - // create a simple graph for the demo - graph = createTree(); - - treeLayout = new TreeLayout(graph.asGraph()); - radialLayout = new RadialTreeLayout(graph.asGraph()); - radialLayout.setSize(new Dimension(600,600)); - vv = new VisualizationViewer(graph, treeLayout, new Dimension(600,600)); - vv.setBackground(Color.white); - vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); - vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - // add a listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); - rings = new Rings(); - - Container content = getContentPane(); - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - content.add(panel); - - final DefaultModalGraphMouse graphMouse - = new DefaultModalGraphMouse(); - - vv.setGraphMouse(graphMouse); - - JComboBox modeBox = graphMouse.getModeComboBox(); - modeBox.addItemListener(graphMouse.getModeListener()); - graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + CTreeNetwork graph; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + VisualizationServer.Paintable rings; + + String root; + + TreeLayout treeLayout; + + RadialTreeLayout radialLayout; + + public TreeLayoutDemo() { + + // create a simple graph for the demo + graph = createTree(); + + treeLayout = new TreeLayout(graph.asGraph()); + radialLayout = new RadialTreeLayout(graph.asGraph()); + radialLayout.setSize(new Dimension(600, 600)); + vv = new VisualizationViewer(graph, treeLayout, new Dimension(600, 600)); + vv.setBackground(Color.white); + vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph)); + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + // add a listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); + rings = new Rings(); + + Container content = getContentPane(); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + content.add(panel); + + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + + vv.setGraphMouse(graphMouse); + + JComboBox modeBox = graphMouse.getModeComboBox(); + modeBox.addItemListener(graphMouse.getModeListener()); + graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } + }); + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); + + JToggleButton radial = new JToggleButton("Radial"); + radial.addItemListener( + new ItemListener() { + + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + + LayoutTransition lt = + new LayoutTransition(vv, treeLayout, radialLayout); + Animator animator = new Animator(lt); + animator.start(); + vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); + vv.addPreRenderPaintable(rings); + } else { + LayoutTransition lt = + new LayoutTransition(vv, radialLayout, treeLayout); + Animator animator = new Animator(lt); + animator.start(); + vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); + vv.removePreRenderPaintable(rings); } + vv.repaint(); + } }); - - JToggleButton radial = new JToggleButton("Radial"); - radial.addItemListener(new ItemListener() { - - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - - LayoutTransition lt = - new LayoutTransition(vv, treeLayout, radialLayout); - Animator animator = new Animator(lt); - animator.start(); - vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); - vv.addPreRenderPaintable(rings); - } else { - LayoutTransition lt = - new LayoutTransition(vv, radialLayout, treeLayout); - Animator animator = new Animator(lt); - animator.start(); - vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); - vv.removePreRenderPaintable(rings); - } - vv.repaint(); - }}); - - JPanel scaleGrid = new JPanel(new GridLayout(1,0)); - scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); - - JPanel controls = new JPanel(); - scaleGrid.add(plus); - scaleGrid.add(minus); - controls.add(radial); - controls.add(scaleGrid); - controls.add(modeBox); - - content.add(controls, BorderLayout.SOUTH); - } - - class Rings implements VisualizationServer.Paintable { - - Collection depths; - - public Rings() { - depths = getDepths(); - } - - private Collection getDepths() { - Set depths = new HashSet(); - Map polarLocations = radialLayout.getPolarLocations(); - for(String v : graph.nodes()) { - PolarPoint pp = polarLocations.get(v); - depths.add(pp.getRadius()); - } - return depths; - } - - public void paint(Graphics g) { - g.setColor(Color.lightGray); - - Graphics2D g2d = (Graphics2D)g; - Point2D center = radialLayout.getCenter(); - - Ellipse2D ellipse = new Ellipse2D.Double(); - for(double d : depths) { - ellipse.setFrameFromDiagonal(center.getX()-d, center.getY()-d, - center.getX()+d, center.getY()+d); - Shape shape = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).transform(ellipse); - g2d.draw(shape); - } - } - - public boolean useTransform() { - return true; - } + + JPanel scaleGrid = new JPanel(new GridLayout(1, 0)); + scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); + + JPanel controls = new JPanel(); + scaleGrid.add(plus); + scaleGrid.add(minus); + controls.add(radial); + controls.add(scaleGrid); + controls.add(modeBox); + + content.add(controls, BorderLayout.SOUTH); + } + + class Rings implements VisualizationServer.Paintable { + + Collection depths; + + public Rings() { + depths = getDepths(); } - - private CTreeNetwork createTree() { - MutableCTreeNetwork tree = - TreeNetworkBuilder.builder().expectedNodeCount(27).build(); - - tree.addNode("root"); - - int edgeId = 0; - tree.addEdge("root", "V0", edgeId++); - tree.addEdge("V0", "V1", edgeId++); - tree.addEdge("V0", "V2", edgeId++); - tree.addEdge("V1", "V4", edgeId++); - tree.addEdge("V2", "V3", edgeId++); - tree.addEdge("V2", "V5", edgeId++); - tree.addEdge("V4", "V6", edgeId++); - tree.addEdge("V4", "V7", edgeId++); - tree.addEdge("V3", "V8", edgeId++); - tree.addEdge("V6", "V9", edgeId++); - tree.addEdge("V4", "V10", edgeId++); - - tree.addEdge("root", "A0", edgeId++); - tree.addEdge("A0", "A1", edgeId++); - tree.addEdge("A0", "A2", edgeId++); - tree.addEdge("A0", "A3", edgeId++); - - tree.addEdge("root", "B0", edgeId++); - tree.addEdge("B0", "B1", edgeId++); - tree.addEdge("B0", "B2", edgeId++); - tree.addEdge("B1", "B4", edgeId++); - tree.addEdge("B2", "B3", edgeId++); - tree.addEdge("B2", "B5", edgeId++); - tree.addEdge("B4", "B6", edgeId++); - tree.addEdge("B4", "B7", edgeId++); - tree.addEdge("B3", "B8", edgeId++); - tree.addEdge("B6", "B9", edgeId++); - - return tree; + + private Collection getDepths() { + Set depths = new HashSet(); + Map polarLocations = radialLayout.getPolarLocations(); + for (String v : graph.nodes()) { + PolarPoint pp = polarLocations.get(v); + depths.add(pp.getRadius()); + } + return depths; } - public static void main(String[] args) { - JFrame frame = new JFrame(); - Container content = frame.getContentPane(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + public void paint(Graphics g) { + g.setColor(Color.lightGray); + + Graphics2D g2d = (Graphics2D) g; + Point2D center = radialLayout.getCenter(); + + Ellipse2D ellipse = new Ellipse2D.Double(); + for (double d : depths) { + ellipse.setFrameFromDiagonal( + center.getX() - d, center.getY() - d, center.getX() + d, center.getY() + d); + Shape shape = + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .transform(ellipse); + g2d.draw(shape); + } + } - content.add(new TreeLayoutDemo()); - frame.pack(); - frame.setVisible(true); + public boolean useTransform() { + return true; } + } + + private CTreeNetwork createTree() { + MutableCTreeNetwork tree = + TreeNetworkBuilder.builder().expectedNodeCount(27).build(); + + tree.addNode("root"); + + int edgeId = 0; + tree.addEdge("root", "V0", edgeId++); + tree.addEdge("V0", "V1", edgeId++); + tree.addEdge("V0", "V2", edgeId++); + tree.addEdge("V1", "V4", edgeId++); + tree.addEdge("V2", "V3", edgeId++); + tree.addEdge("V2", "V5", edgeId++); + tree.addEdge("V4", "V6", edgeId++); + tree.addEdge("V4", "V7", edgeId++); + tree.addEdge("V3", "V8", edgeId++); + tree.addEdge("V6", "V9", edgeId++); + tree.addEdge("V4", "V10", edgeId++); + + tree.addEdge("root", "A0", edgeId++); + tree.addEdge("A0", "A1", edgeId++); + tree.addEdge("A0", "A2", edgeId++); + tree.addEdge("A0", "A3", edgeId++); + + tree.addEdge("root", "B0", edgeId++); + tree.addEdge("B0", "B1", edgeId++); + tree.addEdge("B0", "B2", edgeId++); + tree.addEdge("B1", "B4", edgeId++); + tree.addEdge("B2", "B3", edgeId++); + tree.addEdge("B2", "B5", edgeId++); + tree.addEdge("B4", "B6", edgeId++); + tree.addEdge("B4", "B7", edgeId++); + tree.addEdge("B3", "B8", edgeId++); + tree.addEdge("B6", "B9", edgeId++); + + return tree; + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + Container content = frame.getContentPane(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + content.add(new TreeLayoutDemo()); + frame.pack(); + frame.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/TwoModelDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/TwoModelDemo.java index 20cb3458..7af9ee1f 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/TwoModelDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/TwoModelDemo.java @@ -1,29 +1,14 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JApplet; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JPanel; - import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.FRLayout; import edu.uci.ics.jung.algorithms.layout.ISOMLayout; import edu.uci.ics.jung.algorithms.layout.Layout; @@ -41,146 +26,154 @@ import edu.uci.ics.jung.visualization.picking.MultiPickedState; import edu.uci.ics.jung.visualization.picking.PickedState; import edu.uci.ics.jung.visualization.transform.MutableTransformer; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.BorderFactory; +import javax.swing.JApplet; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; /** - * Demonstrates a single graph with 2 layouts in 2 views. - * They share picking, transforms, and a pluggable renderer - * + * Demonstrates a single graph with 2 layouts in 2 views. They share picking, transforms, and a + * pluggable renderer + * * @author Tom Nelson - * */ @SuppressWarnings("serial") public class TwoModelDemo extends JApplet { - /** - * the graph - */ - Network graph; - - /** - * the visual components and renderers for the graph - */ - VisualizationViewer vv1; - VisualizationViewer vv2; - - /** - * the normal Function - */ - MutableTransformer layoutTransformer; - - Dimension preferredSize = new Dimension(300,300); - - /** - * create an instance of a simple graph in two views with controls to - * demo the zoom features. - * - */ - public TwoModelDemo() { - - // create a simple graph for the demo - // both models will share one graph - graph = TestGraphs.getOneComponentGraph(); - - // create two layouts for the one graph, one layout for each model - Layout layout1 = new FRLayout(graph.asGraph()); - Layout layout2 = new ISOMLayout(graph); - - // create the two models, each with a different layout - VisualizationModel vm1 = - new DefaultVisualizationModel(graph, layout1, preferredSize); - VisualizationModel vm2 = - new DefaultVisualizationModel(graph, layout2, preferredSize); - - // create the two views, one for each model - // they share the same renderer - vv1 = new VisualizationViewer(vm1, preferredSize); - vv2 = new VisualizationViewer(vm2, preferredSize); - vv1.setRenderContext(vv2.getRenderContext()); - - // share the model Function between the two models -// layoutTransformer = vv1.getLayoutTransformer(); -// vv2.setLayoutTransformer(layoutTransformer); -// -// // share the view Function between the two models -// vv2.setViewTransformer(vv1.getViewTransformer()); - - vv2.getRenderContext().setMultiLayerTransformer(vv1.getRenderContext().getMultiLayerTransformer()); - vv2.getRenderContext().getMultiLayerTransformer().addChangeListener(vv1); - - vv1.setBackground(Color.white); - vv2.setBackground(Color.white); - - // share one PickedState between the two views - PickedState ps = new MultiPickedState(); - vv1.setPickedVertexState(ps); - vv2.setPickedVertexState(ps); - PickedState pes = new MultiPickedState(); - vv1.setPickedEdgeState(pes); - vv2.setPickedEdgeState(pes); - - // set an edge paint function that will show picking for edges - vv1.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(vv1.getPickedEdgeState(), Color.black, Color.red)); - vv1.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(vv1.getPickedVertexState(), - Color.red, Color.yellow)); - // add default listeners for ToolTips - vv1.setVertexToolTipTransformer(new ToStringLabeller()); - vv2.setVertexToolTipTransformer(new ToStringLabeller()); - - Container content = getContentPane(); - JPanel panel = new JPanel(new GridLayout(1,0)); - panel.add(new GraphZoomScrollPane(vv1)); - panel.add(new GraphZoomScrollPane(vv2)); - - content.add(panel); - - // create a GraphMouse for each view - final DefaultModalGraphMouse gm1 - = new DefaultModalGraphMouse(); - - DefaultModalGraphMouse gm2 - = new DefaultModalGraphMouse(); - - vv1.setGraphMouse(gm1); - vv2.setGraphMouse(gm2); - - // create zoom buttons for scaling the Function that is - // shared between the two models. - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv1, 1.1f, vv1.getCenter()); - } + /** the graph */ + Network graph; + + /** the visual components and renderers for the graph */ + VisualizationViewer vv1; + + VisualizationViewer vv2; + + /** the normal Function */ + MutableTransformer layoutTransformer; + + Dimension preferredSize = new Dimension(300, 300); + + /** create an instance of a simple graph in two views with controls to demo the zoom features. */ + public TwoModelDemo() { + + // create a simple graph for the demo + // both models will share one graph + graph = TestGraphs.getOneComponentGraph(); + + // create two layouts for the one graph, one layout for each model + Layout layout1 = new FRLayout(graph.asGraph()); + Layout layout2 = new ISOMLayout(graph); + + // create the two models, each with a different layout + VisualizationModel vm1 = + new DefaultVisualizationModel(graph, layout1, preferredSize); + VisualizationModel vm2 = + new DefaultVisualizationModel(graph, layout2, preferredSize); + + // create the two views, one for each model + // they share the same renderer + vv1 = new VisualizationViewer(vm1, preferredSize); + vv2 = new VisualizationViewer(vm2, preferredSize); + vv1.setRenderContext(vv2.getRenderContext()); + + // share the model Function between the two models + // layoutTransformer = vv1.getLayoutTransformer(); + // vv2.setLayoutTransformer(layoutTransformer); + // + // // share the view Function between the two models + // vv2.setViewTransformer(vv1.getViewTransformer()); + + vv2.getRenderContext() + .setMultiLayerTransformer(vv1.getRenderContext().getMultiLayerTransformer()); + vv2.getRenderContext().getMultiLayerTransformer().addChangeListener(vv1); + + vv1.setBackground(Color.white); + vv2.setBackground(Color.white); + + // share one PickedState between the two views + PickedState ps = new MultiPickedState(); + vv1.setPickedVertexState(ps); + vv2.setPickedVertexState(ps); + PickedState pes = new MultiPickedState(); + vv1.setPickedEdgeState(pes); + vv2.setPickedEdgeState(pes); + + // set an edge paint function that will show picking for edges + vv1.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv1.getPickedEdgeState(), Color.black, Color.red)); + vv1.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer( + vv1.getPickedVertexState(), Color.red, Color.yellow)); + // add default listeners for ToolTips + vv1.setVertexToolTipTransformer(new ToStringLabeller()); + vv2.setVertexToolTipTransformer(new ToStringLabeller()); + + Container content = getContentPane(); + JPanel panel = new JPanel(new GridLayout(1, 0)); + panel.add(new GraphZoomScrollPane(vv1)); + panel.add(new GraphZoomScrollPane(vv2)); + + content.add(panel); + + // create a GraphMouse for each view + final DefaultModalGraphMouse gm1 = new DefaultModalGraphMouse(); + + DefaultModalGraphMouse gm2 = new DefaultModalGraphMouse(); + + vv1.setGraphMouse(gm1); + vv2.setGraphMouse(gm2); + + // create zoom buttons for scaling the Function that is + // shared between the two models. + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv1, 1.1f, vv1.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv1, 1/1.1f, vv1.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv1, 1 / 1.1f, vv1.getCenter()); + } }); - - JPanel zoomPanel = new JPanel(new GridLayout(1,2)); - zoomPanel.setBorder(BorderFactory.createTitledBorder("Zoom")); - - JPanel modePanel = new JPanel(); - modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); - gm1.getModeComboBox().addItemListener(gm2.getModeListener()); - modePanel.add(gm1.getModeComboBox()); - - JPanel controls = new JPanel(); - zoomPanel.add(plus); - zoomPanel.add(minus); - controls.add(zoomPanel); - controls.add(modePanel); - content.add(controls, BorderLayout.SOUTH); - } - - public static void main(String[] args) { - JFrame f = new JFrame(); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - f.getContentPane().add(new TwoModelDemo()); - f.pack(); - f.setVisible(true); - } + + JPanel zoomPanel = new JPanel(new GridLayout(1, 2)); + zoomPanel.setBorder(BorderFactory.createTitledBorder("Zoom")); + + JPanel modePanel = new JPanel(); + modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); + gm1.getModeComboBox().addItemListener(gm2.getModeListener()); + modePanel.add(gm1.getModeComboBox()); + + JPanel controls = new JPanel(); + zoomPanel.add(plus); + zoomPanel.add(minus); + controls.add(zoomPanel); + controls.add(modePanel); + content.add(controls, BorderLayout.SOUTH); + } + + public static void main(String[] args) { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.getContentPane().add(new TwoModelDemo()); + f.pack(); + f.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/UnicodeLabelDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/UnicodeLabelDemo.java index 1eb93739..f55417ed 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/UnicodeLabelDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/UnicodeLabelDemo.java @@ -1,36 +1,18 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.util.HashMap; -import java.util.Map; - -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JFrame; -import javax.swing.JPanel; - import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.graph.MutableNetwork; import com.google.common.graph.Network; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.layout.FRLayout; import edu.uci.ics.jung.visualization.GraphZoomScrollPane; import edu.uci.ics.jung.visualization.VisualizationViewer; @@ -44,190 +26,203 @@ import edu.uci.ics.jung.visualization.decorators.VertexIconShapeTransformer; import edu.uci.ics.jung.visualization.renderers.DefaultEdgeLabelRenderer; import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.HashMap; +import java.util.Map; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JPanel; /** - * A demo that shows flag images as vertices, and uses unicode - * to render vertex labels. - * - * @author Tom Nelson - * + * A demo that shows flag images as vertices, and uses unicode to render vertex labels. + * + * @author Tom Nelson */ public class UnicodeLabelDemo { - /** - * the graph - */ - Network graph; + /** the graph */ + Network graph; - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - boolean showLabels; - - public UnicodeLabelDemo() { - - // create a simple graph for the demo - graph = createGraph(); - Map iconMap = new HashMap(); - - vv = new VisualizationViewer(graph, new FRLayout(graph.asGraph())); - vv.getRenderContext().setVertexLabelTransformer(new UnicodeVertexStringer()); - vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan)); - vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan)); - VertexIconShapeTransformer vertexIconShapeFunction = - new VertexIconShapeTransformer(new EllipseVertexShapeTransformer()); - Function vertexIconFunction = Functions.forMap(iconMap); - vv.getRenderContext().setVertexShapeTransformer(vertexIconShapeFunction); - vv.getRenderContext().setVertexIconTransformer(vertexIconFunction); - loadImages(iconMap); - vertexIconShapeFunction.setIconMap(iconMap); - vv.getRenderContext().setVertexFillPaintTransformer( - new PickableVertexPaintTransformer( - vv.getPickedVertexState(), Color.white, Color.yellow)); - vv.getRenderContext().setEdgeDrawPaintTransformer( - new PickableEdgePaintTransformer( - vv.getPickedEdgeState(), Color.black, Color.lightGray)); - - vv.setBackground(Color.white); - - // add my listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - - // create a frome to hold the graph - final JFrame frame = new JFrame(); - Container content = frame.getContentPane(); - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - content.add(panel); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - final DefaultModalGraphMouse gm - = new DefaultModalGraphMouse(); - vv.setGraphMouse(gm); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + boolean showLabels; + + public UnicodeLabelDemo() { + + // create a simple graph for the demo + graph = createGraph(); + Map iconMap = new HashMap(); + + vv = new VisualizationViewer(graph, new FRLayout(graph.asGraph())); + vv.getRenderContext().setVertexLabelTransformer(new UnicodeVertexStringer()); + vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan)); + vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan)); + VertexIconShapeTransformer vertexIconShapeFunction = + new VertexIconShapeTransformer(new EllipseVertexShapeTransformer()); + Function vertexIconFunction = Functions.forMap(iconMap); + vv.getRenderContext().setVertexShapeTransformer(vertexIconShapeFunction); + vv.getRenderContext().setVertexIconTransformer(vertexIconFunction); + loadImages(iconMap); + vertexIconShapeFunction.setIconMap(iconMap); + vv.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer( + vv.getPickedVertexState(), Color.white, Color.yellow)); + vv.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv.getPickedEdgeState(), Color.black, Color.lightGray)); + + vv.setBackground(Color.white); + + // add my listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + + // create a frome to hold the graph + final JFrame frame = new JFrame(); + Container content = frame.getContentPane(); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + content.add(panel); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + final DefaultModalGraphMouse gm = + new DefaultModalGraphMouse(); + vv.setGraphMouse(gm); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - JCheckBox lo = new JCheckBox("Show Labels"); - lo.addItemListener(new ItemListener(){ - public void itemStateChanged(ItemEvent e) { - showLabels = e.getStateChange() == ItemEvent.SELECTED; - vv.repaint(); - } + JCheckBox lo = new JCheckBox("Show Labels"); + lo.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + showLabels = e.getStateChange() == ItemEvent.SELECTED; + vv.repaint(); + } }); - lo.setSelected(true); - - JPanel controls = new JPanel(); - controls.add(plus); - controls.add(minus); - controls.add(lo); - controls.add(gm.getModeComboBox()); - content.add(controls, BorderLayout.SOUTH); - - frame.pack(); - frame.setVisible(true); - } - - - class UnicodeVertexStringer implements Function { - - Map map = new HashMap(); - Map iconMap = new HashMap(); - String[] labels = { - "\u0057\u0065\u006C\u0063\u006F\u006D\u0065\u0020\u0074\u006F\u0020JUNG\u0021", - "\u6B22\u8FCE\u4F7F\u7528\u0020\u0020JUNG\u0021", - "\u0414\u043E\u0431\u0440\u043E\u0020\u043F\u043E\u0436\u0430\u043B\u043E\u0432\u0430\u0422\u044A\u0020\u0432\u0020JUNG\u0021", - "\u0042\u0069\u0065\u006E\u0076\u0065\u006E\u0075\u0065\u0020\u0061\u0075\u0020JUNG\u0021", - "\u0057\u0069\u006C\u006B\u006F\u006D\u006D\u0065\u006E\u0020\u007A\u0075\u0020JUNG\u0021", - "JUNG\u3078\u3087\u3045\u3053\u305D\u0021", -// "\u0053\u00E9\u006A\u0061\u0020\u0042\u0065\u006D\u0076\u0069\u006E\u0064\u006F\u0020JUNG\u0021", - "\u0042\u0069\u0065\u006E\u0076\u0065\u006E\u0069\u0064\u0061\u0020\u0061\u0020JUNG\u0021" - }; - - public UnicodeVertexStringer() { - for (Integer node : graph.nodes()) { - map.put(node, labels[node.intValue() % labels.length]); - } - } - - /** - * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.Vertex) - */ - public String getLabel(Integer v) { - if(showLabels) { - return map.get(v); - } else { - return ""; - } - } - - public String apply(Integer input) { - return getLabel(input); - } + lo.setSelected(true); + + JPanel controls = new JPanel(); + controls.add(plus); + controls.add(minus); + controls.add(lo); + controls.add(gm.getModeComboBox()); + content.add(controls, BorderLayout.SOUTH); + + frame.pack(); + frame.setVisible(true); + } + + class UnicodeVertexStringer implements Function { + + Map map = new HashMap(); + Map iconMap = new HashMap(); + String[] labels = { + "\u0057\u0065\u006C\u0063\u006F\u006D\u0065\u0020\u0074\u006F\u0020JUNG\u0021", + "\u6B22\u8FCE\u4F7F\u7528\u0020\u0020JUNG\u0021", + "\u0414\u043E\u0431\u0440\u043E\u0020\u043F\u043E\u0436\u0430\u043B\u043E\u0432\u0430\u0422\u044A\u0020\u0432\u0020JUNG\u0021", + "\u0042\u0069\u0065\u006E\u0076\u0065\u006E\u0075\u0065\u0020\u0061\u0075\u0020JUNG\u0021", + "\u0057\u0069\u006C\u006B\u006F\u006D\u006D\u0065\u006E\u0020\u007A\u0075\u0020JUNG\u0021", + "JUNG\u3078\u3087\u3045\u3053\u305D\u0021", + // "\u0053\u00E9\u006A\u0061\u0020\u0042\u0065\u006D\u0076\u0069\u006E\u0064\u006F\u0020JUNG\u0021", + "\u0042\u0069\u0065\u006E\u0076\u0065\u006E\u0069\u0064\u0061\u0020\u0061\u0020JUNG\u0021" + }; + + public UnicodeVertexStringer() { + for (Integer node : graph.nodes()) { + map.put(node, labels[node.intValue() % labels.length]); + } } - - Network createGraph() { - MutableNetwork graph = NetworkBuilder.directed().build(); - graph.addEdge(0, 1, new Double(Math.random())); - graph.addEdge(3, 0, new Double(Math.random())); - graph.addEdge(0, 4, new Double(Math.random())); - graph.addEdge(4, 5, new Double(Math.random())); - graph.addEdge(5, 3, new Double(Math.random())); - graph.addEdge(2, 1, new Double(Math.random())); - graph.addEdge(4, 1, new Double(Math.random())); - graph.addEdge(8, 2, new Double(Math.random())); - graph.addEdge(3, 8, new Double(Math.random())); - graph.addEdge(6, 7, new Double(Math.random())); - graph.addEdge(7, 5, new Double(Math.random())); - graph.addEdge(0, 9, new Double(Math.random())); - graph.addEdge(9, 8, new Double(Math.random())); - graph.addEdge(7, 6, new Double(Math.random())); - graph.addEdge(6, 5, new Double(Math.random())); - graph.addEdge(4, 2, new Double(Math.random())); - graph.addEdge(5, 4, new Double(Math.random())); - graph.addEdge(4, 10, new Double(Math.random())); - graph.addEdge(10, 4, new Double(Math.random())); - - return graph; + + /** + * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.Vertex) + */ + public String getLabel(Integer v) { + if (showLabels) { + return map.get(v); + } else { + return ""; + } } - protected void loadImages(Map imageMap) { - - ImageIcon[] icons = null; - try { - icons = new ImageIcon[] { - new ImageIcon(getClass().getResource("/images/united-states.gif")), - new ImageIcon(getClass().getResource("/images/china.gif")), - new ImageIcon(getClass().getResource("/images/russia.gif")), - new ImageIcon(getClass().getResource("/images/france.gif")), - new ImageIcon(getClass().getResource("/images/germany.gif")), - new ImageIcon(getClass().getResource("/images/japan.gif")), - new ImageIcon(getClass().getResource("/images/spain.gif")) - }; - } catch(Exception ex) { - System.err.println("You need flags.jar in your classpath to see the flag icons."); - } - for (Integer node : graph.nodes()) { - int i = node.intValue(); - imageMap.put(node, icons[i % icons.length]); - } + public String apply(Integer input) { + return getLabel(input); } + } + + Network createGraph() { + MutableNetwork graph = NetworkBuilder.directed().build(); + graph.addEdge(0, 1, new Double(Math.random())); + graph.addEdge(3, 0, new Double(Math.random())); + graph.addEdge(0, 4, new Double(Math.random())); + graph.addEdge(4, 5, new Double(Math.random())); + graph.addEdge(5, 3, new Double(Math.random())); + graph.addEdge(2, 1, new Double(Math.random())); + graph.addEdge(4, 1, new Double(Math.random())); + graph.addEdge(8, 2, new Double(Math.random())); + graph.addEdge(3, 8, new Double(Math.random())); + graph.addEdge(6, 7, new Double(Math.random())); + graph.addEdge(7, 5, new Double(Math.random())); + graph.addEdge(0, 9, new Double(Math.random())); + graph.addEdge(9, 8, new Double(Math.random())); + graph.addEdge(7, 6, new Double(Math.random())); + graph.addEdge(6, 5, new Double(Math.random())); + graph.addEdge(4, 2, new Double(Math.random())); + graph.addEdge(5, 4, new Double(Math.random())); + graph.addEdge(4, 10, new Double(Math.random())); + graph.addEdge(10, 4, new Double(Math.random())); + + return graph; + } - public static void main(String[] args) - { - new UnicodeLabelDemo(); + protected void loadImages(Map imageMap) { + + ImageIcon[] icons = null; + try { + icons = + new ImageIcon[] { + new ImageIcon(getClass().getResource("/images/united-states.gif")), + new ImageIcon(getClass().getResource("/images/china.gif")), + new ImageIcon(getClass().getResource("/images/russia.gif")), + new ImageIcon(getClass().getResource("/images/france.gif")), + new ImageIcon(getClass().getResource("/images/germany.gif")), + new ImageIcon(getClass().getResource("/images/japan.gif")), + new ImageIcon(getClass().getResource("/images/spain.gif")) + }; + } catch (Exception ex) { + System.err.println("You need flags.jar in your classpath to see the flag icons."); + } + for (Integer node : graph.nodes()) { + int i = node.intValue(); + imageMap.put(node, icons[i % icons.length]); } + } + + public static void main(String[] args) { + new UnicodeLabelDemo(); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexCollapseDemoWithLayouts.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexCollapseDemoWithLayouts.java index 9c77032f..682256f2 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexCollapseDemoWithLayouts.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexCollapseDemoWithLayouts.java @@ -1,41 +1,17 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.GridLayout; -import java.awt.Shape; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.geom.Point2D; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -import javax.swing.BorderFactory; -import javax.swing.JApplet; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JComponent; -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.swing.JPanel; - import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Sets; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.CircleLayout; import edu.uci.ics.jung.algorithms.layout.FRLayout; import edu.uci.ics.jung.algorithms.layout.ISOMLayout; @@ -58,435 +34,450 @@ import edu.uci.ics.jung.visualization.subLayout.GraphCollapser; import edu.uci.ics.jung.visualization.util.Animator; import edu.uci.ics.jung.visualization.util.PredicatedParallelEdgeIndexFunction; - +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.geom.Point2D; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import javax.swing.BorderFactory; +import javax.swing.JApplet; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JPanel; /** - * A demo that shows how collections of vertices can be collapsed - * into a single vertex. In this demo, the vertices that are - * collapsed are those mouse-picked by the user. Any criteria - * could be used to form the vertex collections to be collapsed, - * perhaps some common characteristic of those vertex objects. - * - * Note that the collection types don't use generics in this - * demo, because the vertices are of two types: String for plain - * vertices, and {@code Network} for the collapsed vertices. - * + * A demo that shows how collections of vertices can be collapsed into a single vertex. In this + * demo, the vertices that are collapsed are those mouse-picked by the user. Any criteria could be + * used to form the vertex collections to be collapsed, perhaps some common characteristic of those + * vertex objects. + * + *

            Note that the collection types don't use generics in this demo, because the vertices are of + * two types: String for plain vertices, and {@code Network} for the collapsed + * vertices. + * * @author Tom Nelson - * */ @SuppressWarnings("serial") public class VertexCollapseDemoWithLayouts extends JApplet { - String instructions = - "Use the mouse to select multiple vertices"+ - "

            either by dragging a region, or by shift-clicking"+ - "

            on multiple vertices."+ - "

            After you select vertices, use the Collapse button"+ - "

            to combine them into a single vertex."+ - "

            Select a 'collapsed' vertex and use the Expand button"+ - "

            to restore the collapsed vertices."+ - "

            The Restore button will restore the original graph."+ - "

            If you select 2 (and only 2) vertices, then press"+ - "

            the Compress Edges button, parallel edges between"+ - "

            those two vertices will no longer be expanded."+ - "

            If you select 2 (and only 2) vertices, then press"+ - "

            the Expand Edges button, parallel edges between"+ - "

            those two vertices will be expanded."+ - "

            You can drag the vertices with the mouse." + - "

            Use the 'Picking'/'Transforming' combo-box to switch"+ - "

            between picking and transforming mode."; - /** - * the graph - */ - @SuppressWarnings("rawtypes") - Network graph; - @SuppressWarnings("rawtypes") - Network collapsedGraph; - - enum Layouts { - KAMADA_KAWAI, - FRUCHTERMAN_REINGOLD, - CIRCLE, - SPRING, - SPRING2, - SELF_ORGANIZING_MAP - }; - - - /** - * the visual component and renderer for the graph - */ - @SuppressWarnings("rawtypes") - VisualizationViewer vv; - - @SuppressWarnings("rawtypes") - Layout layout; - - GraphCollapser collapser; - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public VertexCollapseDemoWithLayouts() { - - // create a simple graph for the demo - graph = TestGraphs.getOneComponentGraph(); - collapsedGraph = graph; - collapser = new GraphCollapser(graph); - - layout = new FRLayout(graph.asGraph()); - - Dimension preferredSize = new Dimension(400,400); - final VisualizationModel visualizationModel = - new DefaultVisualizationModel(graph, layout, preferredSize); - vv = new VisualizationViewer(visualizationModel, preferredSize); - - vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeFunction()); - - final Set exclusions = new HashSet(); - final PredicatedParallelEdgeIndexFunction eif - = new PredicatedParallelEdgeIndexFunction(graph, new Predicate() { - public boolean apply(Object e) { - return exclusions.contains(e); - } - }); - vv.getRenderContext().setParallelEdgeIndexFunction(eif); - - vv.setBackground(Color.white); - - // add a listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller() { - - /* (non-Javadoc) - * @see edu.uci.ics.jung.visualization.decorators.DefaultToolTipFunction#getToolTipText(java.lang.Object) - */ - @Override - public String apply(Object v) { - if(v instanceof Network) { - return ((Network)v).nodes().toString(); - } - return super.apply(v); - }}); - - /** - * the regular graph mouse for the normal view - */ - final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse(); - - vv.setGraphMouse(graphMouse); - - Container content = getContentPane(); - GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv); - content.add(gzsp); - - JComboBox modeBox = graphMouse.getModeComboBox(); - modeBox.addItemListener(graphMouse.getModeListener()); - graphMouse.setMode(ModalGraphMouse.Mode.PICKING); - - Layouts[] combos = getCombos(); - final JComboBox jcb = new JComboBox(combos); - // use a renderer to shorten the layout name presentation -// jcb.setRenderer(new DefaultListCellRenderer() { -// public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { -// String valueString = value.toString(); -// valueString = valueString.substring(valueString.lastIndexOf('.')+1); -// return super.getListCellRendererComponent(list, valueString, index, isSelected, -// cellHasFocus); -// } -// }); - jcb.addActionListener(new LayoutChooser(jcb, vv)); -// jcb.setSelectedItem(FRLayout.class); - jcb.setSelectedItem(Layouts.FRUCHTERMAN_REINGOLD); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); + String instructions = + "Use the mouse to select multiple vertices" + + "

            either by dragging a region, or by shift-clicking" + + "

            on multiple vertices." + + "

            After you select vertices, use the Collapse button" + + "

            to combine them into a single vertex." + + "

            Select a 'collapsed' vertex and use the Expand button" + + "

            to restore the collapsed vertices." + + "

            The Restore button will restore the original graph." + + "

            If you select 2 (and only 2) vertices, then press" + + "

            the Compress Edges button, parallel edges between" + + "

            those two vertices will no longer be expanded." + + "

            If you select 2 (and only 2) vertices, then press" + + "

            the Expand Edges button, parallel edges between" + + "

            those two vertices will be expanded." + + "

            You can drag the vertices with the mouse." + + "

            Use the 'Picking'/'Transforming' combo-box to switch" + + "

            between picking and transforming mode."; + /** the graph */ + @SuppressWarnings("rawtypes") + Network graph; + + @SuppressWarnings("rawtypes") + Network collapsedGraph; + + enum Layouts { + KAMADA_KAWAI, + FRUCHTERMAN_REINGOLD, + CIRCLE, + SPRING, + SPRING2, + SELF_ORGANIZING_MAP + }; + + /** the visual component and renderer for the graph */ + @SuppressWarnings("rawtypes") + VisualizationViewer vv; + + @SuppressWarnings("rawtypes") + Layout layout; + + GraphCollapser collapser; + + @SuppressWarnings({"unchecked", "rawtypes"}) + public VertexCollapseDemoWithLayouts() { + + // create a simple graph for the demo + graph = TestGraphs.getOneComponentGraph(); + collapsedGraph = graph; + collapser = new GraphCollapser(graph); + + layout = new FRLayout(graph.asGraph()); + + Dimension preferredSize = new Dimension(400, 400); + final VisualizationModel visualizationModel = + new DefaultVisualizationModel(graph, layout, preferredSize); + vv = new VisualizationViewer(visualizationModel, preferredSize); + + vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeFunction()); + + final Set exclusions = new HashSet(); + final PredicatedParallelEdgeIndexFunction eif = + new PredicatedParallelEdgeIndexFunction( + graph, + new Predicate() { + public boolean apply(Object e) { + return exclusions.contains(e); + } + }); + vv.getRenderContext().setParallelEdgeIndexFunction(eif); + + vv.setBackground(Color.white); + + // add a listener for ToolTips + vv.setVertexToolTipTransformer( + new ToStringLabeller() { + + /* (non-Javadoc) + * @see edu.uci.ics.jung.visualization.decorators.DefaultToolTipFunction#getToolTipText(java.lang.Object) + */ + @Override + public String apply(Object v) { + if (v instanceof Network) { + return ((Network) v).nodes().toString(); } + return super.apply(v); + } + }); + + /** the regular graph mouse for the normal view */ + final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse(); + + vv.setGraphMouse(graphMouse); + + Container content = getContentPane(); + GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv); + content.add(gzsp); + + JComboBox modeBox = graphMouse.getModeComboBox(); + modeBox.addItemListener(graphMouse.getModeListener()); + graphMouse.setMode(ModalGraphMouse.Mode.PICKING); + + Layouts[] combos = getCombos(); + final JComboBox jcb = new JComboBox(combos); + // use a renderer to shorten the layout name presentation + // jcb.setRenderer(new DefaultListCellRenderer() { + // public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + // String valueString = value.toString(); + // valueString = valueString.substring(valueString.lastIndexOf('.')+1); + // return super.getListCellRendererComponent(list, valueString, index, isSelected, + // cellHasFocus); + // } + // }); + jcb.addActionListener(new LayoutChooser(jcb, vv)); + // jcb.setSelectedItem(FRLayout.class); + jcb.setSelectedItem(Layouts.FRUCHTERMAN_REINGOLD); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } + }); + + JButton collapse = new JButton("Collapse"); + collapse.addActionListener( + new ActionListener() { + + public void actionPerformed(ActionEvent e) { + // FIXME: this is tricky (here and in VertexCollapseDemo): need to make sure that the "inGraph" + // stays the right graph + Collection picked = new HashSet(vv.getPickedVertexState().getPicked()); + if (picked.size() > 1) { + // Network inGraph = layout.getGraph(); + // Network clusterGraph = collapser.getClusterGraph(inGraph, picked); + Network clusterGraph = collapser.getClusterGraph(collapsedGraph, picked); + + // Network g = collapser.collapse(layout.getGraph(), clusterGraph); + collapsedGraph = collapser.collapse(collapsedGraph, clusterGraph); + double sumx = 0; + double sumy = 0; + for (Object v : picked) { + Point2D p = (Point2D) layout.apply(v); + sumx += p.getX(); + sumy += p.getY(); + } + Point2D cp = new Point2D.Double(sumx / picked.size(), sumy / picked.size()); + vv.getRenderContext().getParallelEdgeIndexFunction().reset(); + // layout.setGraph(g); + layout = createLayout((Layouts) jcb.getSelectedItem(), collapsedGraph); + // layout.setGraph(collapsedGraph.asGraph()); + layout.setLocation(clusterGraph, cp); + vv.getPickedVertexState().clear(); + vv.repaint(); } + } }); - - JButton collapse = new JButton("Collapse"); - collapse.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - // FIXME: this is tricky (here and in VertexCollapseDemo): need to make sure that the "inGraph" - // stays the right graph - Collection picked = new HashSet(vv.getPickedVertexState().getPicked()); - if(picked.size() > 1) { -// Network inGraph = layout.getGraph(); -// Network clusterGraph = collapser.getClusterGraph(inGraph, picked); - Network clusterGraph = collapser.getClusterGraph(collapsedGraph, picked); - -// Network g = collapser.collapse(layout.getGraph(), clusterGraph); - collapsedGraph = collapser.collapse(collapsedGraph, clusterGraph); - double sumx = 0; - double sumy = 0; - for(Object v : picked) { - Point2D p = (Point2D)layout.apply(v); - sumx += p.getX(); - sumy += p.getY(); - } - Point2D cp = new Point2D.Double(sumx/picked.size(), sumy/picked.size()); - vv.getRenderContext().getParallelEdgeIndexFunction().reset(); -// layout.setGraph(g); - layout = createLayout((Layouts) jcb.getSelectedItem(), collapsedGraph); -// layout.setGraph(collapsedGraph.asGraph()); - layout.setLocation(clusterGraph, cp); - vv.getPickedVertexState().clear(); - vv.repaint(); - } - }}); - - JButton compressEdges = new JButton("Compress Edges"); - compressEdges.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - Collection picked = vv.getPickedVertexState().getPicked(); - if(picked.size() == 2) { - Iterator pickedIter = picked.iterator(); - Object nodeU = pickedIter.next(); - Object nodeV = pickedIter.next(); -// Pair pair = new Pair(picked); -// Network graph = layout.getGraph(); - Set edges = Sets.intersection( - collapsedGraph.incidentEdges(nodeU), - collapsedGraph.incidentEdges(nodeV)); -// Collection edges = new HashSet(graph.getIncidentEdges(pair.getFirst())); -// edges.retainAll(graph.getIncidentEdges(pair.getSecond())); - exclusions.addAll(edges); - vv.repaint(); - } - - }}); - - JButton expandEdges = new JButton("Expand Edges"); - expandEdges.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - Collection picked = vv.getPickedVertexState().getPicked(); - if(picked.size() == 2) { - Iterator pickedIter = picked.iterator(); - Object nodeU = pickedIter.next(); - Object nodeV = pickedIter.next(); -// Pair pair = new Pair(picked); -// Network graph = layout.getGraph(); - Set edges = Sets.intersection( - collapsedGraph.incidentEdges(nodeU), - collapsedGraph.incidentEdges(nodeV)); -// Collection edges = new HashSet(graph.getIncidentEdges(pair.getFirst())); -// edges.retainAll(graph.getIncidentEdges(pair.getSecond())); - exclusions.removeAll(edges); - vv.repaint(); - } - - }}); - - JButton expand = new JButton("Expand"); - expand.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - Collection picked = new HashSet(vv.getPickedVertexState().getPicked()); - for(Object v : picked) { - if(v instanceof Network) { -// Network g = collapser.expand(layout.getGraph(), (Network)v); - Network g = collapser.expand(collapsedGraph, (Network)v); - vv.getRenderContext().getParallelEdgeIndexFunction().reset(); - collapsedGraph = g; -// vv.getModel() // FIXME: need a setNetwork? - layout = createLayout((Layouts) jcb.getSelectedItem(), collapsedGraph); -// layout.setGraph(g); - } - vv.getPickedVertexState().clear(); - vv.repaint(); - } - }}); - - JButton reset = new JButton("Reset"); - reset.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - layout = createLayout((Layouts) jcb.getSelectedItem(), graph); -// layout.setGraph(graph); - exclusions.clear(); - vv.repaint(); - }}); - - JButton help = new JButton("Help"); - help.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - JOptionPane.showMessageDialog((JComponent)e.getSource(), instructions, "Help", JOptionPane.PLAIN_MESSAGE); + + JButton compressEdges = new JButton("Compress Edges"); + compressEdges.addActionListener( + new ActionListener() { + + public void actionPerformed(ActionEvent e) { + Collection picked = vv.getPickedVertexState().getPicked(); + if (picked.size() == 2) { + Iterator pickedIter = picked.iterator(); + Object nodeU = pickedIter.next(); + Object nodeV = pickedIter.next(); + // Pair pair = new Pair(picked); + // Network graph = layout.getGraph(); + Set edges = + Sets.intersection( + collapsedGraph.incidentEdges(nodeU), collapsedGraph.incidentEdges(nodeV)); + // Collection edges = new HashSet(graph.getIncidentEdges(pair.getFirst())); + // edges.retainAll(graph.getIncidentEdges(pair.getSecond())); + exclusions.addAll(edges); + vv.repaint(); } + } }); + JButton expandEdges = new JButton("Expand Edges"); + expandEdges.addActionListener( + new ActionListener() { + + public void actionPerformed(ActionEvent e) { + Collection picked = vv.getPickedVertexState().getPicked(); + if (picked.size() == 2) { + Iterator pickedIter = picked.iterator(); + Object nodeU = pickedIter.next(); + Object nodeV = pickedIter.next(); + // Pair pair = new Pair(picked); + // Network graph = layout.getGraph(); + Set edges = + Sets.intersection( + collapsedGraph.incidentEdges(nodeU), collapsedGraph.incidentEdges(nodeV)); + // Collection edges = new HashSet(graph.getIncidentEdges(pair.getFirst())); + // edges.retainAll(graph.getIncidentEdges(pair.getSecond())); + exclusions.removeAll(edges); + vv.repaint(); + } + } + }); - JPanel controls = new JPanel(); - JPanel zoomControls = new JPanel(new GridLayout(2,1)); - zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom")); - zoomControls.add(plus); - zoomControls.add(minus); - controls.add(zoomControls); - JPanel collapseControls = new JPanel(new GridLayout(3,1)); - collapseControls.setBorder(BorderFactory.createTitledBorder("Picked")); - collapseControls.add(collapse); - collapseControls.add(expand); - collapseControls.add(compressEdges); - collapseControls.add(expandEdges); - collapseControls.add(reset); - controls.add(collapseControls); - controls.add(modeBox); - controls.add(help); - controls.add(jcb); - content.add(controls, BorderLayout.SOUTH); - } - - /** - * a demo class that will create a vertex shape that is either a - * polygon or star. The number of sides corresponds to the number - * of vertices that were collapsed into the vertex represented by - * this shape. - * - * @author Tom Nelson - * - * @param the vertex type - */ - class ClusterVertexShapeFunction extends EllipseVertexShapeTransformer { - - ClusterVertexShapeFunction() { - setSizeTransformer(new ClusterVertexSizeFunction(20)); - } - @Override - public Shape apply(V v) { - if(v instanceof Network) { - @SuppressWarnings("rawtypes") - int size = ((Network)v).nodes().size(); - if (size < 8) { - int sides = Math.max(size, 3); - return factory.getRegularPolygon(v, sides); - } - else { - return factory.getRegularStar(v, size); - } + JButton expand = new JButton("Expand"); + expand.addActionListener( + new ActionListener() { + + public void actionPerformed(ActionEvent e) { + Collection picked = new HashSet(vv.getPickedVertexState().getPicked()); + for (Object v : picked) { + if (v instanceof Network) { + // Network g = collapser.expand(layout.getGraph(), (Network)v); + Network g = collapser.expand(collapsedGraph, (Network) v); + vv.getRenderContext().getParallelEdgeIndexFunction().reset(); + collapsedGraph = g; + // vv.getModel() // FIXME: need a setNetwork? + layout = createLayout((Layouts) jcb.getSelectedItem(), collapsedGraph); + // layout.setGraph(g); + } + vv.getPickedVertexState().clear(); + vv.repaint(); } - return super.apply(v); - } + } + }); + + JButton reset = new JButton("Reset"); + reset.addActionListener( + new ActionListener() { + + public void actionPerformed(ActionEvent e) { + layout = createLayout((Layouts) jcb.getSelectedItem(), graph); + // layout.setGraph(graph); + exclusions.clear(); + vv.repaint(); + } + }); + + JButton help = new JButton("Help"); + help.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + JOptionPane.showMessageDialog( + (JComponent) e.getSource(), instructions, "Help", JOptionPane.PLAIN_MESSAGE); + } + }); + + JPanel controls = new JPanel(); + JPanel zoomControls = new JPanel(new GridLayout(2, 1)); + zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom")); + zoomControls.add(plus); + zoomControls.add(minus); + controls.add(zoomControls); + JPanel collapseControls = new JPanel(new GridLayout(3, 1)); + collapseControls.setBorder(BorderFactory.createTitledBorder("Picked")); + collapseControls.add(collapse); + collapseControls.add(expand); + collapseControls.add(compressEdges); + collapseControls.add(expandEdges); + collapseControls.add(reset); + controls.add(collapseControls); + controls.add(modeBox); + controls.add(help); + controls.add(jcb); + content.add(controls, BorderLayout.SOUTH); + } + + /** + * a demo class that will create a vertex shape that is either a polygon or star. The number of + * sides corresponds to the number of vertices that were collapsed into the vertex represented by + * this shape. + * + * @author Tom Nelson + * @param the vertex type + */ + class ClusterVertexShapeFunction extends EllipseVertexShapeTransformer { + + ClusterVertexShapeFunction() { + setSizeTransformer(new ClusterVertexSizeFunction(20)); } - - /** - * A demo class that will make vertices larger if they represent - * a collapsed collection of original vertices - * @author Tom Nelson - * - * @param the vertex type - */ - class ClusterVertexSizeFunction implements Function { - int size; - public ClusterVertexSizeFunction(Integer size) { - this.size = size; - } - public Integer apply(V v) { - if(v instanceof Network) { - return 30; - } - return size; + @Override + public Shape apply(V v) { + if (v instanceof Network) { + @SuppressWarnings("rawtypes") + int size = ((Network) v).nodes().size(); + if (size < 8) { + int sides = Math.max(size, 3); + return factory.getRegularPolygon(v, sides); + } else { + return factory.getRegularStar(v, size); } + } + return super.apply(v); + } + } + + /** + * A demo class that will make vertices larger if they represent a collapsed collection of + * original vertices + * + * @author Tom Nelson + * @param the vertex type + */ + class ClusterVertexSizeFunction implements Function { + int size; + + public ClusterVertexSizeFunction(Integer size) { + this.size = size; } - private static Layout createLayout(Layouts layoutType, Network network) { - switch (layoutType) { - case CIRCLE: - return new CircleLayout(network.asGraph()); - case FRUCHTERMAN_REINGOLD: - return new FRLayout(network.asGraph()); - case KAMADA_KAWAI: - return new KKLayout(network.asGraph()); - case SELF_ORGANIZING_MAP: - return new ISOMLayout(network); - case SPRING: - return new SpringLayout(network.asGraph()); - case SPRING2: - return new SpringLayout2(network.asGraph()); - default: - throw new IllegalArgumentException("Unrecognized layout type"); - } + public Integer apply(V v) { + if (v instanceof Network) { + return 30; + } + return size; } - - private class LayoutChooser implements ActionListener - { - private final JComboBox jcb; - @SuppressWarnings("rawtypes") - private final VisualizationViewer vv; + } + + private static Layout createLayout(Layouts layoutType, Network network) { + switch (layoutType) { + case CIRCLE: + return new CircleLayout(network.asGraph()); + case FRUCHTERMAN_REINGOLD: + return new FRLayout(network.asGraph()); + case KAMADA_KAWAI: + return new KKLayout(network.asGraph()); + case SELF_ORGANIZING_MAP: + return new ISOMLayout(network); + case SPRING: + return new SpringLayout(network.asGraph()); + case SPRING2: + return new SpringLayout2(network.asGraph()); + default: + throw new IllegalArgumentException("Unrecognized layout type"); + } + } - private LayoutChooser(JComboBox jcb, VisualizationViewer vv) - { - super(); - this.jcb = jcb; - this.vv = vv; - } + private class LayoutChooser implements ActionListener { + private final JComboBox jcb; - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void actionPerformed(ActionEvent arg0) - { -// Object[] constructorArgs = -// { collapsedGraph }; - Layouts layoutType = (Layouts) jcb.getSelectedItem(); - -// Class layoutC = -// (Class) jcb.getSelectedItem(); - try - { - layout = createLayout(layoutType, collapsedGraph); -// Constructor constructor = layoutC -// .getConstructor(new Class[] {Graph.class}); -// Object o = constructor.newInstance(constructorArgs); -// Layout l = (Layout) o; - layout.setInitializer(vv.getGraphLayout()); - layout.setSize(vv.getSize()); -// layout = l; - LayoutTransition lt = - new LayoutTransition(vv, vv.getGraphLayout(), layout); - Animator animator = new Animator(lt); - animator.start(); - vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); - vv.repaint(); - - } - catch (Exception e) - { - e.printStackTrace(); - } - } - } - /** - * @return - */ -// @SuppressWarnings({ "unchecked", "rawtypes" }) - private Layouts[] getCombos() - { - return Layouts.values(); -// List> layouts = new ArrayList>(); -// layouts.add(KKLayout.class); -// layouts.add(FRLayout.class); -// layouts.add(CircleLayout.class); -// layouts.add(SpringLayout.class); -// layouts.add(SpringLayout2.class); -// layouts.add(ISOMLayout.class); -// return layouts.toArray(new Class[0]); + @SuppressWarnings("rawtypes") + private final VisualizationViewer vv; + + private LayoutChooser(JComboBox jcb, VisualizationViewer vv) { + super(); + this.jcb = jcb; + this.vv = vv; } - public static void main(String[] args) { - JFrame f = new JFrame(); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - f.getContentPane().add(new VertexCollapseDemoWithLayouts()); - f.pack(); - f.setVisible(true); + @SuppressWarnings({"unchecked", "rawtypes"}) + public void actionPerformed(ActionEvent arg0) { + // Object[] constructorArgs = + // { collapsedGraph }; + Layouts layoutType = (Layouts) jcb.getSelectedItem(); + + // Class layoutC = + // (Class) jcb.getSelectedItem(); + try { + layout = createLayout(layoutType, collapsedGraph); + // Constructor constructor = layoutC + // .getConstructor(new Class[] {Graph.class}); + // Object o = constructor.newInstance(constructorArgs); + // Layout l = (Layout) o; + layout.setInitializer(vv.getGraphLayout()); + layout.setSize(vv.getSize()); + // layout = l; + LayoutTransition lt = new LayoutTransition(vv, vv.getGraphLayout(), layout); + Animator animator = new Animator(lt); + animator.start(); + vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); + vv.repaint(); + + } catch (Exception e) { + e.printStackTrace(); + } } + } + /** @return */ + // @SuppressWarnings({ "unchecked", "rawtypes" }) + private Layouts[] getCombos() { + return Layouts.values(); + // List> layouts = new ArrayList>(); + // layouts.add(KKLayout.class); + // layouts.add(FRLayout.class); + // layouts.add(CircleLayout.class); + // layouts.add(SpringLayout.class); + // layouts.add(SpringLayout2.class); + // layouts.add(ISOMLayout.class); + // return layouts.toArray(new Class[0]); + } + + public static void main(String[] args) { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.getContentPane().add(new VertexCollapseDemoWithLayouts()); + f.pack(); + f.setVisible(true); + } } - - diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexImageShaperDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexImageShaperDemo.java index 3dbce763..3c986a16 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexImageShaperDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexImageShaperDemo.java @@ -1,13 +1,40 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; +import com.google.common.base.Function; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.Network; +import com.google.common.graph.NetworkBuilder; +import edu.uci.ics.jung.algorithms.layout.FRLayout; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.layout.util.RandomLocationTransformer; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.LayeredIcon; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer; +import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; +import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; +import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; +import edu.uci.ics.jung.visualization.decorators.VertexIconShapeTransformer; +import edu.uci.ics.jung.visualization.picking.PickedState; +import edu.uci.ics.jung.visualization.renderers.BasicVertexRenderer; +import edu.uci.ics.jung.visualization.renderers.Checkmark; +import edu.uci.ics.jung.visualization.renderers.DefaultEdgeLabelRenderer; +import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer; +import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; +import edu.uci.ics.jung.visualization.util.ImageShapeUtils; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; @@ -28,7 +55,6 @@ import java.awt.geom.Rectangle2D; import java.util.HashMap; import java.util.Map; - import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.ImageIcon; @@ -39,515 +65,464 @@ import javax.swing.JFrame; import javax.swing.JPanel; -import com.google.common.base.Function; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.Network; -import com.google.common.graph.NetworkBuilder; - -import edu.uci.ics.jung.algorithms.layout.FRLayout; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.algorithms.layout.util.RandomLocationTransformer; -import edu.uci.ics.jung.visualization.GraphZoomScrollPane; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.LayeredIcon; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; -import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ScalingControl; -import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer; -import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; -import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; -import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; -import edu.uci.ics.jung.visualization.decorators.VertexIconShapeTransformer; -import edu.uci.ics.jung.visualization.picking.PickedState; -import edu.uci.ics.jung.visualization.renderers.BasicVertexRenderer; -import edu.uci.ics.jung.visualization.renderers.Checkmark; -import edu.uci.ics.jung.visualization.renderers.DefaultEdgeLabelRenderer; -import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer; -import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; -import edu.uci.ics.jung.visualization.util.ImageShapeUtils; - /** - * Demonstrates the use of images to represent graph vertices. - * The images are supplied via the VertexShapeFunction so that - * both the image and its shape can be utilized. - * - * The images used in this demo (courtesy of slashdot.org) are - * rectangular but with a transparent background. When vertices - * are represented by these images, it looks better if the actual - * shape of the opaque part of the image is computed so that the - * edge arrowheads follow the visual shape of the image. This demo - * uses the FourPassImageShaper class to compute the Shape from - * an image with transparent background. - * + * Demonstrates the use of images to represent graph vertices. The images are supplied via the + * VertexShapeFunction so that both the image and its shape can be utilized. + * + *

            The images used in this demo (courtesy of slashdot.org) are rectangular but with a transparent + * background. When vertices are represented by these images, it looks better if the actual shape of + * the opaque part of the image is computed so that the edge arrowheads follow the visual shape of + * the image. This demo uses the FourPassImageShaper class to compute the Shape from an image with + * transparent background. + * * @author Tom Nelson - * */ public class VertexImageShaperDemo extends JApplet { - /** - * - */ - private static final long serialVersionUID = -4332663871914930864L; - - /** - * the graph - */ - Network graph; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - /** - * some icon names to use - */ - String[] iconNames = { - "apple", - "os", - "x", - "linux", - "inputdevices", - "wireless", - "graphics3", - "gamespcgames", - "humor", - "music", - "privacy" - }; - - public VertexImageShaperDemo() { - - // create a simple graph for the demo - graph = createGraph(); - - // Maps for the labels and icons - Map map = new HashMap(); - Map iconMap = new HashMap(); - for (Number node : graph.nodes()) { - int i = node.intValue(); - map.put(node, iconNames[i % iconNames.length]); - - String name = "/images/topic" + iconNames[i] + ".gif"; - try { - Icon icon = new LayeredIcon( - new ImageIcon(VertexImageShaperDemo.class.getResource(name)).getImage()); - iconMap.put(node, icon); - } catch (Exception ex) { - System.err.println( - "You need slashdoticons.jar in your classpath to see the image " + name); - } - } + /** */ + private static final long serialVersionUID = -4332663871914930864L; + + /** the graph */ + Network graph; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + /** some icon names to use */ + String[] iconNames = { + "apple", + "os", + "x", + "linux", + "inputdevices", + "wireless", + "graphics3", + "gamespcgames", + "humor", + "music", + "privacy" + }; + + public VertexImageShaperDemo() { + + // create a simple graph for the demo + graph = createGraph(); + + // Maps for the labels and icons + Map map = new HashMap(); + Map iconMap = new HashMap(); + for (Number node : graph.nodes()) { + int i = node.intValue(); + map.put(node, iconNames[i % iconNames.length]); + + String name = "/images/topic" + iconNames[i] + ".gif"; + try { + Icon icon = + new LayeredIcon( + new ImageIcon(VertexImageShaperDemo.class.getResource(name)).getImage()); + iconMap.put(node, icon); + } catch (Exception ex) { + System.err.println("You need slashdoticons.jar in your classpath to see the image " + name); + } + } - FRLayout layout = new FRLayout(graph.asGraph()); - layout.setMaxIterations(100); - layout.setInitializer(new RandomLocationTransformer(new Dimension(400,400), 0)); - vv = new VisualizationViewer(graph, layout, new Dimension(400,400)); - - // This demo uses a special renderer to turn outlines on and off. - // you do not need to do this in a real application. - // Instead, just let vv use the Renderer it already has - vv.getRenderer().setVertexRenderer(new DemoRenderer(layout, vv.getRenderContext())); - - Function vpf = - new PickableVertexPaintTransformer(vv.getPickedVertexState(), Color.white, Color.yellow); - vv.getRenderContext().setVertexFillPaintTransformer(vpf); - vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(vv.getPickedEdgeState(), Color.black, Color.cyan)); - - vv.setBackground(Color.white); - - - final Function vertexStringerImpl = - new VertexStringerImpl(map); - vv.getRenderContext().setVertexLabelTransformer(vertexStringerImpl); - vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan)); - vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan)); -// vv.getRenderContext().setEdgeLabelTransformer(new Function() { -// URL url = getClass().getResource("/images/lightning-s.gif"); -// public String transform(Number input) { -// -// return ""+input.toString(); -// }}); - - // For this demo only, I use a special class that lets me turn various - // features on and off. For a real application, use VertexIconShapeTransformer instead. - final DemoVertexIconShapeTransformer vertexIconShapeTransformer = - new DemoVertexIconShapeTransformer(new EllipseVertexShapeTransformer()); - vertexIconShapeTransformer.setIconMap(iconMap); - - final DemoVertexIconTransformer vertexIconTransformer - = new DemoVertexIconTransformer(iconMap); - - vv.getRenderContext().setVertexShapeTransformer(vertexIconShapeTransformer); - vv.getRenderContext().setVertexIconTransformer(vertexIconTransformer); - - // un-comment for RStar Tree visual testing - //vv.addPostRenderPaintable(new BoundingRectanglePaintable(vv.getRenderContext(), vv.getGraphLayout())); - - // Get the pickedState and add a listener that will decorate the - // Vertex images with a checkmark icon when they are picked - PickedState ps = vv.getPickedVertexState(); - ps.addItemListener(new PickWithIconListener(vertexIconTransformer)); - - vv.addPostRenderPaintable(new VisualizationViewer.Paintable(){ - int x; - int y; - Font font; - FontMetrics metrics; - int swidth; - int sheight; - String str = "Thank You, slashdot.org, for the images!"; - - public void paint(Graphics g) { - Dimension d = vv.getSize(); - if(font == null) { - font = new Font(g.getFont().getName(), Font.BOLD, 20); - metrics = g.getFontMetrics(font); - swidth = metrics.stringWidth(str); - sheight = metrics.getMaxAscent()+metrics.getMaxDescent(); - x = (d.width-swidth)/2; - y = (int)(d.height-sheight*1.5); - } - g.setFont(font); - Color oldColor = g.getColor(); - g.setColor(Color.lightGray); - g.drawString(str, x, y); - g.setColor(oldColor); - } - public boolean useTransform() { - return false; + FRLayout layout = new FRLayout(graph.asGraph()); + layout.setMaxIterations(100); + layout.setInitializer(new RandomLocationTransformer(new Dimension(400, 400), 0)); + vv = new VisualizationViewer(graph, layout, new Dimension(400, 400)); + + // This demo uses a special renderer to turn outlines on and off. + // you do not need to do this in a real application. + // Instead, just let vv use the Renderer it already has + vv.getRenderer().setVertexRenderer(new DemoRenderer(layout, vv.getRenderContext())); + + Function vpf = + new PickableVertexPaintTransformer( + vv.getPickedVertexState(), Color.white, Color.yellow); + vv.getRenderContext().setVertexFillPaintTransformer(vpf); + vv.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer( + vv.getPickedEdgeState(), Color.black, Color.cyan)); + + vv.setBackground(Color.white); + + final Function vertexStringerImpl = new VertexStringerImpl(map); + vv.getRenderContext().setVertexLabelTransformer(vertexStringerImpl); + vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan)); + vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan)); + // vv.getRenderContext().setEdgeLabelTransformer(new Function() { + // URL url = getClass().getResource("/images/lightning-s.gif"); + // public String transform(Number input) { + // + // return ""+input.toString(); + // }}); + + // For this demo only, I use a special class that lets me turn various + // features on and off. For a real application, use VertexIconShapeTransformer instead. + final DemoVertexIconShapeTransformer vertexIconShapeTransformer = + new DemoVertexIconShapeTransformer(new EllipseVertexShapeTransformer()); + vertexIconShapeTransformer.setIconMap(iconMap); + + final DemoVertexIconTransformer vertexIconTransformer = + new DemoVertexIconTransformer(iconMap); + + vv.getRenderContext().setVertexShapeTransformer(vertexIconShapeTransformer); + vv.getRenderContext().setVertexIconTransformer(vertexIconTransformer); + + // un-comment for RStar Tree visual testing + //vv.addPostRenderPaintable(new BoundingRectanglePaintable(vv.getRenderContext(), vv.getGraphLayout())); + + // Get the pickedState and add a listener that will decorate the + // Vertex images with a checkmark icon when they are picked + PickedState ps = vv.getPickedVertexState(); + ps.addItemListener(new PickWithIconListener(vertexIconTransformer)); + + vv.addPostRenderPaintable( + new VisualizationViewer.Paintable() { + int x; + int y; + Font font; + FontMetrics metrics; + int swidth; + int sheight; + String str = "Thank You, slashdot.org, for the images!"; + + public void paint(Graphics g) { + Dimension d = vv.getSize(); + if (font == null) { + font = new Font(g.getFont().getName(), Font.BOLD, 20); + metrics = g.getFontMetrics(font); + swidth = metrics.stringWidth(str); + sheight = metrics.getMaxAscent() + metrics.getMaxDescent(); + x = (d.width - swidth) / 2; + y = (int) (d.height - sheight * 1.5); } + g.setFont(font); + Color oldColor = g.getColor(); + g.setColor(Color.lightGray); + g.drawString(str, x, y); + g.setColor(oldColor); + } + + public boolean useTransform() { + return false; + } }); - // add a listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - - Container content = getContentPane(); - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - content.add(panel); - - final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse(); - vv.setGraphMouse(graphMouse); - vv.addKeyListener(graphMouse.getModeKeyListener()); - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + // add a listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + + Container content = getContentPane(); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + content.add(panel); + + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + vv.setGraphMouse(graphMouse); + vv.addKeyListener(graphMouse.getModeKeyListener()); + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - JCheckBox shape = new JCheckBox("Shape"); - shape.addItemListener(new ItemListener(){ + JCheckBox shape = new JCheckBox("Shape"); + shape.addItemListener( + new ItemListener() { - public void itemStateChanged(ItemEvent e) { - vertexIconShapeTransformer.setShapeImages(e.getStateChange()==ItemEvent.SELECTED); - vv.repaint(); - } + public void itemStateChanged(ItemEvent e) { + vertexIconShapeTransformer.setShapeImages(e.getStateChange() == ItemEvent.SELECTED); + vv.repaint(); + } }); - shape.setSelected(true); + shape.setSelected(true); - JCheckBox fill = new JCheckBox("Fill"); - fill.addItemListener(new ItemListener(){ + JCheckBox fill = new JCheckBox("Fill"); + fill.addItemListener( + new ItemListener() { - public void itemStateChanged(ItemEvent e) { - vertexIconTransformer.setFillImages(e.getStateChange()==ItemEvent.SELECTED); - vv.repaint(); - } + public void itemStateChanged(ItemEvent e) { + vertexIconTransformer.setFillImages(e.getStateChange() == ItemEvent.SELECTED); + vv.repaint(); + } }); - fill.setSelected(true); - - JCheckBox drawOutlines = new JCheckBox("Outline"); - drawOutlines.addItemListener(new ItemListener(){ - - public void itemStateChanged(ItemEvent e) { - vertexIconTransformer.setOutlineImages(e.getStateChange()==ItemEvent.SELECTED); - vv.repaint(); - } + fill.setSelected(true); + + JCheckBox drawOutlines = new JCheckBox("Outline"); + drawOutlines.addItemListener( + new ItemListener() { + + public void itemStateChanged(ItemEvent e) { + vertexIconTransformer.setOutlineImages(e.getStateChange() == ItemEvent.SELECTED); + vv.repaint(); + } }); - - JComboBox modeBox = graphMouse.getModeComboBox(); - JPanel modePanel = new JPanel(); - modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); - modePanel.add(modeBox); - - JPanel scaleGrid = new JPanel(new GridLayout(1,0)); - scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); - JPanel labelFeatures = new JPanel(new GridLayout(1,0)); - labelFeatures.setBorder(BorderFactory.createTitledBorder("Image Effects")); - JPanel controls = new JPanel(); - scaleGrid.add(plus); - scaleGrid.add(minus); - controls.add(scaleGrid); - labelFeatures.add(shape); - labelFeatures.add(fill); - labelFeatures.add(drawOutlines); - - controls.add(labelFeatures); - controls.add(modePanel); - content.add(controls, BorderLayout.SOUTH); + + JComboBox modeBox = graphMouse.getModeComboBox(); + JPanel modePanel = new JPanel(); + modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode")); + modePanel.add(modeBox); + + JPanel scaleGrid = new JPanel(new GridLayout(1, 0)); + scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); + JPanel labelFeatures = new JPanel(new GridLayout(1, 0)); + labelFeatures.setBorder(BorderFactory.createTitledBorder("Image Effects")); + JPanel controls = new JPanel(); + scaleGrid.add(plus); + scaleGrid.add(minus); + controls.add(scaleGrid); + labelFeatures.add(shape); + labelFeatures.add(fill); + labelFeatures.add(drawOutlines); + + controls.add(labelFeatures); + controls.add(modePanel); + content.add(controls, BorderLayout.SOUTH); + } + + /** + * When Vertices are picked, add a checkmark icon to the imager. Remove the icon when a Vertex is + * unpicked + * + * @author Tom Nelson + */ + public static class PickWithIconListener implements ItemListener { + Function imager; + Icon checked; + + public PickWithIconListener(Function imager) { + this.imager = imager; + checked = new Checkmark(); } - - /** - * When Vertices are picked, add a checkmark icon to the imager. - * Remove the icon when a Vertex is unpicked - * @author Tom Nelson - * - */ - public static class PickWithIconListener implements ItemListener { - Function imager; - Icon checked; - - public PickWithIconListener(Function imager) { - this.imager = imager; - checked = new Checkmark(); - } - public void itemStateChanged(ItemEvent e) { - @SuppressWarnings("unchecked") - Icon icon = imager.apply((V)e.getItem()); - if(icon != null && icon instanceof LayeredIcon) { - if(e.getStateChange() == ItemEvent.SELECTED) { - ((LayeredIcon)icon).add(checked); - } else { - ((LayeredIcon)icon).remove(checked); - } - } + public void itemStateChanged(ItemEvent e) { + @SuppressWarnings("unchecked") + Icon icon = imager.apply((V) e.getItem()); + if (icon != null && icon instanceof LayeredIcon) { + if (e.getStateChange() == ItemEvent.SELECTED) { + ((LayeredIcon) icon).add(checked); + } else { + ((LayeredIcon) icon).remove(checked); } + } + } + } + /** + * A simple implementation of VertexStringer that gets Vertex labels from a Map + * + * @author Tom Nelson + */ + public static class VertexStringerImpl implements Function { + + Map map = new HashMap(); + + boolean enabled = true; + + public VertexStringerImpl(Map map) { + this.map = map; } - /** - * A simple implementation of VertexStringer that - * gets Vertex labels from a Map - * - * @author Tom Nelson - * - * + + /* (non-Javadoc) + * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.Vertex) */ - public static class VertexStringerImpl implements Function { - - Map map = new HashMap(); - - boolean enabled = true; - - public VertexStringerImpl(Map map) { - this.map = map; - } - - /* (non-Javadoc) - * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.Vertex) - */ - public String apply(V v) { - if(isEnabled()) { - return map.get(v); - } else { - return ""; - } - } - - /** - * @return Returns the enabled. - */ - public boolean isEnabled() { - return enabled; - } - - /** - * @param enabled The enabled to set. - */ - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } + public String apply(V v) { + if (isEnabled()) { + return map.get(v); + } else { + return ""; + } } - - Network createGraph() { - MutableNetwork graph = NetworkBuilder.directed().build(); - graph.addEdge(0, 1, new Double(Math.random())); - graph.addEdge(3, 0, new Double(Math.random())); - graph.addEdge(0, 4, new Double(Math.random())); - graph.addEdge(4, 5, new Double(Math.random())); - graph.addEdge(5, 3, new Double(Math.random())); - graph.addEdge(2, 1, new Double(Math.random())); - graph.addEdge(4, 1, new Double(Math.random())); - graph.addEdge(8, 2, new Double(Math.random())); - graph.addEdge(3, 8, new Double(Math.random())); - graph.addEdge(6, 7, new Double(Math.random())); - graph.addEdge(7, 5, new Double(Math.random())); - graph.addEdge(0, 9, new Double(Math.random())); - graph.addEdge(9, 8, new Double(Math.random())); - graph.addEdge(7, 6, new Double(Math.random())); - graph.addEdge(6, 5, new Double(Math.random())); - graph.addEdge(4, 2, new Double(Math.random())); - graph.addEdge(5, 4, new Double(Math.random())); - graph.addEdge(4, 10, new Double(Math.random())); - graph.addEdge(10, 4, new Double(Math.random())); - - return graph; + + /** @return Returns the enabled. */ + public boolean isEnabled() { + return enabled; } - /** - * This class exists only to provide settings to turn on/off shapes and image fill - * in this demo. - * - *

            For a real application, just use {@code Functions.forMap(iconMap)} to provide a - * {@code Function}. - */ - public static class DemoVertexIconTransformer implements Function { - boolean fillImages = true; - boolean outlineImages = false; - Map iconMap = new HashMap(); - - public DemoVertexIconTransformer(Map iconMap) { - this.iconMap = iconMap; - } + /** @param enabled The enabled to set. */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + } + + Network createGraph() { + MutableNetwork graph = NetworkBuilder.directed().build(); + graph.addEdge(0, 1, new Double(Math.random())); + graph.addEdge(3, 0, new Double(Math.random())); + graph.addEdge(0, 4, new Double(Math.random())); + graph.addEdge(4, 5, new Double(Math.random())); + graph.addEdge(5, 3, new Double(Math.random())); + graph.addEdge(2, 1, new Double(Math.random())); + graph.addEdge(4, 1, new Double(Math.random())); + graph.addEdge(8, 2, new Double(Math.random())); + graph.addEdge(3, 8, new Double(Math.random())); + graph.addEdge(6, 7, new Double(Math.random())); + graph.addEdge(7, 5, new Double(Math.random())); + graph.addEdge(0, 9, new Double(Math.random())); + graph.addEdge(9, 8, new Double(Math.random())); + graph.addEdge(7, 6, new Double(Math.random())); + graph.addEdge(6, 5, new Double(Math.random())); + graph.addEdge(4, 2, new Double(Math.random())); + graph.addEdge(5, 4, new Double(Math.random())); + graph.addEdge(4, 10, new Double(Math.random())); + graph.addEdge(10, 4, new Double(Math.random())); + + return graph; + } + + /** + * This class exists only to provide settings to turn on/off shapes and image fill in this demo. + * + *

            For a real application, just use {@code Functions.forMap(iconMap)} to provide a {@code + * Function}. + */ + public static class DemoVertexIconTransformer implements Function { + boolean fillImages = true; + boolean outlineImages = false; + Map iconMap = new HashMap(); + + public DemoVertexIconTransformer(Map iconMap) { + this.iconMap = iconMap; + } - /** - * @return Returns the fillImages. - */ - public boolean isFillImages() { - return fillImages; - } - /** - * @param fillImages The fillImages to set. - */ - public void setFillImages(boolean fillImages) { - this.fillImages = fillImages; - } + /** @return Returns the fillImages. */ + public boolean isFillImages() { + return fillImages; + } + /** @param fillImages The fillImages to set. */ + public void setFillImages(boolean fillImages) { + this.fillImages = fillImages; + } - public boolean isOutlineImages() { - return outlineImages; - } - public void setOutlineImages(boolean outlineImages) { - this.outlineImages = outlineImages; - } - - public Icon apply(V v) { - if(fillImages) { - return (Icon)iconMap.get(v); - } else { - return null; - } - } + public boolean isOutlineImages() { + return outlineImages; } - - /** - * this class exists only to provide settings to turn on/off shapes and image fill - * in this demo. - * In a real application, use VertexIconShapeTransformer instead. - * - */ - public static class DemoVertexIconShapeTransformer extends VertexIconShapeTransformer { - - boolean shapeImages = true; - public DemoVertexIconShapeTransformer(Function delegate) { - super(delegate); - } + public void setOutlineImages(boolean outlineImages) { + this.outlineImages = outlineImages; + } - /** - * @return Returns the shapeImages. - */ - public boolean isShapeImages() { - return shapeImages; - } - /** - * @param shapeImages The shapeImages to set. - */ - public void setShapeImages(boolean shapeImages) { - shapeMap.clear(); - this.shapeImages = shapeImages; - } + public Icon apply(V v) { + if (fillImages) { + return (Icon) iconMap.get(v); + } else { + return null; + } + } + } - public Shape transform(V v) { - Icon icon = (Icon) iconMap.get(v); - - if (icon != null && icon instanceof ImageIcon) { - - Image image = ((ImageIcon) icon).getImage(); - - Shape shape = shapeMap.get(image); - if (shape == null) { - if (shapeImages) { - shape = ImageShapeUtils.getShape(image, 30); - } else { - shape = new Rectangle2D.Float(0, 0, - image.getWidth(null), image.getHeight(null)); - } - if(shape.getBounds().getWidth() > 0 && - shape.getBounds().getHeight() > 0) { - int width = image.getWidth(null); - int height = image.getHeight(null); - AffineTransform transform = - AffineTransform.getTranslateInstance(-width / 2, -height / 2); - shape = transform.createTransformedShape(shape); - shapeMap.put(image, shape); - } - } - return shape; - } else { - return delegate.apply(v); - } - } + /** + * this class exists only to provide settings to turn on/off shapes and image fill in this demo. + * In a real application, use VertexIconShapeTransformer instead. + */ + public static class DemoVertexIconShapeTransformer extends VertexIconShapeTransformer { + + boolean shapeImages = true; + + public DemoVertexIconShapeTransformer(Function delegate) { + super(delegate); } - - /** - * a special renderer that can turn outlines on and off - * in this demo. - * You won't need this for a real application. - * Use BasicVertexRenderer instead - * - * @author Tom Nelson - * - */ - class DemoRenderer extends BasicVertexRenderer { - - public DemoRenderer(Layout layout, RenderContext rc) { - super(layout, rc); - } - public void paintIconForVertex(V v) { - - Point2D p = layout.apply(v); - p = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p); - float x = (float)p.getX(); - float y = (float)p.getY(); - - GraphicsDecorator g = renderContext.getGraphicsContext(); - boolean outlineImages = false; - Function vertexIconFunction = renderContext.getVertexIconTransformer(); - - if(vertexIconFunction instanceof DemoVertexIconTransformer) { - outlineImages = ((DemoVertexIconTransformer)vertexIconFunction).isOutlineImages(); - } - Icon icon = vertexIconFunction.apply(v); - if(icon == null || outlineImages) { - - Shape s = AffineTransform.getTranslateInstance(x,y). - createTransformedShape(renderContext.getVertexShapeTransformer().apply(v)); - paintShapeForVertex(v, s); - } - if(icon != null) { - int xLoc = (int) (x - icon.getIconWidth()/2); - int yLoc = (int) (y - icon.getIconHeight()/2); - icon.paintIcon(renderContext.getScreenDevice(), g.getDelegate(), xLoc, yLoc); - } + + /** @return Returns the shapeImages. */ + public boolean isShapeImages() { + return shapeImages; + } + /** @param shapeImages The shapeImages to set. */ + public void setShapeImages(boolean shapeImages) { + shapeMap.clear(); + this.shapeImages = shapeImages; + } + + public Shape transform(V v) { + Icon icon = (Icon) iconMap.get(v); + + if (icon != null && icon instanceof ImageIcon) { + + Image image = ((ImageIcon) icon).getImage(); + + Shape shape = shapeMap.get(image); + if (shape == null) { + if (shapeImages) { + shape = ImageShapeUtils.getShape(image, 30); + } else { + shape = new Rectangle2D.Float(0, 0, image.getWidth(null), image.getHeight(null)); + } + if (shape.getBounds().getWidth() > 0 && shape.getBounds().getHeight() > 0) { + int width = image.getWidth(null); + int height = image.getHeight(null); + AffineTransform transform = + AffineTransform.getTranslateInstance(-width / 2, -height / 2); + shape = transform.createTransformedShape(shape); + shapeMap.put(image, shape); + } } + return shape; + } else { + return delegate.apply(v); + } } - - public static void main(String[] args) { - JFrame frame = new JFrame(); - Container content = frame.getContentPane(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - content.add(new VertexImageShaperDemo()); - frame.pack(); - frame.setVisible(true); + } + + /** + * a special renderer that can turn outlines on and off in this demo. You won't need this for a + * real application. Use BasicVertexRenderer instead + * + * @author Tom Nelson + */ + class DemoRenderer extends BasicVertexRenderer { + + public DemoRenderer(Layout layout, RenderContext rc) { + super(layout, rc); } + + public void paintIconForVertex(V v) { + + Point2D p = layout.apply(v); + p = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p); + float x = (float) p.getX(); + float y = (float) p.getY(); + + GraphicsDecorator g = renderContext.getGraphicsContext(); + boolean outlineImages = false; + Function vertexIconFunction = renderContext.getVertexIconTransformer(); + + if (vertexIconFunction instanceof DemoVertexIconTransformer) { + outlineImages = ((DemoVertexIconTransformer) vertexIconFunction).isOutlineImages(); + } + Icon icon = vertexIconFunction.apply(v); + if (icon == null || outlineImages) { + + Shape s = + AffineTransform.getTranslateInstance(x, y) + .createTransformedShape(renderContext.getVertexShapeTransformer().apply(v)); + paintShapeForVertex(v, s); + } + if (icon != null) { + int xLoc = (int) (x - icon.getIconWidth() / 2); + int yLoc = (int) (y - icon.getIconHeight() / 2); + icon.paintIcon(renderContext.getScreenDevice(), g.getDelegate(), xLoc, yLoc); + } + } + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + Container content = frame.getContentPane(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + content.add(new VertexImageShaperDemo()); + frame.pack(); + frame.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexLabelAsShapeDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexLabelAsShapeDemo.java index b5d9b8a2..c9fd3cbe 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexLabelAsShapeDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexLabelAsShapeDemo.java @@ -1,35 +1,16 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BasicStroke; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.GridLayout; -import java.awt.Paint; -import java.awt.Stroke; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JApplet; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JFrame; -import javax.swing.JPanel; - import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.FRLayout; import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.graph.util.TestGraphs; @@ -45,118 +26,131 @@ import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer; import edu.uci.ics.jung.visualization.renderers.GradientVertexRenderer; import edu.uci.ics.jung.visualization.renderers.VertexLabelAsShapeRenderer; - +import java.awt.BasicStroke; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.Paint; +import java.awt.Stroke; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.BorderFactory; +import javax.swing.JApplet; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JPanel; /** - * This demo shows how to use the vertex labels themselves as - * the vertex shapes. Additionally, it shows html labels - * so they are multi-line, and gradient painting of the - * vertex labels. - * + * This demo shows how to use the vertex labels themselves as the vertex shapes. Additionally, it + * shows html labels so they are multi-line, and gradient painting of the vertex labels. + * * @author Tom Nelson - * */ public class VertexLabelAsShapeDemo extends JApplet { - /** - * - */ - private static final long serialVersionUID = 1017336668368978842L; - - Network graph; - - VisualizationViewer vv; - - Layout layout; - - /** - * create an instance of a simple graph with basic controls - */ - public VertexLabelAsShapeDemo() { - - // create a simple graph for the demo - graph = TestGraphs.getOneComponentGraph(); - - layout = new FRLayout(graph.asGraph()); - - Dimension preferredSize = new Dimension(400,400); - final VisualizationModel visualizationModel = - new DefaultVisualizationModel(graph, layout, preferredSize); - vv = new VisualizationViewer(visualizationModel, preferredSize); - - // this class will provide both label drawing and vertex shapes - VertexLabelAsShapeRenderer vlasr - = new VertexLabelAsShapeRenderer(layout, vv.getRenderContext()); - - // customize the render context - vv.getRenderContext().setVertexLabelTransformer( - // this chains together Functions so that the html tags - // are prepended to the toString method output - Functions.compose( - new Function(){ - public String apply(String input) { - return "

            Vertex

            "+input; - }}, new ToStringLabeller())); - vv.getRenderContext().setVertexShapeTransformer(vlasr); - vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.red)); - vv.getRenderContext().setEdgeDrawPaintTransformer(Functions.constant(Color.yellow)); - vv.getRenderContext().setEdgeStrokeTransformer(Functions.constant(new BasicStroke(2.5f))); - - // customize the renderer - vv.getRenderer().setVertexRenderer(new GradientVertexRenderer(vv, Color.gray, Color.white, true)); - vv.getRenderer().setVertexLabelRenderer(vlasr); - - vv.setBackground(Color.black); - - // add a listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - - final DefaultModalGraphMouse graphMouse = - new DefaultModalGraphMouse(); - - vv.setGraphMouse(graphMouse); - vv.addKeyListener(graphMouse.getModeKeyListener()); - - Container content = getContentPane(); - GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv); - content.add(gzsp); - - JComboBox modeBox = graphMouse.getModeComboBox(); - modeBox.addItemListener(graphMouse.getModeListener()); - graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + /** */ + private static final long serialVersionUID = 1017336668368978842L; + + Network graph; + + VisualizationViewer vv; + + Layout layout; + + /** create an instance of a simple graph with basic controls */ + public VertexLabelAsShapeDemo() { + + // create a simple graph for the demo + graph = TestGraphs.getOneComponentGraph(); + + layout = new FRLayout(graph.asGraph()); + + Dimension preferredSize = new Dimension(400, 400); + final VisualizationModel visualizationModel = + new DefaultVisualizationModel(graph, layout, preferredSize); + vv = new VisualizationViewer(visualizationModel, preferredSize); + + // this class will provide both label drawing and vertex shapes + VertexLabelAsShapeRenderer vlasr = + new VertexLabelAsShapeRenderer(layout, vv.getRenderContext()); + + // customize the render context + vv.getRenderContext() + .setVertexLabelTransformer( + // this chains together Functions so that the html tags + // are prepended to the toString method output + Functions.compose( + new Function() { + public String apply(String input) { + return "

            Vertex

            " + input; + } + }, + new ToStringLabeller())); + vv.getRenderContext().setVertexShapeTransformer(vlasr); + vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.red)); + vv.getRenderContext().setEdgeDrawPaintTransformer(Functions.constant(Color.yellow)); + vv.getRenderContext() + .setEdgeStrokeTransformer(Functions.constant(new BasicStroke(2.5f))); + + // customize the renderer + vv.getRenderer() + .setVertexRenderer(new GradientVertexRenderer(vv, Color.gray, Color.white, true)); + vv.getRenderer().setVertexLabelRenderer(vlasr); + + vv.setBackground(Color.black); + + // add a listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + + vv.setGraphMouse(graphMouse); + vv.addKeyListener(graphMouse.getModeKeyListener()); + + Container content = getContentPane(); + GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv); + content.add(gzsp); + + JComboBox modeBox = graphMouse.getModeComboBox(); + modeBox.addItemListener(graphMouse.getModeListener()); + graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - - JPanel controls = new JPanel(); - JPanel zoomControls = new JPanel(new GridLayout(2,1)); - zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom")); - zoomControls.add(plus); - zoomControls.add(minus); - controls.add(zoomControls); - controls.add(modeBox); - content.add(controls, BorderLayout.SOUTH); - } - - public static void main(String[] args) { - JFrame f = new JFrame(); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - f.getContentPane().add(new VertexLabelAsShapeDemo()); - f.pack(); - f.setVisible(true); - } -} + JPanel controls = new JPanel(); + JPanel zoomControls = new JPanel(new GridLayout(2, 1)); + zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom")); + zoomControls.add(plus); + zoomControls.add(minus); + controls.add(zoomControls); + controls.add(modeBox); + content.add(controls, BorderLayout.SOUTH); + } + public static void main(String[] args) { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.getContentPane().add(new VertexLabelAsShapeDemo()); + f.pack(); + f.setVisible(true); + } +} diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexLabelPositionDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexLabelPositionDemo.java index 88e91dbd..c13fcc8d 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexLabelPositionDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/VertexLabelPositionDemo.java @@ -1,33 +1,14 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; - -import javax.swing.BorderFactory; -import javax.swing.JApplet; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JFrame; -import javax.swing.JMenuBar; -import javax.swing.JPanel; - import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.FRLayout; import edu.uci.ics.jung.graph.util.TestGraphs; import edu.uci.ics.jung.visualization.DefaultVisualizationModel; @@ -43,133 +24,143 @@ import edu.uci.ics.jung.visualization.picking.PickedState; import edu.uci.ics.jung.visualization.renderers.Renderer; import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import javax.swing.BorderFactory; +import javax.swing.JApplet; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JMenuBar; +import javax.swing.JPanel; /** - * Demonstrates vertex label positioning - * controlled by the user. - * In the AUTO setting, labels are placed according to - * which quadrant the vertex is in - * + * Demonstrates vertex label positioning controlled by the user. In the AUTO setting, labels are + * placed according to which quadrant the vertex is in + * * @author Tom Nelson - * */ @SuppressWarnings("serial") public class VertexLabelPositionDemo extends JApplet { - /** - * the graph - */ - Network graph; - - FRLayout graphLayout; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - ScalingControl scaler; - - /** - * create an instance of a simple graph with controls to - * demo the zoomand hyperbolic features. - * - */ - public VertexLabelPositionDemo() { - - // create a simple graph for the demo - graph = TestGraphs.getOneComponentGraph(); - - graphLayout = new FRLayout(graph.asGraph()); - graphLayout.setMaxIterations(1000); - - Dimension preferredSize = new Dimension(600,600); - - final VisualizationModel visualizationModel = - new DefaultVisualizationModel(graph, graphLayout, preferredSize); - vv = new VisualizationViewer(visualizationModel, preferredSize); - - PickedState ps = vv.getPickedVertexState(); - PickedState pes = vv.getPickedEdgeState(); - vv.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer(ps, Color.red, Color.yellow)); - vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(pes, Color.black, Color.cyan)); - vv.setBackground(Color.white); - vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.W); - - vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - - // add a listener for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - - Container content = getContentPane(); - GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv); - content.add(gzsp); - - /** - * the regular graph mouse for the normal view - */ - final DefaultModalGraphMouse graphMouse - = new DefaultModalGraphMouse(); - - vv.setGraphMouse(graphMouse); - vv.addKeyListener(graphMouse.getModeKeyListener()); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); - } + /** the graph */ + Network graph; + + FRLayout graphLayout; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + ScalingControl scaler; + + /** create an instance of a simple graph with controls to demo the zoomand hyperbolic features. */ + public VertexLabelPositionDemo() { + + // create a simple graph for the demo + graph = TestGraphs.getOneComponentGraph(); + + graphLayout = new FRLayout(graph.asGraph()); + graphLayout.setMaxIterations(1000); + + Dimension preferredSize = new Dimension(600, 600); + + final VisualizationModel visualizationModel = + new DefaultVisualizationModel(graph, graphLayout, preferredSize); + vv = new VisualizationViewer(visualizationModel, preferredSize); + + PickedState ps = vv.getPickedVertexState(); + PickedState pes = vv.getPickedEdgeState(); + vv.getRenderContext() + .setVertexFillPaintTransformer( + new PickableVertexPaintTransformer(ps, Color.red, Color.yellow)); + vv.getRenderContext() + .setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer(pes, Color.black, Color.cyan)); + vv.setBackground(Color.white); + vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.W); + + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + + // add a listener for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + + Container content = getContentPane(); + GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv); + content.add(gzsp); + + /** the regular graph mouse for the normal view */ + final DefaultModalGraphMouse graphMouse = + new DefaultModalGraphMouse(); + + vv.setGraphMouse(graphMouse); + vv.addKeyListener(graphMouse.getModeKeyListener()); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); - } + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } }); - JPanel positionPanel = new JPanel(); - positionPanel.setBorder(BorderFactory.createTitledBorder("Label Position")); - JMenuBar menubar = new JMenuBar(); - menubar.add(graphMouse.getModeMenu()); - gzsp.setCorner(menubar); - JComboBox cb = new JComboBox(); - cb.addItem(Renderer.VertexLabel.Position.N); - cb.addItem(Renderer.VertexLabel.Position.NE); - cb.addItem(Renderer.VertexLabel.Position.E); - cb.addItem(Renderer.VertexLabel.Position.SE); - cb.addItem(Renderer.VertexLabel.Position.S); - cb.addItem(Renderer.VertexLabel.Position.SW); - cb.addItem(Renderer.VertexLabel.Position.W); - cb.addItem(Renderer.VertexLabel.Position.NW); - cb.addItem(Renderer.VertexLabel.Position.N); - cb.addItem(Renderer.VertexLabel.Position.CNTR); - cb.addItem(Renderer.VertexLabel.Position.AUTO); - cb.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - Renderer.VertexLabel.Position position = - (Renderer.VertexLabel.Position)e.getItem(); - vv.getRenderer().getVertexLabelRenderer().setPosition(position); - vv.repaint(); - }}); - cb.setSelectedItem(Renderer.VertexLabel.Position.SE); - positionPanel.add(cb); - JPanel controls = new JPanel(); - JPanel zoomControls = new JPanel(new GridLayout(2,1)); - zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom")); - zoomControls.add(plus); - zoomControls.add(minus); - - controls.add(zoomControls); - controls.add(positionPanel); - content.add(controls, BorderLayout.SOUTH); - } - - public static void main(String[] args) { - JFrame f = new JFrame(); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - f.getContentPane().add(new VertexLabelPositionDemo()); - f.pack(); - f.setVisible(true); - } + JPanel positionPanel = new JPanel(); + positionPanel.setBorder(BorderFactory.createTitledBorder("Label Position")); + JMenuBar menubar = new JMenuBar(); + menubar.add(graphMouse.getModeMenu()); + gzsp.setCorner(menubar); + JComboBox cb = new JComboBox(); + cb.addItem(Renderer.VertexLabel.Position.N); + cb.addItem(Renderer.VertexLabel.Position.NE); + cb.addItem(Renderer.VertexLabel.Position.E); + cb.addItem(Renderer.VertexLabel.Position.SE); + cb.addItem(Renderer.VertexLabel.Position.S); + cb.addItem(Renderer.VertexLabel.Position.SW); + cb.addItem(Renderer.VertexLabel.Position.W); + cb.addItem(Renderer.VertexLabel.Position.NW); + cb.addItem(Renderer.VertexLabel.Position.N); + cb.addItem(Renderer.VertexLabel.Position.CNTR); + cb.addItem(Renderer.VertexLabel.Position.AUTO); + cb.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + Renderer.VertexLabel.Position position = (Renderer.VertexLabel.Position) e.getItem(); + vv.getRenderer().getVertexLabelRenderer().setPosition(position); + vv.repaint(); + } + }); + cb.setSelectedItem(Renderer.VertexLabel.Position.SE); + positionPanel.add(cb); + JPanel controls = new JPanel(); + JPanel zoomControls = new JPanel(new GridLayout(2, 1)); + zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom")); + zoomControls.add(plus); + zoomControls.add(minus); + + controls.add(zoomControls); + controls.add(positionPanel); + content.add(controls, BorderLayout.SOUTH); + } + + public static void main(String[] args) { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.getContentPane().add(new VertexLabelPositionDemo()); + f.pack(); + f.setVisible(true); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/VisualizationImageServerDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/VisualizationImageServerDemo.java index 9d34da46..251a1292 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/VisualizationImageServerDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/VisualizationImageServerDemo.java @@ -1,122 +1,109 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; -import java.awt.Color; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.Image; -import java.awt.Paint; -import java.awt.geom.Point2D; - -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JFrame; -import javax.swing.JLabel; - import com.google.common.base.Functions; import com.google.common.graph.MutableNetwork; import com.google.common.graph.Network; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.layout.KKLayout; import edu.uci.ics.jung.visualization.VisualizationImageServer; import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; import edu.uci.ics.jung.visualization.renderers.BasicVertexLabelRenderer.InsidePositioner; import edu.uci.ics.jung.visualization.renderers.GradientVertexRenderer; import edu.uci.ics.jung.visualization.renderers.Renderer; - +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Image; +import java.awt.Paint; +import java.awt.geom.Point2D; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; /** * Demonstrates VisualizationImageServer. + * * @author Tom Nelson - * */ public class VisualizationImageServerDemo { - /** - * the graph - */ - Network graph; + /** the graph */ + Network graph; + + /** the visual component and renderer for the graph */ + VisualizationImageServer vv; + + /** */ + public VisualizationImageServerDemo() { + + // create a simple graph for the demo + graph = createGraph(); + + vv = + new VisualizationImageServer( + graph, new KKLayout(graph.asGraph()), new Dimension(600, 600)); + + vv.getRenderer() + .setVertexRenderer( + new GradientVertexRenderer( + vv, Color.white, Color.red, Color.white, Color.blue, false)); + vv.getRenderContext().setEdgeDrawPaintTransformer(Functions.constant(Color.lightGray)); + vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); + vv.getRenderContext().setArrowDrawPaintTransformer(Functions.constant(Color.lightGray)); + + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + vv.getRenderer().getVertexLabelRenderer().setPositioner(new InsidePositioner()); + vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.AUTO); - /** - * the visual component and renderer for the graph - */ - VisualizationImageServer vv; - - /** - * - */ - public VisualizationImageServerDemo() { - - // create a simple graph for the demo - graph = createGraph(); + // create a frome to hold the graph + final JFrame frame = new JFrame(); + Container content = frame.getContentPane(); - vv = new VisualizationImageServer(graph, - new KKLayout(graph.asGraph()), - new Dimension(600,600)); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - vv.getRenderer().setVertexRenderer( - new GradientVertexRenderer(vv, - Color.white, Color.red, - Color.white, Color.blue, - false)); - vv.getRenderContext().setEdgeDrawPaintTransformer(Functions.constant(Color.lightGray)); - vv.getRenderContext().setArrowFillPaintTransformer(Functions.constant(Color.lightGray)); - vv.getRenderContext().setArrowDrawPaintTransformer(Functions.constant(Color.lightGray)); - - vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - vv.getRenderer().getVertexLabelRenderer().setPositioner(new InsidePositioner()); - vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.AUTO); + Image im = vv.getImage(new Point2D.Double(300, 300), new Dimension(600, 600)); + Icon icon = new ImageIcon(im); + JLabel label = new JLabel(icon); + content.add(label); + frame.pack(); + frame.setVisible(true); + } - // create a frome to hold the graph - final JFrame frame = new JFrame(); - Container content = frame.getContentPane(); + Network createGraph() { + MutableNetwork graph = NetworkBuilder.directed().build(); + graph.addEdge(0, 1, new Double(Math.random())); + graph.addEdge(3, 0, new Double(Math.random())); + graph.addEdge(0, 4, new Double(Math.random())); + graph.addEdge(4, 5, new Double(Math.random())); + graph.addEdge(5, 3, new Double(Math.random())); + graph.addEdge(2, 1, new Double(Math.random())); + graph.addEdge(4, 1, new Double(Math.random())); + graph.addEdge(8, 2, new Double(Math.random())); + graph.addEdge(3, 8, new Double(Math.random())); + graph.addEdge(6, 7, new Double(Math.random())); + graph.addEdge(7, 5, new Double(Math.random())); + graph.addEdge(0, 9, new Double(Math.random())); + graph.addEdge(9, 8, new Double(Math.random())); + graph.addEdge(7, 6, new Double(Math.random())); + graph.addEdge(6, 5, new Double(Math.random())); + graph.addEdge(4, 2, new Double(Math.random())); + graph.addEdge(5, 4, new Double(Math.random())); + graph.addEdge(4, 10, new Double(Math.random())); + graph.addEdge(10, 4, new Double(Math.random())); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - Image im = vv.getImage(new Point2D.Double(300,300), new Dimension(600,600)); - Icon icon = new ImageIcon(im); - JLabel label = new JLabel(icon); - content.add(label); - frame.pack(); - frame.setVisible(true); - } - - Network createGraph() { - MutableNetwork graph = NetworkBuilder.directed().build(); - graph.addEdge(0, 1, new Double(Math.random())); - graph.addEdge(3, 0, new Double(Math.random())); - graph.addEdge(0, 4, new Double(Math.random())); - graph.addEdge(4, 5, new Double(Math.random())); - graph.addEdge(5, 3, new Double(Math.random())); - graph.addEdge(2, 1, new Double(Math.random())); - graph.addEdge(4, 1, new Double(Math.random())); - graph.addEdge(8, 2, new Double(Math.random())); - graph.addEdge(3, 8, new Double(Math.random())); - graph.addEdge(6, 7, new Double(Math.random())); - graph.addEdge(7, 5, new Double(Math.random())); - graph.addEdge(0, 9, new Double(Math.random())); - graph.addEdge(9, 8, new Double(Math.random())); - graph.addEdge(7, 6, new Double(Math.random())); - graph.addEdge(6, 5, new Double(Math.random())); - graph.addEdge(4, 2, new Double(Math.random())); - graph.addEdge(5, 4, new Double(Math.random())); - graph.addEdge(4, 10, new Double(Math.random())); - graph.addEdge(10, 4, new Double(Math.random())); - - return graph; - } + return graph; + } - public static void main(String[] args) - { - new VisualizationImageServerDemo(); - - } + public static void main(String[] args) { + new VisualizationImageServerDemo(); + } } diff --git a/jung-samples/src/main/java/edu/uci/ics/jung/samples/WorldMapGraphDemo.java b/jung-samples/src/main/java/edu/uci/ics/jung/samples/WorldMapGraphDemo.java index 54852419..377b6514 100644 --- a/jung-samples/src/main/java/edu/uci/ics/jung/samples/WorldMapGraphDemo.java +++ b/jung-samples/src/main/java/edu/uci/ics/jung/samples/WorldMapGraphDemo.java @@ -1,13 +1,31 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.samples; +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.graph.MutableNetwork; +import com.google.common.graph.Network; +import com.google.common.graph.NetworkBuilder; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.layout.StaticLayout; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.AbstractModalGraphMouse; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; +import edu.uci.ics.jung.visualization.renderers.BasicVertexLabelRenderer.InsidePositioner; +import edu.uci.ics.jung.visualization.renderers.GradientVertexRenderer; +import edu.uci.ics.jung.visualization.renderers.Renderer; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; @@ -22,274 +40,257 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - import javax.swing.ImageIcon; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.graph.MutableNetwork; -import com.google.common.graph.Network; -import com.google.common.graph.NetworkBuilder; - -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.algorithms.layout.StaticLayout; -import edu.uci.ics.jung.visualization.GraphZoomScrollPane; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.AbstractModalGraphMouse; -import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; -import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; -import edu.uci.ics.jung.visualization.control.ScalingControl; -import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; -import edu.uci.ics.jung.visualization.renderers.BasicVertexLabelRenderer.InsidePositioner; -import edu.uci.ics.jung.visualization.renderers.GradientVertexRenderer; -import edu.uci.ics.jung.visualization.renderers.Renderer; - - /** - * Shows a graph overlaid on a world map image. - * Scaling of the graph also scales the image background. + * Shows a graph overlaid on a world map image. Scaling of the graph also scales the image + * background. + * * @author Tom Nelson - * */ @SuppressWarnings("serial") public class WorldMapGraphDemo extends JApplet { - /** - * the graph - */ - Network graph; - - /** - * the visual component and renderer for the graph - */ - VisualizationViewer vv; - - List cityList; - - - /** - * create an instance of a simple graph with controls to - * demo the zoom features. - * - */ - public WorldMapGraphDemo() { - setLayout(new BorderLayout()); - - Map map = buildMap(); - - cityList = new ArrayList(map.keySet()); - - // create a simple graph for the demo - graph = buildGraph(map); - - ImageIcon mapIcon = null; - String imageLocation = "/images/political_world_map.jpg"; - try { - mapIcon = - new ImageIcon(getClass().getResource(imageLocation)); - } catch(Exception ex) { - System.err.println("Can't load \""+imageLocation+"\""); - } - final ImageIcon icon = mapIcon; - - Dimension layoutSize = new Dimension(2000,1000); - - Layout layout = new StaticLayout(graph.asGraph(), - Functions.compose( - new LatLonPixelTransformer(new Dimension(2000,1000)), - new CityTransformer(map)) - ); - - layout.setSize(layoutSize); - vv = new VisualizationViewer(graph, layout, - new Dimension(800,400)); - - if(icon != null) { - vv.addPreRenderPaintable(new VisualizationViewer.Paintable(){ - public void paint(Graphics g) { - Graphics2D g2d = (Graphics2D)g; - AffineTransform oldXform = g2d.getTransform(); - AffineTransform lat = - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getTransform(); - AffineTransform vat = - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getTransform(); - AffineTransform at = new AffineTransform(); - at.concatenate(g2d.getTransform()); - at.concatenate(vat); - at.concatenate(lat); - g2d.setTransform(at); - g.drawImage(icon.getImage(), 0, 0, - icon.getIconWidth(),icon.getIconHeight(),vv); - g2d.setTransform(oldXform); - } - public boolean useTransform() { return false; } - }); - } - - vv.getRenderer().setVertexRenderer( - new GradientVertexRenderer(vv, - Color.white, Color.red, - Color.white, Color.blue, - false)); - - // add my listeners for ToolTips - vv.setVertexToolTipTransformer(new ToStringLabeller()); - vv.setEdgeToolTipTransformer(new Function() { - public String apply(Number edge) { - return "E"+graph.incidentNodes(edge).toString(); - }}); - - vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); - vv.getRenderer().getVertexLabelRenderer().setPositioner(new InsidePositioner()); - vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.AUTO); - - final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); - add(panel); - final AbstractModalGraphMouse graphMouse = new DefaultModalGraphMouse(); - vv.setGraphMouse(graphMouse); - - vv.addKeyListener(graphMouse.getModeKeyListener()); - vv.setToolTipText("

            Type 'p' for Pick mode

            Type 't' for Transform mode"); - - final ScalingControl scaler = new CrossoverScalingControl(); - - JButton plus = new JButton("+"); - plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1.1f, vv.getCenter()); + /** the graph */ + Network graph; + + /** the visual component and renderer for the graph */ + VisualizationViewer vv; + + List cityList; + + /** create an instance of a simple graph with controls to demo the zoom features. */ + public WorldMapGraphDemo() { + setLayout(new BorderLayout()); + + Map map = buildMap(); + + cityList = new ArrayList(map.keySet()); + + // create a simple graph for the demo + graph = buildGraph(map); + + ImageIcon mapIcon = null; + String imageLocation = "/images/political_world_map.jpg"; + try { + mapIcon = new ImageIcon(getClass().getResource(imageLocation)); + } catch (Exception ex) { + System.err.println("Can't load \"" + imageLocation + "\""); + } + final ImageIcon icon = mapIcon; + + Dimension layoutSize = new Dimension(2000, 1000); + + Layout layout = + new StaticLayout( + graph.asGraph(), + Functions.compose( + new LatLonPixelTransformer(new Dimension(2000, 1000)), new CityTransformer(map))); + + layout.setSize(layoutSize); + vv = new VisualizationViewer(graph, layout, new Dimension(800, 400)); + + if (icon != null) { + vv.addPreRenderPaintable( + new VisualizationViewer.Paintable() { + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + AffineTransform oldXform = g2d.getTransform(); + AffineTransform lat = + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .getTransform(); + AffineTransform vat = + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.VIEW) + .getTransform(); + AffineTransform at = new AffineTransform(); + at.concatenate(g2d.getTransform()); + at.concatenate(vat); + at.concatenate(lat); + g2d.setTransform(at); + g.drawImage(icon.getImage(), 0, 0, icon.getIconWidth(), icon.getIconHeight(), vv); + g2d.setTransform(oldXform); } - }); - JButton minus = new JButton("-"); - minus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - scaler.scale(vv, 1/1.1f, vv.getCenter()); + + public boolean useTransform() { + return false; } + }); + } + + vv.getRenderer() + .setVertexRenderer( + new GradientVertexRenderer( + vv, Color.white, Color.red, Color.white, Color.blue, false)); + + // add my listeners for ToolTips + vv.setVertexToolTipTransformer(new ToStringLabeller()); + vv.setEdgeToolTipTransformer( + new Function() { + public String apply(Number edge) { + return "E" + graph.incidentNodes(edge).toString(); + } }); - JButton reset = new JButton("reset"); - reset.addActionListener(new ActionListener() { + vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); + vv.getRenderer().getVertexLabelRenderer().setPositioner(new InsidePositioner()); + vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.AUTO); - public void actionPerformed(ActionEvent e) { - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).setToIdentity(); - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).setToIdentity(); - }}); + final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); + add(panel); + final AbstractModalGraphMouse graphMouse = new DefaultModalGraphMouse(); + vv.setGraphMouse(graphMouse); - JPanel controls = new JPanel(); - controls.add(plus); - controls.add(minus); - controls.add(reset); - add(controls, BorderLayout.SOUTH); - } + vv.addKeyListener(graphMouse.getModeKeyListener()); + vv.setToolTipText("

            Type 'p' for Pick mode

            Type 't' for Transform mode"); + + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1.1f, vv.getCenter()); + } + }); + JButton minus = new JButton("-"); + minus.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(vv, 1 / 1.1f, vv.getCenter()); + } + }); + + JButton reset = new JButton("reset"); + reset.addActionListener( + new ActionListener() { + + public void actionPerformed(ActionEvent e) { + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .setToIdentity(); + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.VIEW) + .setToIdentity(); + } + }); - private Map buildMap() { - Map map = new HashMap(); - - map.put("TYO", new String[] {"35 40 N", "139 45 E"}); - map.put("PEK", new String[] {"39 55 N", "116 26 E"}); - map.put("MOW", new String[] {"55 45 N", "37 42 E"}); - map.put("JRS", new String[] {"31 47 N", "35 13 E"}); - map.put("CAI", new String[] {"30 03 N", "31 15 E"}); - map.put("CPT", new String[] {"33 55 S", "18 22 E"}); - map.put("PAR", new String[] {"48 52 N", "2 20 E"}); - map.put("LHR", new String[] {"51 30 N", "0 10 W"}); - map.put("HNL", new String[] {"21 18 N", "157 51 W"}); - map.put("NYC", new String[] {"40 77 N", "73 98 W"}); - map.put("SFO", new String[] {"37 62 N", "122 38 W"}); - map.put("AKL", new String[] {"36 55 S", "174 47 E"}); - map.put("BNE", new String[] {"27 28 S", "153 02 E"}); - map.put("HKG", new String[] {"22 15 N", "114 10 E"}); - map.put("KTM", new String[] {"27 42 N", "85 19 E"}); - map.put("IST", new String[] {"41 01 N", "28 58 E"}); - map.put("STO", new String[] {"59 20 N", "18 03 E"}); - map.put("RIO", new String[] {"22 54 S", "43 14 W"}); - map.put("LIM", new String[] {"12 03 S", "77 03 W"}); - map.put("YTO", new String[] {"43 39 N", "79 23 W"}); - - return map; + JPanel controls = new JPanel(); + controls.add(plus); + controls.add(minus); + controls.add(reset); + add(controls, BorderLayout.SOUTH); + } + + private Map buildMap() { + Map map = new HashMap(); + + map.put("TYO", new String[] {"35 40 N", "139 45 E"}); + map.put("PEK", new String[] {"39 55 N", "116 26 E"}); + map.put("MOW", new String[] {"55 45 N", "37 42 E"}); + map.put("JRS", new String[] {"31 47 N", "35 13 E"}); + map.put("CAI", new String[] {"30 03 N", "31 15 E"}); + map.put("CPT", new String[] {"33 55 S", "18 22 E"}); + map.put("PAR", new String[] {"48 52 N", "2 20 E"}); + map.put("LHR", new String[] {"51 30 N", "0 10 W"}); + map.put("HNL", new String[] {"21 18 N", "157 51 W"}); + map.put("NYC", new String[] {"40 77 N", "73 98 W"}); + map.put("SFO", new String[] {"37 62 N", "122 38 W"}); + map.put("AKL", new String[] {"36 55 S", "174 47 E"}); + map.put("BNE", new String[] {"27 28 S", "153 02 E"}); + map.put("HKG", new String[] {"22 15 N", "114 10 E"}); + map.put("KTM", new String[] {"27 42 N", "85 19 E"}); + map.put("IST", new String[] {"41 01 N", "28 58 E"}); + map.put("STO", new String[] {"59 20 N", "18 03 E"}); + map.put("RIO", new String[] {"22 54 S", "43 14 W"}); + map.put("LIM", new String[] {"12 03 S", "77 03 W"}); + map.put("YTO", new String[] {"43 39 N", "79 23 W"}); + + return map; + } + + private Network buildGraph(Map map) { + MutableNetwork graph = + NetworkBuilder.directed().allowsParallelEdges(true).build(); + for (String city : map.keySet()) { + graph.addNode(city); } - - private Network buildGraph(Map map) { - MutableNetwork graph - = NetworkBuilder.directed().allowsParallelEdges(true).build(); - for (String city : map.keySet()) { - graph.addNode(city); - } - for (int i = 0; i < map.keySet().size() * 1.3; i++) { - graph.addEdge(randomCity(), randomCity(), new Double(Math.random())); - } - return graph; + for (int i = 0; i < map.keySet().size() * 1.3; i++) { + graph.addEdge(randomCity(), randomCity(), new Double(Math.random())); } - - private String randomCity() { - int m = cityList.size(); - return cityList.get((int)(Math.random()*m)); + return graph; + } + + private String randomCity() { + int m = cityList.size(); + return cityList.get((int) (Math.random() * m)); + } + + static class CityTransformer implements Function { + + Map map; + + public CityTransformer(Map map) { + this.map = map; } - - static class CityTransformer implements Function { - - Map map; - public CityTransformer(Map map) { - this.map = map; - } - - /** - * transform airport code to latlon string - */ - public String[] apply(String city) { - return map.get(city); - } + + /** transform airport code to latlon string */ + public String[] apply(String city) { + return map.get(city); } - - static class LatLonPixelTransformer implements Function { - Dimension d; - int startOffset; - - public LatLonPixelTransformer(Dimension d) { - this.d = d; - } - /** - * transform a lat - */ - public Point2D apply(String[] latlon) { - double latitude = 0; - double longitude = 0; - String[] lat = latlon[0].split(" "); - String[] lon = latlon[1].split(" "); - latitude = Integer.parseInt(lat[0]) + Integer.parseInt(lat[1])/60f; - latitude *= d.height/180f; - longitude = Integer.parseInt(lon[0]) + Integer.parseInt(lon[1])/60f; - longitude *= d.width/360f; - if(lat[2].equals("N")) { - latitude = d.height / 2 - latitude; - - } else { // assume S - latitude = d.height / 2 + latitude; - } - - if(lon[2].equals("W")) { - longitude = d.width / 2 - longitude; - - } else { // assume E - longitude = d.width / 2 + longitude; - } - - return new Point2D.Double(longitude,latitude); - } - + } + + static class LatLonPixelTransformer implements Function { + Dimension d; + int startOffset; + + public LatLonPixelTransformer(Dimension d) { + this.d = d; } + /** transform a lat */ + public Point2D apply(String[] latlon) { + double latitude = 0; + double longitude = 0; + String[] lat = latlon[0].split(" "); + String[] lon = latlon[1].split(" "); + latitude = Integer.parseInt(lat[0]) + Integer.parseInt(lat[1]) / 60f; + latitude *= d.height / 180f; + longitude = Integer.parseInt(lon[0]) + Integer.parseInt(lon[1]) / 60f; + longitude *= d.width / 360f; + if (lat[2].equals("N")) { + latitude = d.height / 2 - latitude; + + } else { // assume S + latitude = d.height / 2 + latitude; + } + + if (lon[2].equals("W")) { + longitude = d.width / 2 - longitude; - public static void main(String[] args) { - // create a frome to hold the graph - final JFrame frame = new JFrame(); - Container content = frame.getContentPane(); - content.add(new WorldMapGraphDemo()); - frame.pack(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setVisible(true); + } else { // assume E + longitude = d.width / 2 + longitude; + } + + return new Point2D.Double(longitude, latitude); } + } + + public static void main(String[] args) { + // create a frome to hold the graph + final JFrame frame = new JFrame(); + Container content = frame.getContentPane(); + content.add(new WorldMapGraphDemo()); + frame.pack(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/BasicTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/BasicTransformer.java index c621fd19..6c4c2765 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/BasicTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/BasicTransformer.java @@ -1,173 +1,168 @@ package edu.uci.ics.jung.visualization; -import java.awt.Shape; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; - -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - import edu.uci.ics.jung.visualization.transform.MutableAffineTransformer; import edu.uci.ics.jung.visualization.transform.MutableTransformer; import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer; import edu.uci.ics.jung.visualization.util.ChangeEventSupport; import edu.uci.ics.jung.visualization.util.DefaultChangeEventSupport; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; /** - * A basic implementation of the MultiLayerTransformer interface that - * provides two Layers: VIEW and LAYOUT. It also provides ChangeEventSupport - * @author Tom Nelson - tomnelson@dev.java.net + * A basic implementation of the MultiLayerTransformer interface that provides two Layers: VIEW and + * LAYOUT. It also provides ChangeEventSupport * + * @author Tom Nelson - tomnelson@dev.java.net */ -public class BasicTransformer implements MultiLayerTransformer, - ShapeTransformer, ChangeListener, ChangeEventSupport { - - protected ChangeEventSupport changeSupport = - new DefaultChangeEventSupport(this); - - protected MutableTransformer viewTransformer = - new MutableAffineTransformer(new AffineTransform()); - - protected MutableTransformer layoutTransformer = - new MutableAffineTransformer(new AffineTransform()); - - /** - * Creates an instance and notifies the view and layout Functions to listen to - * changes published by this instance. - */ - public BasicTransformer() { - super(); - viewTransformer.addChangeListener(this); - layoutTransformer.addChangeListener(this); - } - - protected void setViewTransformer(MutableTransformer Function) { - this.viewTransformer.removeChangeListener(this); - this.viewTransformer = Function; - this.viewTransformer.addChangeListener(this); - } - - protected void setLayoutTransformer(MutableTransformer Function) { - this.layoutTransformer.removeChangeListener(this); - this.layoutTransformer = Function; - this.layoutTransformer.addChangeListener(this); - } - - - protected MutableTransformer getLayoutTransformer() { - return layoutTransformer; - } - - protected MutableTransformer getViewTransformer() { - return viewTransformer; - } - - public Point2D inverseTransform(Point2D p) { - return inverseLayoutTransform(inverseViewTransform(p)); - } - - protected Point2D inverseViewTransform(Point2D p) { - return viewTransformer.inverseTransform(p); - } - - protected Point2D inverseLayoutTransform(Point2D p) { - return layoutTransformer.inverseTransform(p); - } - - public Point2D transform(Point2D p) { - return viewTransform(layoutTransform(p)); - } - - protected Point2D viewTransform(Point2D p) { - return viewTransformer.transform(p); - } - - protected Point2D layoutTransform(Point2D p) { - return layoutTransformer.transform(p); - } - - public Shape inverseTransform(Shape shape) { - return inverseLayoutTransform(inverseViewTransform(shape)); - } - - protected Shape inverseViewTransform(Shape shape) { - return viewTransformer.inverseTransform(shape); - } - - protected Shape inverseLayoutTransform(Shape shape) { - return layoutTransformer.inverseTransform(shape); - } - - public Shape transform(Shape shape) { - return viewTransform(layoutTransform(shape)); - } - - protected Shape viewTransform(Shape shape) { - return viewTransformer.transform(shape); - } - - protected Shape layoutTransform(Shape shape) { - return layoutTransformer.transform(shape); - } - - public void setToIdentity() { - layoutTransformer.setToIdentity(); - viewTransformer.setToIdentity(); - } - - public void addChangeListener(ChangeListener l) { - changeSupport.addChangeListener(l); - } - - public void removeChangeListener(ChangeListener l) { - changeSupport.removeChangeListener(l); - } - - public ChangeListener[] getChangeListeners() { - return changeSupport.getChangeListeners(); - } - - public void fireStateChanged() { - changeSupport.fireStateChanged(); - } - - public void stateChanged(ChangeEvent e) { - fireStateChanged(); - } - - public MutableTransformer getTransformer(Layer layer) { - if(layer == Layer.LAYOUT) return layoutTransformer; - if(layer == Layer.VIEW) return viewTransformer; - return null; - } - - public Point2D inverseTransform(Layer layer, Point2D p) { - if(layer == Layer.LAYOUT) return inverseLayoutTransform(p); - if(layer == Layer.VIEW) return inverseViewTransform(p); - return null; - } - - public void setTransformer(Layer layer, MutableTransformer Function) { - if(layer == Layer.LAYOUT) setLayoutTransformer(Function); - if(layer == Layer.VIEW) setViewTransformer(Function); - - } - - public Point2D transform(Layer layer, Point2D p) { - if(layer == Layer.LAYOUT) return layoutTransform(p); - if(layer == Layer.VIEW) return viewTransform(p); - return null; - } - - public Shape transform(Layer layer, Shape shape) { - if(layer == Layer.LAYOUT) return layoutTransform(shape); - if(layer == Layer.VIEW) return viewTransform(shape); - return null; - } - - public Shape inverseTransform(Layer layer, Shape shape) { - if(layer == Layer.LAYOUT) return inverseLayoutTransform(shape); - if(layer == Layer.VIEW) return inverseViewTransform(shape); - return null; - } +public class BasicTransformer + implements MultiLayerTransformer, ShapeTransformer, ChangeListener, ChangeEventSupport { + + protected ChangeEventSupport changeSupport = new DefaultChangeEventSupport(this); + + protected MutableTransformer viewTransformer = + new MutableAffineTransformer(new AffineTransform()); + + protected MutableTransformer layoutTransformer = + new MutableAffineTransformer(new AffineTransform()); + + /** + * Creates an instance and notifies the view and layout Functions to listen to changes published + * by this instance. + */ + public BasicTransformer() { + super(); + viewTransformer.addChangeListener(this); + layoutTransformer.addChangeListener(this); + } + + protected void setViewTransformer(MutableTransformer Function) { + this.viewTransformer.removeChangeListener(this); + this.viewTransformer = Function; + this.viewTransformer.addChangeListener(this); + } + + protected void setLayoutTransformer(MutableTransformer Function) { + this.layoutTransformer.removeChangeListener(this); + this.layoutTransformer = Function; + this.layoutTransformer.addChangeListener(this); + } + + protected MutableTransformer getLayoutTransformer() { + return layoutTransformer; + } + + protected MutableTransformer getViewTransformer() { + return viewTransformer; + } + + public Point2D inverseTransform(Point2D p) { + return inverseLayoutTransform(inverseViewTransform(p)); + } + + protected Point2D inverseViewTransform(Point2D p) { + return viewTransformer.inverseTransform(p); + } + + protected Point2D inverseLayoutTransform(Point2D p) { + return layoutTransformer.inverseTransform(p); + } + + public Point2D transform(Point2D p) { + return viewTransform(layoutTransform(p)); + } + + protected Point2D viewTransform(Point2D p) { + return viewTransformer.transform(p); + } + + protected Point2D layoutTransform(Point2D p) { + return layoutTransformer.transform(p); + } + + public Shape inverseTransform(Shape shape) { + return inverseLayoutTransform(inverseViewTransform(shape)); + } + + protected Shape inverseViewTransform(Shape shape) { + return viewTransformer.inverseTransform(shape); + } + + protected Shape inverseLayoutTransform(Shape shape) { + return layoutTransformer.inverseTransform(shape); + } + + public Shape transform(Shape shape) { + return viewTransform(layoutTransform(shape)); + } + + protected Shape viewTransform(Shape shape) { + return viewTransformer.transform(shape); + } + + protected Shape layoutTransform(Shape shape) { + return layoutTransformer.transform(shape); + } + + public void setToIdentity() { + layoutTransformer.setToIdentity(); + viewTransformer.setToIdentity(); + } + + public void addChangeListener(ChangeListener l) { + changeSupport.addChangeListener(l); + } + + public void removeChangeListener(ChangeListener l) { + changeSupport.removeChangeListener(l); + } + + public ChangeListener[] getChangeListeners() { + return changeSupport.getChangeListeners(); + } + + public void fireStateChanged() { + changeSupport.fireStateChanged(); + } + + public void stateChanged(ChangeEvent e) { + fireStateChanged(); + } + + public MutableTransformer getTransformer(Layer layer) { + if (layer == Layer.LAYOUT) return layoutTransformer; + if (layer == Layer.VIEW) return viewTransformer; + return null; + } + + public Point2D inverseTransform(Layer layer, Point2D p) { + if (layer == Layer.LAYOUT) return inverseLayoutTransform(p); + if (layer == Layer.VIEW) return inverseViewTransform(p); + return null; + } + + public void setTransformer(Layer layer, MutableTransformer Function) { + if (layer == Layer.LAYOUT) setLayoutTransformer(Function); + if (layer == Layer.VIEW) setViewTransformer(Function); + } + + public Point2D transform(Layer layer, Point2D p) { + if (layer == Layer.LAYOUT) return layoutTransform(p); + if (layer == Layer.VIEW) return viewTransform(p); + return null; + } + + public Shape transform(Layer layer, Shape shape) { + if (layer == Layer.LAYOUT) return layoutTransform(shape); + if (layer == Layer.VIEW) return viewTransform(shape); + return null; + } + + public Shape inverseTransform(Layer layer, Shape shape) { + if (layer == Layer.LAYOUT) return inverseLayoutTransform(shape); + if (layer == Layer.VIEW) return inverseViewTransform(shape); + return null; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/BasicVisualizationServer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/BasicVisualizationServer.java index 990fafe5..efd8069e 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/BasicVisualizationServer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/BasicVisualizationServer.java @@ -1,5 +1,5 @@ /* -\* Copyright (c) 2003, The JUNG Authors +\* Copyright (c) 2003, The JUNG Authors * * All rights reserved. * @@ -9,6 +9,21 @@ */ package edu.uci.ics.jung.visualization; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; +import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; +import edu.uci.ics.jung.visualization.picking.MultiPickedState; +import edu.uci.ics.jung.visualization.picking.PickedState; +import edu.uci.ics.jung.visualization.picking.ShapePickSupport; +import edu.uci.ics.jung.visualization.renderers.BasicRenderer; +import edu.uci.ics.jung.visualization.renderers.Renderer; +import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; +import edu.uci.ics.jung.visualization.util.Caching; +import edu.uci.ics.jung.visualization.util.ChangeEventSupport; +import edu.uci.ics.jung.visualization.util.DefaultChangeEventSupport; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; @@ -26,486 +41,443 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - import javax.swing.JPanel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import com.google.common.graph.Network; - -import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.control.ScalingControl; -import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; -import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; -import edu.uci.ics.jung.visualization.picking.MultiPickedState; -import edu.uci.ics.jung.visualization.picking.PickedState; -import edu.uci.ics.jung.visualization.picking.ShapePickSupport; -import edu.uci.ics.jung.visualization.renderers.BasicRenderer; -import edu.uci.ics.jung.visualization.renderers.Renderer; -import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; -import edu.uci.ics.jung.visualization.util.Caching; -import edu.uci.ics.jung.visualization.util.ChangeEventSupport; -import edu.uci.ics.jung.visualization.util.DefaultChangeEventSupport; - /** - * A class that maintains many of the details necessary for creating - * visualizations of graphs. - * This is the old VisualizationViewer without tooltips and mouse behaviors. Its purpose is - * to be a base class that can also be used on the server side of a multi-tiered application. - * + * A class that maintains many of the details necessary for creating visualizations of graphs. This + * is the old VisualizationViewer without tooltips and mouse behaviors. Its purpose is to be a base + * class that can also be used on the server side of a multi-tiered application. + * * @author Joshua O'Madadhain * @author Tom Nelson * @author Danyel Fisher */ @SuppressWarnings("serial") -public class BasicVisualizationServer extends JPanel - implements ChangeListener, ChangeEventSupport, VisualizationServer{ - - protected ChangeEventSupport changeSupport = - new DefaultChangeEventSupport(this); - - /** - * holds the state of this View - */ - protected VisualizationModel model; - - /** - * handles the actual drawing of graph elements - */ - protected Renderer renderer; - - /** - * rendering hints used in drawing. Anti-aliasing is on - * by default - */ - protected Map renderingHints = new HashMap(); - - /** - * holds the state of which vertices of the graph are - * currently 'picked' - */ - protected PickedState pickedVertexState; - - /** - * holds the state of which edges of the graph are - * currently 'picked' - */ - protected PickedState pickedEdgeState; - - /** - * a listener used to cause pick events to result in - * repaints, even if they come from another view - */ - protected ItemListener pickEventListener; - - /** - * an offscreen image to render the graph - * Used if doubleBuffered is set to true - */ - protected BufferedImage offscreen; - - /** - * graphics context for the offscreen image - * Used if doubleBuffered is set to true - */ - protected Graphics2D offscreenG2d; - - /** - * user-settable choice to use the offscreen image - * or not. 'false' by default - */ - protected boolean doubleBuffered; - - /** - * a collection of user-implementable functions to render under - * the topology (before the graph is rendered) - */ - protected List preRenderers = new ArrayList(); - - /** - * a collection of user-implementable functions to render over the - * topology (after the graph is rendered) - */ - protected List postRenderers = new ArrayList(); - - protected RenderContext renderContext; - - /** - * Create an instance with the specified Layout. - * - * @param layout The Layout to apply, with its associated Graph - */ - public BasicVisualizationServer(Network network, Layout layout) { - this(new DefaultVisualizationModel(network, layout)); - } - - /** - * Create an instance with the specified Layout and view dimension. - * - * @param layout The Layout to apply, with its associated Graph - * @param preferredSize the preferred size of this View - */ - public BasicVisualizationServer(Network network, Layout layout, Dimension preferredSize) { - this(new DefaultVisualizationModel(network, layout, preferredSize), preferredSize); - } - - /** - * Create an instance with the specified model and a default dimension (600x600). - * - * @param model the model to use - */ - public BasicVisualizationServer(VisualizationModel model) { - this(model, new Dimension(600,600)); - } - - /** - * Create an instance with the specified model and view dimension. - * - * @param model the model to use - * @param preferredSize initial preferred size of the view - */ - public BasicVisualizationServer(VisualizationModel model, - Dimension preferredSize) { - this.model = model; - renderContext = new PluggableRenderContext(model.getNetwork()); - renderer = new BasicRenderer(model.getGraphLayout(), renderContext); - model.addChangeListener(this); - setDoubleBuffered(false); - this.addComponentListener(new VisualizationListener(this)); - - setPickSupport(new ShapePickSupport(this)); - setPickedVertexState(new MultiPickedState()); - setPickedEdgeState(new MultiPickedState()); - - renderContext.setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer(getPickedEdgeState(), Color.black, Color.cyan)); - renderContext.setVertexFillPaintTransformer(new PickableVertexPaintTransformer(getPickedVertexState(), - Color.red, Color.yellow)); - - setPreferredSize(preferredSize); - renderingHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - renderContext.getMultiLayerTransformer().addChangeListener(this); - } - - @Override - public void setDoubleBuffered(boolean doubleBuffered) { - this.doubleBuffered = doubleBuffered; - } - - @Override - public boolean isDoubleBuffered() { - return doubleBuffered; - } - - /** - * Always sanity-check getSize so that we don't use a - * value that is improbable - * @see java.awt.Component#getSize() - */ - @Override - public Dimension getSize() { - Dimension d = super.getSize(); - if(d.width <= 0 || d.height <= 0) { - d = getPreferredSize(); - } - return d; - } - - /** - * Ensure that, if doubleBuffering is enabled, the offscreen - * image buffer exists and is the correct size. - * @param d the expected Dimension of the offscreen buffer - */ - protected void checkOffscreenImage(Dimension d) { - if(doubleBuffered) { - if(offscreen == null || offscreen.getWidth() != d.width || offscreen.getHeight() != d.height) { - offscreen = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB); - offscreenG2d = offscreen.createGraphics(); - } - } - } - - public VisualizationModel getModel() { - return model; +public class BasicVisualizationServer extends JPanel + implements ChangeListener, ChangeEventSupport, VisualizationServer { + + protected ChangeEventSupport changeSupport = new DefaultChangeEventSupport(this); + + /** holds the state of this View */ + protected VisualizationModel model; + + /** handles the actual drawing of graph elements */ + protected Renderer renderer; + + /** rendering hints used in drawing. Anti-aliasing is on by default */ + protected Map renderingHints = new HashMap(); + + /** holds the state of which vertices of the graph are currently 'picked' */ + protected PickedState pickedVertexState; + + /** holds the state of which edges of the graph are currently 'picked' */ + protected PickedState pickedEdgeState; + + /** + * a listener used to cause pick events to result in repaints, even if they come from another view + */ + protected ItemListener pickEventListener; + + /** an offscreen image to render the graph Used if doubleBuffered is set to true */ + protected BufferedImage offscreen; + + /** graphics context for the offscreen image Used if doubleBuffered is set to true */ + protected Graphics2D offscreenG2d; + + /** user-settable choice to use the offscreen image or not. 'false' by default */ + protected boolean doubleBuffered; + + /** + * a collection of user-implementable functions to render under the topology (before the graph is + * rendered) + */ + protected List preRenderers = new ArrayList(); + + /** + * a collection of user-implementable functions to render over the topology (after the graph is + * rendered) + */ + protected List postRenderers = new ArrayList(); + + protected RenderContext renderContext; + + /** + * Create an instance with the specified Layout. + * + * @param layout The Layout to apply, with its associated Graph + */ + public BasicVisualizationServer(Network network, Layout layout) { + this(new DefaultVisualizationModel(network, layout)); + } + + /** + * Create an instance with the specified Layout and view dimension. + * + * @param layout The Layout to apply, with its associated Graph + * @param preferredSize the preferred size of this View + */ + public BasicVisualizationServer( + Network network, Layout layout, Dimension preferredSize) { + this(new DefaultVisualizationModel(network, layout, preferredSize), preferredSize); + } + + /** + * Create an instance with the specified model and a default dimension (600x600). + * + * @param model the model to use + */ + public BasicVisualizationServer(VisualizationModel model) { + this(model, new Dimension(600, 600)); + } + + /** + * Create an instance with the specified model and view dimension. + * + * @param model the model to use + * @param preferredSize initial preferred size of the view + */ + public BasicVisualizationServer(VisualizationModel model, Dimension preferredSize) { + this.model = model; + renderContext = new PluggableRenderContext(model.getNetwork()); + renderer = new BasicRenderer(model.getGraphLayout(), renderContext); + model.addChangeListener(this); + setDoubleBuffered(false); + this.addComponentListener(new VisualizationListener(this)); + + setPickSupport(new ShapePickSupport(this)); + setPickedVertexState(new MultiPickedState()); + setPickedEdgeState(new MultiPickedState()); + + renderContext.setEdgeDrawPaintTransformer( + new PickableEdgePaintTransformer(getPickedEdgeState(), Color.black, Color.cyan)); + renderContext.setVertexFillPaintTransformer( + new PickableVertexPaintTransformer(getPickedVertexState(), Color.red, Color.yellow)); + + setPreferredSize(preferredSize); + renderingHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + renderContext.getMultiLayerTransformer().addChangeListener(this); + } + + @Override + public void setDoubleBuffered(boolean doubleBuffered) { + this.doubleBuffered = doubleBuffered; + } + + @Override + public boolean isDoubleBuffered() { + return doubleBuffered; + } + + /** + * Always sanity-check getSize so that we don't use a value that is improbable + * + * @see java.awt.Component#getSize() + */ + @Override + public Dimension getSize() { + Dimension d = super.getSize(); + if (d.width <= 0 || d.height <= 0) { + d = getPreferredSize(); } - - public void setModel(VisualizationModel model) { - this.model = model; + return d; + } + + /** + * Ensure that, if doubleBuffering is enabled, the offscreen image buffer exists and is the + * correct size. + * + * @param d the expected Dimension of the offscreen buffer + */ + protected void checkOffscreenImage(Dimension d) { + if (doubleBuffered) { + if (offscreen == null + || offscreen.getWidth() != d.width + || offscreen.getHeight() != d.height) { + offscreen = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB); + offscreenG2d = offscreen.createGraphics(); + } + } + } + + public VisualizationModel getModel() { + return model; + } + + public void setModel(VisualizationModel model) { + this.model = model; + } + + public void stateChanged(ChangeEvent e) { + repaint(); + fireStateChanged(); + } + + public void setRenderer(Renderer r) { + this.renderer = r; + repaint(); + } + + public Renderer getRenderer() { + return renderer; + } + + public void setGraphLayout(Layout layout) { + Dimension viewSize = getPreferredSize(); + if (this.isShowing()) { + viewSize = getSize(); } + model.setGraphLayout(layout, viewSize); + } - public void stateChanged(ChangeEvent e) { - repaint(); - fireStateChanged(); - } - - public void setRenderer(Renderer r) { - this.renderer = r; - repaint(); - } - - public Renderer getRenderer() { - return renderer; - } - - public void setGraphLayout(Layout layout) { - Dimension viewSize = getPreferredSize(); - if(this.isShowing()) { - viewSize = getSize(); - } - model.setGraphLayout(layout, viewSize); + public void scaleToLayout(ScalingControl scaler) { + Dimension vd = getPreferredSize(); + if (this.isShowing()) { + vd = getSize(); } - - public void scaleToLayout(ScalingControl scaler) { - Dimension vd = getPreferredSize(); - if(this.isShowing()) { - vd = getSize(); - } - Dimension ld = getGraphLayout().getSize(); - if(vd.equals(ld) == false) { - scaler.scale(this, (float)(vd.getWidth()/ld.getWidth()), new Point2D.Double()); - } + Dimension ld = getGraphLayout().getSize(); + if (vd.equals(ld) == false) { + scaler.scale(this, (float) (vd.getWidth() / ld.getWidth()), new Point2D.Double()); } - - public Layout getGraphLayout() { - return model.getGraphLayout(); - } - - @Override - public void setVisible(boolean aFlag) { - super.setVisible(aFlag); - if(aFlag == true) { - Dimension d = this.getSize(); - if(d.width <= 0 || d.height <= 0) { - d = this.getPreferredSize(); - } - model.getGraphLayout().setSize(d); - } - } - - public Map getRenderingHints() { - return renderingHints; + } + + public Layout getGraphLayout() { + return model.getGraphLayout(); + } + + @Override + public void setVisible(boolean aFlag) { + super.setVisible(aFlag); + if (aFlag == true) { + Dimension d = this.getSize(); + if (d.width <= 0 || d.height <= 0) { + d = this.getPreferredSize(); + } + model.getGraphLayout().setSize(d); } - - public void setRenderingHints(Map renderingHints) { - this.renderingHints = renderingHints; + } + + public Map getRenderingHints() { + return renderingHints; + } + + public void setRenderingHints(Map renderingHints) { + this.renderingHints = renderingHints; + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + Graphics2D g2d = (Graphics2D) g; + if (doubleBuffered) { + checkOffscreenImage(getSize()); + renderGraph(offscreenG2d); + g2d.drawImage(offscreen, null, 0, 0); + } else { + renderGraph(g2d); } - - @Override - protected void paintComponent(Graphics g) { - super.paintComponent(g); - - Graphics2D g2d = (Graphics2D)g; - if(doubleBuffered) { - checkOffscreenImage(getSize()); - renderGraph(offscreenG2d); - g2d.drawImage(offscreen, null, 0, 0); - } else { - renderGraph(g2d); - } - } - - protected void renderGraph(Graphics2D g2d) { - if(renderContext.getGraphicsContext() == null) { - renderContext.setGraphicsContext(new GraphicsDecorator(g2d)); - } else { - renderContext.getGraphicsContext().setDelegate(g2d); - } - renderContext.setScreenDevice(this); - Layout layout = model.getGraphLayout(); - - g2d.setRenderingHints(renderingHints); - - // the size of the VisualizationViewer - Dimension d = getSize(); - - // clear the offscreen image - g2d.setColor(getBackground()); - g2d.fillRect(0,0,d.width,d.height); - - AffineTransform oldXform = g2d.getTransform(); - AffineTransform newXform = new AffineTransform(oldXform); - newXform.concatenate( - renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW).getTransform()); - - g2d.setTransform(newXform); + } - // if there are preRenderers set, paint them - for(Paintable paintable : preRenderers) { - - if(paintable.useTransform()) { - paintable.paint(g2d); - } else { - g2d.setTransform(oldXform); - paintable.paint(g2d); - g2d.setTransform(newXform); - } - } - - if(layout instanceof Caching) { - ((Caching)layout).clear(); - } - - renderer.render(); - - // if there are postRenderers set, do it - for(Paintable paintable : postRenderers) { - - if(paintable.useTransform()) { - paintable.paint(g2d); - } else { - g2d.setTransform(oldXform); - paintable.paint(g2d); - g2d.setTransform(newXform); - } - } - g2d.setTransform(oldXform); - } - - /** - * VisualizationListener reacts to changes in the size of the - * VisualizationViewer. When the size changes, it ensures - * that the offscreen image is sized properly. - * If the layout is locked to this view size, then the layout - * is also resized to be the same as the view size. - * - * - */ - protected class VisualizationListener extends ComponentAdapter { - protected BasicVisualizationServer vv; - public VisualizationListener(BasicVisualizationServer vv) { - this.vv = vv; - } - - /** - * create a new offscreen image for the graph - * whenever the window is resied - */ - @Override - public void componentResized(ComponentEvent e) { - Dimension d = vv.getSize(); - if(d.width <= 0 || d.height <= 0) return; - checkOffscreenImage(d); - repaint(); - } - } - - public void addPreRenderPaintable(Paintable paintable) { - if(preRenderers == null) { - preRenderers = new ArrayList(); - } - preRenderers.add(paintable); + protected void renderGraph(Graphics2D g2d) { + if (renderContext.getGraphicsContext() == null) { + renderContext.setGraphicsContext(new GraphicsDecorator(g2d)); + } else { + renderContext.getGraphicsContext().setDelegate(g2d); } - - public void prependPreRenderPaintable(Paintable paintable) { - if(preRenderers == null) { - preRenderers = new ArrayList(); - } - preRenderers.add(0,paintable); + renderContext.setScreenDevice(this); + Layout layout = model.getGraphLayout(); + + g2d.setRenderingHints(renderingHints); + + // the size of the VisualizationViewer + Dimension d = getSize(); + + // clear the offscreen image + g2d.setColor(getBackground()); + g2d.fillRect(0, 0, d.width, d.height); + + AffineTransform oldXform = g2d.getTransform(); + AffineTransform newXform = new AffineTransform(oldXform); + newXform.concatenate( + renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW).getTransform()); + + g2d.setTransform(newXform); + + // if there are preRenderers set, paint them + for (Paintable paintable : preRenderers) { + + if (paintable.useTransform()) { + paintable.paint(g2d); + } else { + g2d.setTransform(oldXform); + paintable.paint(g2d); + g2d.setTransform(newXform); + } } - - public void removePreRenderPaintable(Paintable paintable) { - if(preRenderers != null) { - preRenderers.remove(paintable); - } + + if (layout instanceof Caching) { + ((Caching) layout).clear(); } - - public void addPostRenderPaintable(Paintable paintable) { - if(postRenderers == null) { - postRenderers = new ArrayList(); - } - postRenderers.add(paintable); + + renderer.render(); + + // if there are postRenderers set, do it + for (Paintable paintable : postRenderers) { + + if (paintable.useTransform()) { + paintable.paint(g2d); + } else { + g2d.setTransform(oldXform); + paintable.paint(g2d); + g2d.setTransform(newXform); + } } - - public void prependPostRenderPaintable(Paintable paintable) { - if(postRenderers == null) { - postRenderers = new ArrayList(); - } - postRenderers.add(0,paintable); + g2d.setTransform(oldXform); + } + + /** + * VisualizationListener reacts to changes in the size of the VisualizationViewer. When the size + * changes, it ensures that the offscreen image is sized properly. If the layout is locked to this + * view size, then the layout is also resized to be the same as the view size. + */ + protected class VisualizationListener extends ComponentAdapter { + protected BasicVisualizationServer vv; + + public VisualizationListener(BasicVisualizationServer vv) { + this.vv = vv; } - - public void removePostRenderPaintable(Paintable paintable) { - if(postRenderers != null) { - postRenderers.remove(paintable); - } + + /** create a new offscreen image for the graph whenever the window is resied */ + @Override + public void componentResized(ComponentEvent e) { + Dimension d = vv.getSize(); + if (d.width <= 0 || d.height <= 0) return; + checkOffscreenImage(d); + repaint(); } + } - public void addChangeListener(ChangeListener l) { - changeSupport.addChangeListener(l); + public void addPreRenderPaintable(Paintable paintable) { + if (preRenderers == null) { + preRenderers = new ArrayList(); } - - public void removeChangeListener(ChangeListener l) { - changeSupport.removeChangeListener(l); + preRenderers.add(paintable); + } + + public void prependPreRenderPaintable(Paintable paintable) { + if (preRenderers == null) { + preRenderers = new ArrayList(); } - - public ChangeListener[] getChangeListeners() { - return changeSupport.getChangeListeners(); + preRenderers.add(0, paintable); + } + + public void removePreRenderPaintable(Paintable paintable) { + if (preRenderers != null) { + preRenderers.remove(paintable); } + } - public void fireStateChanged() { - changeSupport.fireStateChanged(); - } - - public PickedState getPickedVertexState() { - return pickedVertexState; + public void addPostRenderPaintable(Paintable paintable) { + if (postRenderers == null) { + postRenderers = new ArrayList(); } + postRenderers.add(paintable); + } - public PickedState getPickedEdgeState() { - return pickedEdgeState; + public void prependPostRenderPaintable(Paintable paintable) { + if (postRenderers == null) { + postRenderers = new ArrayList(); } - - public void setPickedVertexState(PickedState pickedVertexState) { - if(pickEventListener != null && this.pickedVertexState != null) { - this.pickedVertexState.removeItemListener(pickEventListener); - } - this.pickedVertexState = pickedVertexState; - this.renderContext.setPickedVertexState(pickedVertexState); - if(pickEventListener == null) { - pickEventListener = new ItemListener() { - - public void itemStateChanged(ItemEvent e) { - repaint(); - } - }; - } - pickedVertexState.addItemListener(pickEventListener); + postRenderers.add(0, paintable); + } + + public void removePostRenderPaintable(Paintable paintable) { + if (postRenderers != null) { + postRenderers.remove(paintable); } - - public void setPickedEdgeState(PickedState pickedEdgeState) { - if(pickEventListener != null && this.pickedEdgeState != null) { - this.pickedEdgeState.removeItemListener(pickEventListener); - } - this.pickedEdgeState = pickedEdgeState; - this.renderContext.setPickedEdgeState(pickedEdgeState); - if(pickEventListener == null) { - pickEventListener = new ItemListener() { - - public void itemStateChanged(ItemEvent e) { - repaint(); - } - }; - } - pickedEdgeState.addItemListener(pickEventListener); + } + + public void addChangeListener(ChangeListener l) { + changeSupport.addChangeListener(l); + } + + public void removeChangeListener(ChangeListener l) { + changeSupport.removeChangeListener(l); + } + + public ChangeListener[] getChangeListeners() { + return changeSupport.getChangeListeners(); + } + + public void fireStateChanged() { + changeSupport.fireStateChanged(); + } + + public PickedState getPickedVertexState() { + return pickedVertexState; + } + + public PickedState getPickedEdgeState() { + return pickedEdgeState; + } + + public void setPickedVertexState(PickedState pickedVertexState) { + if (pickEventListener != null && this.pickedVertexState != null) { + this.pickedVertexState.removeItemListener(pickEventListener); } - - public NetworkElementAccessor getPickSupport() { - return renderContext.getPickSupport(); + this.pickedVertexState = pickedVertexState; + this.renderContext.setPickedVertexState(pickedVertexState); + if (pickEventListener == null) { + pickEventListener = + new ItemListener() { + + public void itemStateChanged(ItemEvent e) { + repaint(); + } + }; } + pickedVertexState.addItemListener(pickEventListener); + } - public void setPickSupport(NetworkElementAccessor pickSupport) { - renderContext.setPickSupport(pickSupport); + public void setPickedEdgeState(PickedState pickedEdgeState) { + if (pickEventListener != null && this.pickedEdgeState != null) { + this.pickedEdgeState.removeItemListener(pickEventListener); } - - public Point2D getCenter() { - Dimension d = getSize(); - return new Point2D.Float(d.width/2, d.height/2); + this.pickedEdgeState = pickedEdgeState; + this.renderContext.setPickedEdgeState(pickedEdgeState); + if (pickEventListener == null) { + pickEventListener = + new ItemListener() { + + public void itemStateChanged(ItemEvent e) { + repaint(); + } + }; } + pickedEdgeState.addItemListener(pickEventListener); + } - public RenderContext getRenderContext() { - return renderContext; - } + public NetworkElementAccessor getPickSupport() { + return renderContext.getPickSupport(); + } - public void setRenderContext(RenderContext renderContext) { - this.renderContext = renderContext; - } + public void setPickSupport(NetworkElementAccessor pickSupport) { + renderContext.setPickSupport(pickSupport); + } + + public Point2D getCenter() { + Dimension d = getSize(); + return new Point2D.Float(d.width / 2, d.height / 2); + } + + public RenderContext getRenderContext() { + return renderContext; + } + + public void setRenderContext(RenderContext renderContext) { + this.renderContext = renderContext; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/DefaultVisualizationModel.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/DefaultVisualizationModel.java index fe64ed03..a89975aa 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/DefaultVisualizationModel.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/DefaultVisualizationModel.java @@ -1,22 +1,15 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.visualization; -import java.awt.Dimension; - -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.EventListenerList; - import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.algorithms.layout.util.Relaxer; import edu.uci.ics.jung.algorithms.layout.util.VisRunner; @@ -24,178 +17,168 @@ import edu.uci.ics.jung.visualization.layout.ObservableCachingLayout; import edu.uci.ics.jung.visualization.util.ChangeEventSupport; import edu.uci.ics.jung.visualization.util.DefaultChangeEventSupport; +import java.awt.Dimension; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.EventListenerList; /** - * The model containing state values for - * visualizations of graphs. - * Refactored and extracted from the 1.6.0 version of VisualizationViewer - * + * The model containing state values for visualizations of graphs. Refactored and extracted from the + * 1.6.0 version of VisualizationViewer + * * @author Tom Nelson */ -public class DefaultVisualizationModel implements VisualizationModel, ChangeEventSupport { - - ChangeEventSupport changeSupport = new DefaultChangeEventSupport(this); - - /** - * manages the thread that applies the current layout algorithm - */ - protected Relaxer relaxer; - - /** - * the layout algorithm currently in use - */ - protected Layout layout; - - /** - * listens for changes in the layout, forwards to the viewer - * - */ - protected ChangeListener changeListener; - - private final Network network; - - /** - * - * @param layout The Layout to apply, with its associated Network - */ - public DefaultVisualizationModel(Network network, Layout layout) { - this(network, layout, null); - } - - /** - * Create an instance with the specified layout and dimension. - * @param layout the layout to use - * @param d The preferred size of the View that will display this graph - */ - public DefaultVisualizationModel(Network network, Layout layout, Dimension d) { - this.network = network; - if(changeListener == null) { - changeListener = new ChangeListener() { - public void stateChanged(ChangeEvent e) { - fireStateChanged(); - } - }; - } - setGraphLayout(layout, d); - } - - /** - * Removes the current graph layout, and adds a new one. - * @param layout the new layout to use - * @param viewSize the size of the View that will display this layout - */ - public void setGraphLayout(Layout layout, Dimension viewSize) { - // remove listener from old layout - if(this.layout != null && this.layout instanceof ChangeEventSupport) { - ((ChangeEventSupport)this.layout).removeChangeListener(changeListener); - } - // set to new layout - if(layout instanceof ChangeEventSupport) { - this.layout = layout; - } else { - this.layout = new ObservableCachingLayout(network.asGraph(), layout); - } - - ((ChangeEventSupport)this.layout).addChangeListener(changeListener); - - if(viewSize == null) { - viewSize = new Dimension(600,600); - } - Dimension layoutSize = layout.getSize(); - // if the layout has NOT been initialized yet, initialize its size - // now to the size of the VisualizationViewer window - if(layoutSize == null) { - layout.setSize(viewSize); - } - if(relaxer != null) { - relaxer.stop(); - relaxer = null; - } -// Layout decoratedLayout = (layout instanceof LayoutDecorator) -// ? ((LayoutDecorator) layout).getDelegate() -// : layout; - if (layout instanceof IterativeContext) { - layout.initialize(); - if(relaxer == null) { - relaxer = new VisRunner((IterativeContext)this.layout); -// relaxer = new VisRunner((IterativeContext) decoratedLayout); - relaxer.prerelax(); - relaxer.relax(); +public class DefaultVisualizationModel + implements VisualizationModel, ChangeEventSupport { + + ChangeEventSupport changeSupport = new DefaultChangeEventSupport(this); + + /** manages the thread that applies the current layout algorithm */ + protected Relaxer relaxer; + + /** the layout algorithm currently in use */ + protected Layout layout; + + /** listens for changes in the layout, forwards to the viewer */ + protected ChangeListener changeListener; + + private final Network network; + + /** @param layout The Layout to apply, with its associated Network */ + public DefaultVisualizationModel(Network network, Layout layout) { + this(network, layout, null); + } + + /** + * Create an instance with the specified layout and dimension. + * + * @param layout the layout to use + * @param d The preferred size of the View that will display this graph + */ + public DefaultVisualizationModel(Network network, Layout layout, Dimension d) { + this.network = network; + if (changeListener == null) { + changeListener = + new ChangeListener() { + public void stateChanged(ChangeEvent e) { + fireStateChanged(); } - } - fireStateChanged(); - } - - /** - * set the graph Layout and if it is not already initialized, initialize - * it to the default VisualizationViewer preferred size of 600x600 - */ - public void setGraphLayout(Layout layout) { - setGraphLayout(layout, null); - } - - /** - * Returns the current graph layout. - */ - public Layout getGraphLayout() { - return layout; - } - - public Network getNetwork() { - return network; - } - - /** - * @return the relaxer - */ - public Relaxer getRelaxer() { - return relaxer; - } - - /** - * @param relaxer the relaxer to set - */ - public void setRelaxer(VisRunner relaxer) { - this.relaxer = relaxer; - } - - /** - * Adds a ChangeListener. - * @param l the listener to be added - */ - public void addChangeListener(ChangeListener l) { - changeSupport.addChangeListener(l); + }; + } + setGraphLayout(layout, d); + } + + /** + * Removes the current graph layout, and adds a new one. + * + * @param layout the new layout to use + * @param viewSize the size of the View that will display this layout + */ + public void setGraphLayout(Layout layout, Dimension viewSize) { + // remove listener from old layout + if (this.layout != null && this.layout instanceof ChangeEventSupport) { + ((ChangeEventSupport) this.layout).removeChangeListener(changeListener); + } + // set to new layout + if (layout instanceof ChangeEventSupport) { + this.layout = layout; + } else { + this.layout = new ObservableCachingLayout(network.asGraph(), layout); + } + + ((ChangeEventSupport) this.layout).addChangeListener(changeListener); + + if (viewSize == null) { + viewSize = new Dimension(600, 600); + } + Dimension layoutSize = layout.getSize(); + // if the layout has NOT been initialized yet, initialize its size + // now to the size of the VisualizationViewer window + if (layoutSize == null) { + layout.setSize(viewSize); } - - /** - * Removes a ChangeListener. - * @param l the listener to be removed - */ - public void removeChangeListener(ChangeListener l) { - changeSupport.removeChangeListener(l); + if (relaxer != null) { + relaxer.stop(); + relaxer = null; } - - /** - * Returns an array of all the ChangeListeners added - * with addChangeListener(). - * - * @return all of the ChangeListeners added or an empty - * array if no listeners have been added - */ - public ChangeListener[] getChangeListeners() { - return changeSupport.getChangeListeners(); + // Layout decoratedLayout = (layout instanceof LayoutDecorator) + // ? ((LayoutDecorator) layout).getDelegate() + // : layout; + if (layout instanceof IterativeContext) { + layout.initialize(); + if (relaxer == null) { + relaxer = new VisRunner((IterativeContext) this.layout); + // relaxer = new VisRunner((IterativeContext) decoratedLayout); + relaxer.prerelax(); + relaxer.relax(); + } } + fireStateChanged(); + } + + /** + * set the graph Layout and if it is not already initialized, initialize it to the default + * VisualizationViewer preferred size of 600x600 + */ + public void setGraphLayout(Layout layout) { + setGraphLayout(layout, null); + } + + /** Returns the current graph layout. */ + public Layout getGraphLayout() { + return layout; + } + + public Network getNetwork() { + return network; + } + + /** @return the relaxer */ + public Relaxer getRelaxer() { + return relaxer; + } + + /** @param relaxer the relaxer to set */ + public void setRelaxer(VisRunner relaxer) { + this.relaxer = relaxer; + } + + /** + * Adds a ChangeListener. + * + * @param l the listener to be added + */ + public void addChangeListener(ChangeListener l) { + changeSupport.addChangeListener(l); + } + + /** + * Removes a ChangeListener. + * + * @param l the listener to be removed + */ + public void removeChangeListener(ChangeListener l) { + changeSupport.removeChangeListener(l); + } + + /** + * Returns an array of all the ChangeListeners added with addChangeListener(). + * + * @return all of the ChangeListeners added or an empty array if no listeners have + * been added + */ + public ChangeListener[] getChangeListeners() { + return changeSupport.getChangeListeners(); + } - /** - * Notifies all listeners that have registered interest for - * notification on this event type. The event instance - * is lazily created. - * The primary listeners will be views that need to be repainted - * because of changes in this model instance - * @see EventListenerList - */ - public void fireStateChanged() { - changeSupport.fireStateChanged(); - } - + /** + * Notifies all listeners that have registered interest for notification on this event type. The + * event instance is lazily created. The primary listeners will be views that need to be repainted + * because of changes in this model instance + * + * @see EventListenerList + */ + public void fireStateChanged() { + changeSupport.fireStateChanged(); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/FourPassImageShaper.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/FourPassImageShaper.java index 5b5f1c71..eb314bb5 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/FourPassImageShaper.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/FourPassImageShaper.java @@ -18,234 +18,232 @@ import java.awt.image.BufferedImage; /** - * Provides Supplier methods that, given a BufferedImage, an Image, - * or the fileName of an image, will return a java.awt.Shape that - * is the contiguous traced outline of the opaque part of the image. - * This could be used to define an image for use in a Vertex, where - * the shape used for picking and edge-arrow placement follows the - * opaque part of an image that has a transparent background. - * The methods try to detect lines in order to minimize points - * in the path - * - * @author Tom Nelson + * Provides Supplier methods that, given a BufferedImage, an Image, or the fileName of an image, + * will return a java.awt.Shape that is the contiguous traced outline of the opaque part of the + * image. This could be used to define an image for use in a Vertex, where the shape used for + * picking and edge-arrow placement follows the opaque part of an image that has a transparent + * background. The methods try to detect lines in order to minimize points in the path * - * + * @author Tom Nelson */ public class FourPassImageShaper { - - public static Shape getShape(BufferedImage image) { - Area area = new Area(leftEdge(image)); - area.intersect(new Area(bottomEdge(image))); - area.intersect(new Area(rightEdge(image))); - area.intersect(new Area(topEdge(image))); - return area; - } - /** - * Checks to see if point p is on a line that passes thru - * points p1 and p2. If p is on the line, extend the line - * segment so that it is from p1 to the location of p. - * If the point p is not on the line, update my shape - * with a line extending to the old p2 location, make - * the old p2 the new p1, and make p2 the old p - * @param p1 - * @param p2 - * @param p - * @param line - * @param path - * @return - */ - private static Point2D detectLine(Point2D p1, Point2D p2, Point2D p, - Line2D line, GeneralPath path) { - - // check for line - // if p is on the line that extends thru p1 and p2 - if(line.ptLineDistSq(p) == 0) { // p is on the line p1,p2 - // extend line so that p2 is at p - p2.setLocation(p); - } else { // its not on the current line - // start a new line from p2 to p - p1.setLocation(p2); - p2.setLocation(p); - line.setLine(p1,p2); - // end the ongoing path line at the new p1 (the old p2) - path.lineTo((float)p1.getX(), (float)p1.getY()); - } - return p2; + + public static Shape getShape(BufferedImage image) { + Area area = new Area(leftEdge(image)); + area.intersect(new Area(bottomEdge(image))); + area.intersect(new Area(rightEdge(image))); + area.intersect(new Area(topEdge(image))); + return area; + } + /** + * Checks to see if point p is on a line that passes thru points p1 and p2. If p is on the line, + * extend the line segment so that it is from p1 to the location of p. If the point p is not on + * the line, update my shape with a line extending to the old p2 location, make the old p2 the new + * p1, and make p2 the old p + * + * @param p1 + * @param p2 + * @param p + * @param line + * @param path + * @return + */ + private static Point2D detectLine( + Point2D p1, Point2D p2, Point2D p, Line2D line, GeneralPath path) { + + // check for line + // if p is on the line that extends thru p1 and p2 + if (line.ptLineDistSq(p) == 0) { // p is on the line p1,p2 + // extend line so that p2 is at p + p2.setLocation(p); + } else { // its not on the current line + // start a new line from p2 to p + p1.setLocation(p2); + p2.setLocation(p); + line.setLine(p1, p2); + // end the ongoing path line at the new p1 (the old p2) + path.lineTo((float) p1.getX(), (float) p1.getY()); } - /** - * trace the left side of the image - * @param image - * @param path - * @return - */ - private static Shape leftEdge(BufferedImage image) { - GeneralPath path = new GeneralPath(); - Point2D p1 = null; - Point2D p2 = null; - Line2D line = new Line2D.Float(); - Point2D p = new Point2D.Float(); - int foundPointY = -1; - for(int i=0; i= 0) { - if(p2 == null) { - // this is the first point found. project line to right edge - p1 = new Point2D.Float(image.getWidth()-1, foundPointY); - path.moveTo(p1.getX(), p1.getY()); - p2 = new Point2D.Float(); - p2.setLocation(p); - } else { - p2 = detectLine(p1, p2, p, line, path); - } - } + return p2; + } + /** + * trace the left side of the image + * + * @param image + * @param path + * @return + */ + private static Shape leftEdge(BufferedImage image) { + GeneralPath path = new GeneralPath(); + Point2D p1 = null; + Point2D p2 = null; + Line2D line = new Line2D.Float(); + Point2D p = new Point2D.Float(); + int foundPointY = -1; + for (int i = 0; i < image.getHeight(); i++) { + // go until we reach an opaque point, then stop + for (int j = 0; j < image.getWidth(); j++) { + if ((image.getRGB(j, i) & 0xff000000) != 0) { + // this is a point I want + p = new Point2D.Float(j, i); + foundPointY = i; + break; } - path.lineTo(p.getX(), p.getY()); - if(foundPointY >= 0) { - path.lineTo(image.getWidth()-1, foundPointY); + } + if (foundPointY >= 0) { + if (p2 == null) { + // this is the first point found. project line to right edge + p1 = new Point2D.Float(image.getWidth() - 1, foundPointY); + path.moveTo(p1.getX(), p1.getY()); + p2 = new Point2D.Float(); + p2.setLocation(p); + } else { + p2 = detectLine(p1, p2, p, line, path); } - path.closePath(); - return path; + } } - - /** - * trace the bottom of the image - * @param image - * @param path - * @param start - * @return - */ - private static Shape bottomEdge(BufferedImage image) { - GeneralPath path = new GeneralPath(); - Point2D p1 = null; - Point2D p2 = null; - Line2D line = new Line2D.Float(); - Point2D p = new Point2D.Float(); - int foundPointX = -1; - for(int i=0; i=0; j--) { - if((image.getRGB(i,j) & 0xff000000) != 0) { - // this is a point I want - p.setLocation(i,j); - foundPointX = i; - break; - } - } - if(foundPointX >= 0) { - if(p2 == null) { - // this is the first point found. project line to top edge - p1 = new Point2D.Float(foundPointX, 0); - // path starts here - path.moveTo(p1.getX(), p1.getY()); - p2 = new Point2D.Float(); - p2.setLocation(p); - } else { - p2 = detectLine(p1, p2, p, line, path); - } - } + path.lineTo(p.getX(), p.getY()); + if (foundPointY >= 0) { + path.lineTo(image.getWidth() - 1, foundPointY); + } + path.closePath(); + return path; + } + + /** + * trace the bottom of the image + * + * @param image + * @param path + * @param start + * @return + */ + private static Shape bottomEdge(BufferedImage image) { + GeneralPath path = new GeneralPath(); + Point2D p1 = null; + Point2D p2 = null; + Line2D line = new Line2D.Float(); + Point2D p = new Point2D.Float(); + int foundPointX = -1; + for (int i = 0; i < image.getWidth(); i++) { + for (int j = image.getHeight() - 1; j >= 0; j--) { + if ((image.getRGB(i, j) & 0xff000000) != 0) { + // this is a point I want + p.setLocation(i, j); + foundPointX = i; + break; } - path.lineTo(p.getX(), p.getY()); - if(foundPointX >= 0) { - path.lineTo(foundPointX, 0); + } + if (foundPointX >= 0) { + if (p2 == null) { + // this is the first point found. project line to top edge + p1 = new Point2D.Float(foundPointX, 0); + // path starts here + path.moveTo(p1.getX(), p1.getY()); + p2 = new Point2D.Float(); + p2.setLocation(p); + } else { + p2 = detectLine(p1, p2, p, line, path); } - path.closePath(); - return path; + } } - - /** - * trace the right side of the image - * @param image - * @param path - * @param start - * @return - */ - private static Shape rightEdge(BufferedImage image) { - GeneralPath path = new GeneralPath(); - Point2D p1 = null; - Point2D p2 = null; - Line2D line = new Line2D.Float(); - Point2D p = new Point2D.Float(); - int foundPointY = -1; - - for(int i=image.getHeight()-1; i>=0; i--) { - for(int j=image.getWidth()-1; j>=0; j--) { - if((image.getRGB(j,i) & 0xff000000) != 0) { - // this is a point I want - p.setLocation(j,i); - foundPointY = i; - break; - } - } - if(foundPointY >= 0) { - if(p2 == null) { - // this is the first point found. project line to top edge - p1 = new Point2D.Float(0, foundPointY); - // path starts here - path.moveTo(p1.getX(), p1.getY()); - p2 = new Point2D.Float(); - p2.setLocation(p); - } else { - p2 = detectLine(p1, p2, p, line, path); - } - } + path.lineTo(p.getX(), p.getY()); + if (foundPointX >= 0) { + path.lineTo(foundPointX, 0); + } + path.closePath(); + return path; + } + + /** + * trace the right side of the image + * + * @param image + * @param path + * @param start + * @return + */ + private static Shape rightEdge(BufferedImage image) { + GeneralPath path = new GeneralPath(); + Point2D p1 = null; + Point2D p2 = null; + Line2D line = new Line2D.Float(); + Point2D p = new Point2D.Float(); + int foundPointY = -1; + + for (int i = image.getHeight() - 1; i >= 0; i--) { + for (int j = image.getWidth() - 1; j >= 0; j--) { + if ((image.getRGB(j, i) & 0xff000000) != 0) { + // this is a point I want + p.setLocation(j, i); + foundPointY = i; + break; } - path.lineTo(p.getX(), p.getY()); - if(foundPointY >= 0) { - path.lineTo(0, foundPointY); + } + if (foundPointY >= 0) { + if (p2 == null) { + // this is the first point found. project line to top edge + p1 = new Point2D.Float(0, foundPointY); + // path starts here + path.moveTo(p1.getX(), p1.getY()); + p2 = new Point2D.Float(); + p2.setLocation(p); + } else { + p2 = detectLine(p1, p2, p, line, path); } - path.closePath(); - return path; + } + } + path.lineTo(p.getX(), p.getY()); + if (foundPointY >= 0) { + path.lineTo(0, foundPointY); } - - /** - * trace the top of the image - * @param image - * @param path - * @param start - * @return - */ - private static Shape topEdge(BufferedImage image) { - GeneralPath path = new GeneralPath(); - Point2D p1 = null; - Point2D p2 = null; - Line2D line = new Line2D.Float(); - Point2D p = new Point2D.Float(); - int foundPointX = -1; - - for(int i=image.getWidth()-1; i>=0; i--) { - for(int j=0; j= 0) { - if(p2 == null) { - // this is the first point found. project line to top edge - p1 = new Point2D.Float(foundPointX, image.getHeight()-1); - // path starts here - path.moveTo(p1.getX(), p1.getY()); - p2 = new Point2D.Float(); - p2.setLocation(p); - } else { - p2 = detectLine(p1, p2, p, line, path); - } - } + path.closePath(); + return path; + } + + /** + * trace the top of the image + * + * @param image + * @param path + * @param start + * @return + */ + private static Shape topEdge(BufferedImage image) { + GeneralPath path = new GeneralPath(); + Point2D p1 = null; + Point2D p2 = null; + Line2D line = new Line2D.Float(); + Point2D p = new Point2D.Float(); + int foundPointX = -1; + + for (int i = image.getWidth() - 1; i >= 0; i--) { + for (int j = 0; j < image.getHeight(); j++) { + if ((image.getRGB(i, j) & 0xff000000) != 0) { + // this is a point I want + p.setLocation(i, j); + foundPointX = i; + break; } - path.lineTo(p.getX(), p.getY()); - if(foundPointX >= 0) { - path.lineTo(foundPointX, image.getHeight()-1); + } + if (foundPointX >= 0) { + if (p2 == null) { + // this is the first point found. project line to top edge + p1 = new Point2D.Float(foundPointX, image.getHeight() - 1); + // path starts here + path.moveTo(p1.getX(), p1.getY()); + p2 = new Point2D.Float(); + p2.setLocation(p); + } else { + p2 = detectLine(p1, p2, p, line, path); } - path.closePath(); - return path; + } + } + path.lineTo(p.getX(), p.getY()); + if (foundPointX >= 0) { + path.lineTo(foundPointX, image.getHeight() - 1); } + path.closePath(); + return path; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/GraphZoomScrollPane.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/GraphZoomScrollPane.java index a06b71ce..04fc0312 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/GraphZoomScrollPane.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/GraphZoomScrollPane.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. * Created on Feb 2, 2005 @@ -9,6 +9,8 @@ */ package edu.uci.ics.jung.visualization; +import edu.uci.ics.jung.visualization.transform.BidirectionalTransformer; +import edu.uci.ics.jung.visualization.transform.shape.Intersector; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Rectangle; @@ -21,7 +23,6 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Set; - import javax.swing.BoundedRangeModel; import javax.swing.JComponent; import javax.swing.JPanel; @@ -29,300 +30,300 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import edu.uci.ics.jung.visualization.transform.BidirectionalTransformer; -import edu.uci.ics.jung.visualization.transform.shape.Intersector; - - - /** - * GraphZoomScrollPane is a Container for the Graph's VisualizationViewer - * and includes custom horizontal and vertical scrollbars. - * GraphZoomScrollPane listens for changes in the scale and - * translation of the VisualizationViewer, and will update the - * scrollbar positions and sizes accordingly. Changes in the - * scrollbar positions will cause the corresponding change in - * the translation component (offset) of the VisualizationViewer. - * The scrollbars are modified so that they will allow panning - * of the graph when the scale has been changed (e.g. zoomed-in - * or zoomed-out). - * - * The lower-right corner of this component is available to - * use as a small button or menu. - * - * samples.graph.GraphZoomScrollPaneDemo shows the use of this component. - * - * @author Tom Nelson + * GraphZoomScrollPane is a Container for the Graph's VisualizationViewer and includes custom + * horizontal and vertical scrollbars. GraphZoomScrollPane listens for changes in the scale and + * translation of the VisualizationViewer, and will update the scrollbar positions and sizes + * accordingly. Changes in the scrollbar positions will cause the corresponding change in the + * translation component (offset) of the VisualizationViewer. The scrollbars are modified so that + * they will allow panning of the graph when the scale has been changed (e.g. zoomed-in or + * zoomed-out). + * + *

            The lower-right corner of this component is available to use as a small button or menu. * - * + *

            samples.graph.GraphZoomScrollPaneDemo shows the use of this component. + * + * @author Tom Nelson */ @SuppressWarnings("serial") public class GraphZoomScrollPane extends JPanel { - protected VisualizationViewer vv; - protected JScrollBar horizontalScrollBar; - protected JScrollBar verticalScrollBar; - protected JComponent corner; - protected boolean scrollBarsMayControlAdjusting = true; - protected JPanel south; - - /** - * Create an instance of the GraphZoomScrollPane to contain the - * VisualizationViewer - * @param vv the VisualizationViewer for which this instance is to be created - */ - public GraphZoomScrollPane(VisualizationViewer vv) { - super(new BorderLayout()); - this.vv = vv; - addComponentListener(new ResizeListener()); - Dimension d = vv.getGraphLayout().getSize(); - verticalScrollBar = new JScrollBar(JScrollBar.VERTICAL, 0, d.height, 0, d.height); - horizontalScrollBar = new JScrollBar(JScrollBar.HORIZONTAL, 0, d.width, 0, d.width); - verticalScrollBar.addAdjustmentListener(new VerticalAdjustmentListenerImpl()); - horizontalScrollBar.addAdjustmentListener(new HorizontalAdjustmentListenerImpl()); - verticalScrollBar.setUnitIncrement(20); - horizontalScrollBar.setUnitIncrement(20); - // respond to changes in the VisualizationViewer's transform - // and set the scroll bar parameters appropriately - vv.addChangeListener( - new ChangeListener(){ - public void stateChanged(ChangeEvent evt) { - VisualizationViewer vv = - (VisualizationViewer)evt.getSource(); - setScrollBars(vv); - } + protected VisualizationViewer vv; + protected JScrollBar horizontalScrollBar; + protected JScrollBar verticalScrollBar; + protected JComponent corner; + protected boolean scrollBarsMayControlAdjusting = true; + protected JPanel south; + + /** + * Create an instance of the GraphZoomScrollPane to contain the VisualizationViewer + * + * @param vv the VisualizationViewer for which this instance is to be created + */ + public GraphZoomScrollPane(VisualizationViewer vv) { + super(new BorderLayout()); + this.vv = vv; + addComponentListener(new ResizeListener()); + Dimension d = vv.getGraphLayout().getSize(); + verticalScrollBar = new JScrollBar(JScrollBar.VERTICAL, 0, d.height, 0, d.height); + horizontalScrollBar = new JScrollBar(JScrollBar.HORIZONTAL, 0, d.width, 0, d.width); + verticalScrollBar.addAdjustmentListener(new VerticalAdjustmentListenerImpl()); + horizontalScrollBar.addAdjustmentListener(new HorizontalAdjustmentListenerImpl()); + verticalScrollBar.setUnitIncrement(20); + horizontalScrollBar.setUnitIncrement(20); + // respond to changes in the VisualizationViewer's transform + // and set the scroll bar parameters appropriately + vv.addChangeListener( + new ChangeListener() { + public void stateChanged(ChangeEvent evt) { + VisualizationViewer vv = (VisualizationViewer) evt.getSource(); + setScrollBars(vv); + } }); - add(vv); - add(verticalScrollBar, BorderLayout.EAST); - south = new JPanel(new BorderLayout()); - south.add(horizontalScrollBar); - setCorner(new JPanel()); - add(south, BorderLayout.SOUTH); - } - - /** - * listener for adjustment of the horizontal scroll bar. - * Sets the translation of the VisualizationViewer - */ - class HorizontalAdjustmentListenerImpl implements AdjustmentListener { - int previous = 0; - public void adjustmentValueChanged(AdjustmentEvent e) { - int hval = e.getValue(); - float dh = previous - hval; - previous = hval; - if(dh != 0 && scrollBarsMayControlAdjusting) { - // get the uniform scale of all transforms - float layoutScale = (float) vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getScale(); - dh *= layoutScale; - AffineTransform at = AffineTransform.getTranslateInstance(dh, 0); - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).preConcatenate(at); - } - } - } - - /** - * Listener for adjustment of the vertical scroll bar. - * Sets the translation of the VisualizationViewer - */ - class VerticalAdjustmentListenerImpl implements AdjustmentListener { - int previous = 0; - public void adjustmentValueChanged(AdjustmentEvent e) { - JScrollBar sb = (JScrollBar)e.getSource(); - BoundedRangeModel m = sb.getModel(); - int vval = m.getValue(); - float dv = previous - vval; - previous = vval; - if(dv != 0 && scrollBarsMayControlAdjusting) { - - // get the uniform scale of all transforms - float layoutScale = (float) vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getScale(); - dv *= layoutScale; - AffineTransform at = AffineTransform.getTranslateInstance(0, dv); - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).preConcatenate(at); - } - } + add(vv); + add(verticalScrollBar, BorderLayout.EAST); + south = new JPanel(new BorderLayout()); + south.add(horizontalScrollBar); + setCorner(new JPanel()); + add(south, BorderLayout.SOUTH); + } + + /** + * listener for adjustment of the horizontal scroll bar. Sets the translation of the + * VisualizationViewer + */ + class HorizontalAdjustmentListenerImpl implements AdjustmentListener { + int previous = 0; + + public void adjustmentValueChanged(AdjustmentEvent e) { + int hval = e.getValue(); + float dh = previous - hval; + previous = hval; + if (dh != 0 && scrollBarsMayControlAdjusting) { + // get the uniform scale of all transforms + float layoutScale = + (float) + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .getScale(); + dh *= layoutScale; + AffineTransform at = AffineTransform.getTranslateInstance(dh, 0); + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .preConcatenate(at); + } } - - /** - * use the supplied vv characteristics to set the position and - * dimensions of the scroll bars. Called in response to - * a ChangeEvent from the VisualizationViewer - * @param xform the transform of the VisualizationViewer - */ - private void setScrollBars(VisualizationViewer vv) { - Dimension d = vv.getGraphLayout().getSize(); - Rectangle2D vvBounds = vv.getBounds(); - - // a rectangle representing the layout - Rectangle layoutRectangle = - new Rectangle(0,0,d.width,d.height); - //-d.width/2, -d.height/2, 2*d.width, 2*d.height); - - BidirectionalTransformer viewTransformer = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); - BidirectionalTransformer layoutTransformer = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - - Point2D h0 = new Point2D.Double(vvBounds.getMinX(), vvBounds.getCenterY()); - Point2D h1 = new Point2D.Double(vvBounds.getMaxX(), vvBounds.getCenterY()); - Point2D v0 = new Point2D.Double(vvBounds.getCenterX(), vvBounds.getMinY()); - Point2D v1 = new Point2D.Double(vvBounds.getCenterX(), vvBounds.getMaxY()); - - h0 = viewTransformer.inverseTransform(h0); - h0 = layoutTransformer.inverseTransform(h0); - h1 = viewTransformer.inverseTransform(h1); - h1 = layoutTransformer.inverseTransform(h1); - v0 = viewTransformer.inverseTransform(v0); - v0 = layoutTransformer.inverseTransform(v0); - v1 = viewTransformer.inverseTransform(v1); - v1 = layoutTransformer.inverseTransform(v1); - - scrollBarsMayControlAdjusting = false; - setScrollBarValues(layoutRectangle, h0, h1, v0, v1); - scrollBarsMayControlAdjusting = true; + } + + /** + * Listener for adjustment of the vertical scroll bar. Sets the translation of the + * VisualizationViewer + */ + class VerticalAdjustmentListenerImpl implements AdjustmentListener { + int previous = 0; + + public void adjustmentValueChanged(AdjustmentEvent e) { + JScrollBar sb = (JScrollBar) e.getSource(); + BoundedRangeModel m = sb.getModel(); + int vval = m.getValue(); + float dv = previous - vval; + previous = vval; + if (dv != 0 && scrollBarsMayControlAdjusting) { + + // get the uniform scale of all transforms + float layoutScale = + (float) + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .getScale(); + dv *= layoutScale; + AffineTransform at = AffineTransform.getTranslateInstance(0, dv); + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .preConcatenate(at); + } } - - protected void setScrollBarValues(Rectangle rectangle, - Point2D h0, Point2D h1, - Point2D v0, Point2D v1) { - boolean containsH0 = rectangle.contains(h0); - boolean containsH1 = rectangle.contains(h1); - boolean containsV0 = rectangle.contains(v0); - boolean containsV1 = rectangle.contains(v1); - - // horizontal scrollbar: - - Intersector intersector = new Intersector(rectangle, new Line2D.Double(h0, h1)); - - int min = 0; - int ext; - int val = 0; - int max; - - Set points = intersector.getPoints(); - Point2D first = null; - Point2D second = null; - - Point2D[] pointArray = (Point2D[])points.toArray(new Point2D[points.size()]); - if(pointArray.length > 1) { - first = pointArray[0]; - second = pointArray[1]; - } else if(pointArray.length > 0) { - first = second = pointArray[0]; - } - - if(first != null && second != null) { - // correct direction of intersect points - if((h0.getX() - h1.getX()) * (first.getX() - second.getX()) < 0) { - // swap them - Point2D temp = first; - first = second; - second = temp; - } - - if(containsH0 && containsH1) { - max = (int)first.distance(second); - val = (int)first.distance(h0); - ext = (int)h0.distance(h1); - - } else if(containsH0) { - max = (int)first.distance(second); - val = (int)first.distance(h0); - ext = (int)h0.distance(second); - - } else if(containsH1) { - max = (int) first.distance(second); - val = 0; - ext = (int) first.distance(h1); - - } else { - max = ext = rectangle.width; - val = min; - } - horizontalScrollBar.setValues(val, ext+1, min, max); - } - - // vertical scroll bar - min = val = 0; - - intersector.intersectLine(new Line2D.Double(v0, v1)); - points = intersector.getPoints(); - - pointArray = (Point2D[])points.toArray(new Point2D[points.size()]); - if(pointArray.length > 1) { - first = pointArray[0]; - second = pointArray[1]; - } else if(pointArray.length > 0) { - first = second = pointArray[0]; - } - - if(first != null && second != null) { - - // arrange for direction - if((v0.getY() - v1.getY()) * (first.getY() - second.getY()) < 0) { - // swap them - Point2D temp = first; - first = second; - second = temp; - } - - if(containsV0 && containsV1) { - max = (int)first.distance(second); - val = (int)first.distance(v0); - ext = (int)v0.distance(v1); - - } else if(containsV0) { - max = (int)first.distance(second); - val = (int)first.distance(v0); - ext = (int)v0.distance(second); - - } else if(containsV1) { - max = (int) first.distance(second); - val = 0; - ext = (int) first.distance(v1); - - } else { - max = ext = rectangle.height; - val = min; - } - verticalScrollBar.setValues(val, ext+1, min, max); - } + } + + /** + * use the supplied vv characteristics to set the position and dimensions of the scroll bars. + * Called in response to a ChangeEvent from the VisualizationViewer + * + * @param xform the transform of the VisualizationViewer + */ + private void setScrollBars(VisualizationViewer vv) { + Dimension d = vv.getGraphLayout().getSize(); + Rectangle2D vvBounds = vv.getBounds(); + + // a rectangle representing the layout + Rectangle layoutRectangle = new Rectangle(0, 0, d.width, d.height); + //-d.width/2, -d.height/2, 2*d.width, 2*d.height); + + BidirectionalTransformer viewTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); + BidirectionalTransformer layoutTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + + Point2D h0 = new Point2D.Double(vvBounds.getMinX(), vvBounds.getCenterY()); + Point2D h1 = new Point2D.Double(vvBounds.getMaxX(), vvBounds.getCenterY()); + Point2D v0 = new Point2D.Double(vvBounds.getCenterX(), vvBounds.getMinY()); + Point2D v1 = new Point2D.Double(vvBounds.getCenterX(), vvBounds.getMaxY()); + + h0 = viewTransformer.inverseTransform(h0); + h0 = layoutTransformer.inverseTransform(h0); + h1 = viewTransformer.inverseTransform(h1); + h1 = layoutTransformer.inverseTransform(h1); + v0 = viewTransformer.inverseTransform(v0); + v0 = layoutTransformer.inverseTransform(v0); + v1 = viewTransformer.inverseTransform(v1); + v1 = layoutTransformer.inverseTransform(v1); + + scrollBarsMayControlAdjusting = false; + setScrollBarValues(layoutRectangle, h0, h1, v0, v1); + scrollBarsMayControlAdjusting = true; + } + + protected void setScrollBarValues( + Rectangle rectangle, Point2D h0, Point2D h1, Point2D v0, Point2D v1) { + boolean containsH0 = rectangle.contains(h0); + boolean containsH1 = rectangle.contains(h1); + boolean containsV0 = rectangle.contains(v0); + boolean containsV1 = rectangle.contains(v1); + + // horizontal scrollbar: + + Intersector intersector = new Intersector(rectangle, new Line2D.Double(h0, h1)); + + int min = 0; + int ext; + int val = 0; + int max; + + Set points = intersector.getPoints(); + Point2D first = null; + Point2D second = null; + + Point2D[] pointArray = (Point2D[]) points.toArray(new Point2D[points.size()]); + if (pointArray.length > 1) { + first = pointArray[0]; + second = pointArray[1]; + } else if (pointArray.length > 0) { + first = second = pointArray[0]; } - /** - * Listener to adjust the scroll bar parameters when the window - * is resized - */ - protected class ResizeListener extends ComponentAdapter { - - public void componentHidden(ComponentEvent e) { - } - - public void componentResized(ComponentEvent e) { - setScrollBars(vv); - } - public void componentShown(ComponentEvent e) { - } - } - - /** - * @return Returns the corner component. - */ - public JComponent getCorner() { - return corner; + if (first != null && second != null) { + // correct direction of intersect points + if ((h0.getX() - h1.getX()) * (first.getX() - second.getX()) < 0) { + // swap them + Point2D temp = first; + first = second; + second = temp; + } + + if (containsH0 && containsH1) { + max = (int) first.distance(second); + val = (int) first.distance(h0); + ext = (int) h0.distance(h1); + + } else if (containsH0) { + max = (int) first.distance(second); + val = (int) first.distance(h0); + ext = (int) h0.distance(second); + + } else if (containsH1) { + max = (int) first.distance(second); + val = 0; + ext = (int) first.distance(h1); + + } else { + max = ext = rectangle.width; + val = min; + } + horizontalScrollBar.setValues(val, ext + 1, min, max); } - /** - * @param corner The cornerButton to set. - */ - public void setCorner(JComponent corner) { - this.corner = corner; - corner.setPreferredSize(new Dimension(verticalScrollBar.getPreferredSize().width, - horizontalScrollBar.getPreferredSize().height)); - south.add(this.corner, BorderLayout.EAST); + // vertical scroll bar + min = val = 0; + + intersector.intersectLine(new Line2D.Double(v0, v1)); + points = intersector.getPoints(); + + pointArray = (Point2D[]) points.toArray(new Point2D[points.size()]); + if (pointArray.length > 1) { + first = pointArray[0]; + second = pointArray[1]; + } else if (pointArray.length > 0) { + first = second = pointArray[0]; } - public JScrollBar getHorizontalScrollBar() { - return horizontalScrollBar; + if (first != null && second != null) { + + // arrange for direction + if ((v0.getY() - v1.getY()) * (first.getY() - second.getY()) < 0) { + // swap them + Point2D temp = first; + first = second; + second = temp; + } + + if (containsV0 && containsV1) { + max = (int) first.distance(second); + val = (int) first.distance(v0); + ext = (int) v0.distance(v1); + + } else if (containsV0) { + max = (int) first.distance(second); + val = (int) first.distance(v0); + ext = (int) v0.distance(second); + + } else if (containsV1) { + max = (int) first.distance(second); + val = 0; + ext = (int) first.distance(v1); + + } else { + max = ext = rectangle.height; + val = min; + } + verticalScrollBar.setValues(val, ext + 1, min, max); } + } + + /** Listener to adjust the scroll bar parameters when the window is resized */ + protected class ResizeListener extends ComponentAdapter { - public JScrollBar getVerticalScrollBar() { - return verticalScrollBar; + public void componentHidden(ComponentEvent e) {} + + public void componentResized(ComponentEvent e) { + setScrollBars(vv); } + + public void componentShown(ComponentEvent e) {} + } + + /** @return Returns the corner component. */ + public JComponent getCorner() { + return corner; + } + + /** @param corner The cornerButton to set. */ + public void setCorner(JComponent corner) { + this.corner = corner; + corner.setPreferredSize( + new Dimension( + verticalScrollBar.getPreferredSize().width, + horizontalScrollBar.getPreferredSize().height)); + south.add(this.corner, BorderLayout.EAST); + } + + public JScrollBar getHorizontalScrollBar() { + return horizontalScrollBar; + } + + public JScrollBar getVerticalScrollBar() { + return verticalScrollBar; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/Layer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/Layer.java index 708e980e..cd15cec6 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/Layer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/Layer.java @@ -1,5 +1,6 @@ package edu.uci.ics.jung.visualization; public enum Layer { - LAYOUT, VIEW + LAYOUT, + VIEW } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/LayeredIcon.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/LayeredIcon.java index c6480226..7ae0c619 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/LayeredIcon.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/LayeredIcon.java @@ -6,43 +6,40 @@ import java.awt.Image; import java.util.LinkedHashSet; import java.util.Set; - import javax.swing.Icon; import javax.swing.ImageIcon; /** - * An icon that is made up of a collection of Icons. - * They are rendered in layers starting with the first - * Icon added (from the constructor). - * - * @author Tom Nelson + * An icon that is made up of a collection of Icons. They are rendered in layers starting with the + * first Icon added (from the constructor). * + * @author Tom Nelson */ @SuppressWarnings("serial") public class LayeredIcon extends ImageIcon { - Set iconSet = new LinkedHashSet(); - - public LayeredIcon(Image image) { - super(image); - } - - public void paintIcon(Component c, Graphics g, int x, int y) { - super.paintIcon(c, g, x, y); - Dimension d = new Dimension(getIconWidth(), getIconHeight()); - for (Icon icon : iconSet) { - Dimension id = new Dimension(icon.getIconWidth(), icon.getIconHeight()); - int dx = (d.width - id.width)/2; - int dy = (d.height - id.height)/2; - icon.paintIcon(c, g, x+dx, y+dy); - } - } - - public void add(Icon icon) { - iconSet.add(icon); - } - - public boolean remove(Icon icon) { - return iconSet.remove(icon); - } -} \ No newline at end of file + Set iconSet = new LinkedHashSet(); + + public LayeredIcon(Image image) { + super(image); + } + + public void paintIcon(Component c, Graphics g, int x, int y) { + super.paintIcon(c, g, x, y); + Dimension d = new Dimension(getIconWidth(), getIconHeight()); + for (Icon icon : iconSet) { + Dimension id = new Dimension(icon.getIconWidth(), icon.getIconHeight()); + int dx = (d.width - id.width) / 2; + int dy = (d.height - id.height) / 2; + icon.paintIcon(c, g, x + dx, y + dy); + } + } + + public void add(Icon icon) { + iconSet.add(icon); + } + + public boolean remove(Icon icon) { + return iconSet.remove(icon); + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/MultiLayerTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/MultiLayerTransformer.java index 566f0503..301b2d9f 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/MultiLayerTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/MultiLayerTransformer.java @@ -1,28 +1,26 @@ package edu.uci.ics.jung.visualization; -import java.awt.Shape; -import java.awt.geom.Point2D; - import edu.uci.ics.jung.visualization.transform.BidirectionalTransformer; import edu.uci.ics.jung.visualization.transform.MutableTransformer; import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer; import edu.uci.ics.jung.visualization.util.ChangeEventSupport; +import java.awt.Shape; +import java.awt.geom.Point2D; -public interface MultiLayerTransformer extends BidirectionalTransformer, ShapeTransformer, ChangeEventSupport { +public interface MultiLayerTransformer + extends BidirectionalTransformer, ShapeTransformer, ChangeEventSupport { - - void setTransformer(Layer layer, MutableTransformer Function); + void setTransformer(Layer layer, MutableTransformer Function); - MutableTransformer getTransformer(Layer layer); + MutableTransformer getTransformer(Layer layer); - Point2D inverseTransform(Layer layer, Point2D p); + Point2D inverseTransform(Layer layer, Point2D p); - Point2D transform(Layer layer, Point2D p); + Point2D transform(Layer layer, Point2D p); - Shape transform(Layer layer, Shape shape); - - Shape inverseTransform(Layer layer, Shape shape); + Shape transform(Layer layer, Shape shape); - void setToIdentity(); + Shape inverseTransform(Layer layer, Shape shape); -} \ No newline at end of file + void setToIdentity(); +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/PivotingImageShaper.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/PivotingImageShaper.java index eac96b83..0e6d139b 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/PivotingImageShaper.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/PivotingImageShaper.java @@ -16,193 +16,185 @@ import java.awt.geom.Point2D; import java.awt.image.BufferedImage; - /** - * Provides Supplier methods that, given a BufferedImage, an Image, - * or the fileName of an image, will return a java.awt.Shape that - * is the contiguous traced outline of the opaque part of the image. - * This could be used to define an image for use in a Vertex, where - * the shape used for picking and edge-arrow placement follows the - * opaque part of an image that has a transparent background. - * The methods try to detect lines in order to minimize points - * in the path - * - * @author Tom Nelson + * Provides Supplier methods that, given a BufferedImage, an Image, or the fileName of an image, + * will return a java.awt.Shape that is the contiguous traced outline of the opaque part of the + * image. This could be used to define an image for use in a Vertex, where the shape used for + * picking and edge-arrow placement follows the opaque part of an image that has a transparent + * background. The methods try to detect lines in order to minimize points in the path * - * + * @author Tom Nelson */ public class PivotingImageShaper { - - /** - * the number of pixels to skip while sampling the - * images edges - */ - static int sample = 1; - /** - * the first x coordinate of the shape. Used to discern - * when we are done - */ - static int firstx = 0; - - - /** - * Given an image, possibly with a transparent background, return - * the Shape of the opaque part of the image - * @param image the image whose shape is being returned - * @return the Shape - */ - public static Shape getShape(BufferedImage image) { - firstx = 0; - return leftEdge(image, new GeneralPath()); + + /** the number of pixels to skip while sampling the images edges */ + static int sample = 1; + /** the first x coordinate of the shape. Used to discern when we are done */ + static int firstx = 0; + + /** + * Given an image, possibly with a transparent background, return the Shape of the opaque part of + * the image + * + * @param image the image whose shape is being returned + * @return the Shape + */ + public static Shape getShape(BufferedImage image) { + firstx = 0; + return leftEdge(image, new GeneralPath()); + } + + private static Point2D detectLine( + Point2D p1, Point2D p2, Point2D p, Line2D line, GeneralPath path) { + if (p2 == null) { + p2 = p; + line.setLine(p1, p2); } - - private static Point2D detectLine(Point2D p1, Point2D p2, Point2D p, - Line2D line, GeneralPath path) { - if(p2 == null) { - p2 = p; - line.setLine(p1,p2); - } - // check for line - else if(line.ptLineDistSq(p) < 1) { // its on the line - // make it p2 - p2.setLocation(p); - } else { // its not on the current line - p1.setLocation(p2); - p2.setLocation(p); - line.setLine(p1,p2); - path.lineTo((float)p1.getX(), (float)p1.getY()); - } - return p2; + // check for line + else if (line.ptLineDistSq(p) < 1) { // its on the line + // make it p2 + p2.setLocation(p); + } else { // its not on the current line + p1.setLocation(p2); + p2.setLocation(p); + line.setLine(p1, p2); + path.lineTo((float) p1.getX(), (float) p1.getY()); } - /** - * trace the left side of the image - * @param image - * @param path - * @return - */ - private static Shape leftEdge(BufferedImage image, GeneralPath path) { - int lastj = 0; - Point2D p1 = null; - Point2D p2 = null; - Line2D line = new Line2D.Float(); - for(int i=0; i=0; j-=sample) { - if((image.getRGB(i,j) & 0xff000000) != 0) { - // this is a point I want - Point2D p = new Point2D.Float(i,j); - aPointExistsOnThisLine = true; - p2 = detectLine(p1,p2,p,line,path); - lastj = j; - break; - } - } - if(aPointExistsOnThisLine == false) { - break; - } + return bottomEdge(image, path, lastj); + } + + /** + * trace the bottom of the image + * + * @param image + * @param path + * @param start + * @return + */ + private static Shape bottomEdge(BufferedImage image, GeneralPath path, int start) { + int lastj = 0; + Point2D p1 = path.getCurrentPoint(); + Point2D p2 = null; + Line2D line = new Line2D.Float(); + for (int i = start; i < image.getWidth(); i += sample) { + boolean aPointExistsOnThisLine = false; + for (int j = image.getHeight() - 1; j >= 0; j -= sample) { + if ((image.getRGB(i, j) & 0xff000000) != 0) { + // this is a point I want + Point2D p = new Point2D.Float(i, j); + aPointExistsOnThisLine = true; + p2 = detectLine(p1, p2, p, line, path); + lastj = j; + break; } - return rightEdge(image, path, lastj); + } + if (aPointExistsOnThisLine == false) { + break; + } } - - /** - * trace the right side of the image - * @param image - * @param path - * @param start - * @return - */ - private static Shape rightEdge(BufferedImage image, GeneralPath path, int start) { - int lastj = 0; - Point2D p1 = path.getCurrentPoint(); - Point2D p2 = null; - Line2D line = new Line2D.Float(); - for(int i=start; i>=0; i-=sample) { - boolean aPointExistsOnThisLine = false; + return rightEdge(image, path, lastj); + } - for(int j=image.getWidth()-1; j>=0; j-=sample) { - if((image.getRGB(j,i) & 0xff000000) != 0) { - // this is a point I want - Point2D p = new Point2D.Float(j,i); - aPointExistsOnThisLine = true; - p2 = detectLine(p1,p2,p,line,path); - lastj=j; - break; - } - } - if(aPointExistsOnThisLine == false) { - break; - } + /** + * trace the right side of the image + * + * @param image + * @param path + * @param start + * @return + */ + private static Shape rightEdge(BufferedImage image, GeneralPath path, int start) { + int lastj = 0; + Point2D p1 = path.getCurrentPoint(); + Point2D p2 = null; + Line2D line = new Line2D.Float(); + for (int i = start; i >= 0; i -= sample) { + boolean aPointExistsOnThisLine = false; + + for (int j = image.getWidth() - 1; j >= 0; j -= sample) { + if ((image.getRGB(j, i) & 0xff000000) != 0) { + // this is a point I want + Point2D p = new Point2D.Float(j, i); + aPointExistsOnThisLine = true; + p2 = detectLine(p1, p2, p, line, path); + lastj = j; + break; } - return topEdge(image, path, lastj); + } + if (aPointExistsOnThisLine == false) { + break; + } } - - /** - * trace the top of the image - * @param image - * @param path - * @param start - * @return - */ - private static Shape topEdge(BufferedImage image, GeneralPath path, int start) { - Point2D p1 = path.getCurrentPoint(); - Point2D p2 = null; - Line2D line = new Line2D.Float(); - for(int i=start; i>=firstx; i-=sample) { - boolean aPointExistsOnThisLine = false; - for(int j=0; j= firstx; i -= sample) { + boolean aPointExistsOnThisLine = false; + for (int j = 0; j < image.getHeight(); j += sample) { + if ((image.getRGB(i, j) & 0xff000000) != 0) { + // this is a point I want + Point2D p = new Point2D.Float(i, j); + aPointExistsOnThisLine = true; + p2 = detectLine(p1, p2, p, line, path); + break; } - path.closePath(); - return path; + } + if (aPointExistsOnThisLine == false) { + break; + } } + path.closePath(); + return path; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationImageServer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationImageServer.java index 123c8bb3..b7dd2bd8 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationImageServer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationImageServer.java @@ -1,14 +1,16 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.visualization; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.layout.Layout; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Image; @@ -18,56 +20,53 @@ import java.util.HashMap; import java.util.Map; -import com.google.common.graph.Network; - -import edu.uci.ics.jung.algorithms.layout.Layout; - /** * A class that could be used on the server side of a thin-client application. It creates the jung * visualization, then produces an image of it. - * @author tom * + * @author tom * @param the vertex type * @param the edge type */ @SuppressWarnings("serial") -public class VisualizationImageServer extends BasicVisualizationServer { +public class VisualizationImageServer extends BasicVisualizationServer { - Map renderingHints = new HashMap(); - - /** - * Creates a new instance with the specified layout and preferred size. - * - * @param layout the Layout instance; provides the vertex locations - * @param preferredSize the preferred size of the image - */ - public VisualizationImageServer(Network network, Layout layout, Dimension preferredSize) { - super(network, layout, preferredSize); - setSize(preferredSize); - renderingHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - addNotify(); - } - - public Image getImage(Point2D center, Dimension d) - { - int width = getWidth(); - int height = getHeight(); - - float scalex = (float)width/d.width; - float scaley = (float)height/d.height; - try - { - renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW).scale(scalex, scaley, center); - - BufferedImage bi = new BufferedImage(width, height, - BufferedImage.TYPE_INT_RGB); - Graphics2D graphics = bi.createGraphics(); - graphics.setRenderingHints(renderingHints); - paint(graphics); - graphics.dispose(); - return bi; - } finally { - renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW).setToIdentity(); - } + Map renderingHints = new HashMap(); + + /** + * Creates a new instance with the specified layout and preferred size. + * + * @param layout the Layout instance; provides the vertex locations + * @param preferredSize the preferred size of the image + */ + public VisualizationImageServer( + Network network, Layout layout, Dimension preferredSize) { + super(network, layout, preferredSize); + setSize(preferredSize); + renderingHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + addNotify(); + } + + public Image getImage(Point2D center, Dimension d) { + int width = getWidth(); + int height = getHeight(); + + float scalex = (float) width / d.width; + float scaley = (float) height / d.height; + try { + renderContext + .getMultiLayerTransformer() + .getTransformer(Layer.VIEW) + .scale(scalex, scaley, center); + + BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = bi.createGraphics(); + graphics.setRenderingHints(renderingHints); + paint(graphics); + graphics.dispose(); + return bi; + } finally { + renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW).setToIdentity(); } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationModel.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationModel.java index 4a8491ad..e418c6e2 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationModel.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationModel.java @@ -10,67 +10,64 @@ package edu.uci.ics.jung.visualization; -import java.awt.Dimension; - -import javax.swing.event.ChangeListener; - import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.algorithms.layout.util.Relaxer; import edu.uci.ics.jung.visualization.util.ChangeEventSupport; +import java.awt.Dimension; +import javax.swing.event.ChangeListener; /** - * Interface for the state holding model of the VisualizationViewer. - * Refactored and extracted from the 1.6.0 version of VisualizationViewer - * - * @author Tom Nelson + * Interface for the state holding model of the VisualizationViewer. Refactored and extracted from + * the 1.6.0 version of VisualizationViewer + * + * @author Tom Nelson */ public interface VisualizationModel extends ChangeEventSupport { - Relaxer getRelaxer(); + Relaxer getRelaxer(); + + /** + * set the graph Layout + * + * @param layout the layout to use + */ + void setGraphLayout(Layout layout); + + /** + * Sets the graph Layout and initialize the Layout size to the passed dimensions. The passed + * Dimension will often be the size of the View that will display the graph. + * + * @param layout the layout to use + * @param d the dimensions to use + */ + void setGraphLayout(Layout layout, Dimension d); - /** - * set the graph Layout - * @param layout the layout to use - */ - void setGraphLayout(Layout layout); - - /** - * Sets the graph Layout and initialize the Layout size to - * the passed dimensions. The passed Dimension will often be - * the size of the View that will display the graph. - * @param layout the layout to use - * @param d the dimensions to use - */ - void setGraphLayout(Layout layout, Dimension d); + /** @return the current graph layout */ + Layout getGraphLayout(); - /** - * @return the current graph layout - */ - Layout getGraphLayout(); - - Network getNetwork(); + Network getNetwork(); - /** - * Register l as a listeners to changes in the model. The View registers - * in order to repaint itself when the model changes. - * @param l the listener to add - */ - void addChangeListener(ChangeListener l); + /** + * Register l as a listeners to changes in the model. The View registers in order to + * repaint itself when the model changes. + * + * @param l the listener to add + */ + void addChangeListener(ChangeListener l); - /** - * Removes a ChangeListener. - * @param l the listener to be removed - */ - void removeChangeListener(ChangeListener l); + /** + * Removes a ChangeListener. + * + * @param l the listener to be removed + */ + void removeChangeListener(ChangeListener l); - /** - * Returns an array of all the ChangeListeners added - * with addChangeListener(). - * - * @return all of the ChangeListeners added or an empty - * array if no listeners have been added - */ - ChangeListener[] getChangeListeners(); -} \ No newline at end of file + /** + * Returns an array of all the ChangeListeners added with addChangeListener(). + * + * @return all of the ChangeListeners added or an empty array if no listeners have + * been added + */ + ChangeListener[] getChangeListeners(); +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationServer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationServer.java index 46444f49..bb8e35ed 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationServer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationServer.java @@ -1,198 +1,167 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.visualization; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; +import edu.uci.ics.jung.visualization.picking.PickedState; +import edu.uci.ics.jung.visualization.renderers.Renderer; import java.awt.Graphics; import java.awt.RenderingHints.Key; import java.awt.geom.Point2D; import java.util.Map; - import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.EventListenerList; -import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.picking.PickedState; -import edu.uci.ics.jung.visualization.renderers.Renderer; - /** * @author tom - * * @param the vertex type * @param the edge type */ public interface VisualizationServer { - /** - * Specify whether this class uses its offscreen image or not. - * - * @param doubleBuffered if true, then doubleBuffering in the superclass is set to 'false' - */ - void setDoubleBuffered(boolean doubleBuffered); - - /** - * Returns whether this class uses double buffering. The superclass - * will be the opposite state. - * @return the double buffered state - */ - boolean isDoubleBuffered(); - - /** - * @return the model. - */ - VisualizationModel getModel(); - - /** - * @param model the model for this class to use - */ - void setModel(VisualizationModel model); - - /** - * In response to changes from the model, repaint the - * view, then fire an event to any listeners. - * Examples of listeners are the GraphZoomScrollPane and - * the BirdsEyeVisualizationViewer - * @param e the change event - */ - void stateChanged(ChangeEvent e); - - /** - * Sets the showing Renderer to be the input Renderer. Also - * tells the Renderer to refer to this instance - * as a PickedKey. (Because Renderers maintain a small - * amount of state, such as the PickedKey, it is important - * to create a separate instance for each VV instance.) - * @param r the renderer to use - */ - void setRenderer(Renderer r); - - /** - * @return the renderer used by this instance. - */ - Renderer getRenderer(); - - /** - * Replaces the current graph layout with {@code layout}. - * @param layout the new layout to set - */ - void setGraphLayout(Layout layout); - - /** - * @return the current graph layout. - */ - Layout getGraphLayout(); - - /** - * Makes the component visible if {@code aFlag} is true, or invisible if false. - * @param aFlag true iff the component should be visible - * @see javax.swing.JComponent#setVisible(boolean) - */ - void setVisible(boolean aFlag); - - /** - * @return the renderingHints - */ - Map getRenderingHints(); - - /** - * @param renderingHints The renderingHints to set. - */ - void setRenderingHints(Map renderingHints); - - /** - * @param paintable The paintable to add. - */ - void addPreRenderPaintable(Paintable paintable); - - /** - * @param paintable The paintable to remove. - */ - void removePreRenderPaintable(Paintable paintable); - - /** - * @param paintable The paintable to add. - */ - void addPostRenderPaintable(Paintable paintable); - - /** - * @param paintable The paintable to remove. - */ - void removePostRenderPaintable(Paintable paintable); - - /** - * Adds a ChangeListener. - * @param l the listener to be added - */ - void addChangeListener(ChangeListener l); - - /** - * Removes a ChangeListener. - * @param l the listener to be removed - */ - void removeChangeListener(ChangeListener l); - - /** - * Returns an array of all the ChangeListeners added - * with addChangeListener(). - * - * @return all of the ChangeListeners added or an empty - * array if no listeners have been added - */ - ChangeListener[] getChangeListeners(); - - /** - * Notifies all listeners that have registered interest for - * notification on this event type. The event instance - * is lazily created. - * @see EventListenerList - */ - void fireStateChanged(); - - /** - * @return the vertex PickedState instance - */ - PickedState getPickedVertexState(); - - /** - * @return the edge PickedState instance - */ - PickedState getPickedEdgeState(); - - void setPickedVertexState(PickedState pickedVertexState); - - void setPickedEdgeState(PickedState pickedEdgeState); - - /** - * @return the NetworkElementAccessor - */ - NetworkElementAccessor getPickSupport(); - - /** - * @param pickSupport The pickSupport to set. - */ - void setPickSupport(NetworkElementAccessor pickSupport); - - Point2D getCenter(); - - RenderContext getRenderContext(); - - void setRenderContext(RenderContext renderContext); - - void repaint(); - - /** - * an interface for the preRender and postRender - */ - interface Paintable { - public void paint(Graphics g); - public boolean useTransform(); - } -} \ No newline at end of file + /** + * Specify whether this class uses its offscreen image or not. + * + * @param doubleBuffered if true, then doubleBuffering in the superclass is set to 'false' + */ + void setDoubleBuffered(boolean doubleBuffered); + + /** + * Returns whether this class uses double buffering. The superclass will be the opposite state. + * + * @return the double buffered state + */ + boolean isDoubleBuffered(); + + /** @return the model. */ + VisualizationModel getModel(); + + /** @param model the model for this class to use */ + void setModel(VisualizationModel model); + + /** + * In response to changes from the model, repaint the view, then fire an event to any listeners. + * Examples of listeners are the GraphZoomScrollPane and the BirdsEyeVisualizationViewer + * + * @param e the change event + */ + void stateChanged(ChangeEvent e); + + /** + * Sets the showing Renderer to be the input Renderer. Also tells the Renderer to refer to this + * instance as a PickedKey. (Because Renderers maintain a small amount of state, such as the + * PickedKey, it is important to create a separate instance for each VV instance.) + * + * @param r the renderer to use + */ + void setRenderer(Renderer r); + + /** @return the renderer used by this instance. */ + Renderer getRenderer(); + + /** + * Replaces the current graph layout with {@code layout}. + * + * @param layout the new layout to set + */ + void setGraphLayout(Layout layout); + + /** @return the current graph layout. */ + Layout getGraphLayout(); + + /** + * Makes the component visible if {@code aFlag} is true, or invisible if false. + * + * @param aFlag true iff the component should be visible + * @see javax.swing.JComponent#setVisible(boolean) + */ + void setVisible(boolean aFlag); + + /** @return the renderingHints */ + Map getRenderingHints(); + + /** @param renderingHints The renderingHints to set. */ + void setRenderingHints(Map renderingHints); + + /** @param paintable The paintable to add. */ + void addPreRenderPaintable(Paintable paintable); + + /** @param paintable The paintable to remove. */ + void removePreRenderPaintable(Paintable paintable); + + /** @param paintable The paintable to add. */ + void addPostRenderPaintable(Paintable paintable); + + /** @param paintable The paintable to remove. */ + void removePostRenderPaintable(Paintable paintable); + + /** + * Adds a ChangeListener. + * + * @param l the listener to be added + */ + void addChangeListener(ChangeListener l); + + /** + * Removes a ChangeListener. + * + * @param l the listener to be removed + */ + void removeChangeListener(ChangeListener l); + + /** + * Returns an array of all the ChangeListeners added with addChangeListener(). + * + * @return all of the ChangeListeners added or an empty array if no listeners have + * been added + */ + ChangeListener[] getChangeListeners(); + + /** + * Notifies all listeners that have registered interest for notification on this event type. The + * event instance is lazily created. + * + * @see EventListenerList + */ + void fireStateChanged(); + + /** @return the vertex PickedState instance */ + PickedState getPickedVertexState(); + + /** @return the edge PickedState instance */ + PickedState getPickedEdgeState(); + + void setPickedVertexState(PickedState pickedVertexState); + + void setPickedEdgeState(PickedState pickedEdgeState); + + /** @return the NetworkElementAccessor */ + NetworkElementAccessor getPickSupport(); + + /** @param pickSupport The pickSupport to set. */ + void setPickSupport(NetworkElementAccessor pickSupport); + + Point2D getCenter(); + + RenderContext getRenderContext(); + + void setRenderContext(RenderContext renderContext); + + void repaint(); + + /** an interface for the preRender and postRender */ + interface Paintable { + public void paint(Graphics g); + + public boolean useTransform(); + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationViewer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationViewer.java index 34b0e627..7600dd28 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationViewer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/VisualizationViewer.java @@ -1,14 +1,19 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.visualization; +import com.google.common.base.Function; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.control.GraphMouseListener; +import edu.uci.ics.jung.visualization.control.MouseListenerTranslator; import java.awt.Dimension; import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; @@ -17,176 +22,153 @@ import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelListener; import java.awt.geom.Point2D; - import javax.swing.ToolTipManager; -import com.google.common.base.Function; -import com.google.common.graph.Network; - -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.control.GraphMouseListener; -import edu.uci.ics.jung.visualization.control.MouseListenerTranslator; - /** * Adds mouse behaviors and tooltips to the graph visualization base class - * + * * @author Joshua O'Madadhain - * @author Tom Nelson + * @author Tom Nelson * @author Danyel Fisher */ @SuppressWarnings("serial") -public class VisualizationViewer extends BasicVisualizationServer { - - protected Function vertexToolTipTransformer; - protected Function edgeToolTipTransformer; - protected Function mouseEventToolTipTransformer; - - /** - * provides MouseListener, MouseMotionListener, and MouseWheelListener - * events to the graph - */ - protected GraphMouse graphMouse; - - protected MouseListener requestFocusListener = new MouseAdapter() { - public void mouseClicked(MouseEvent e) { - requestFocusInWindow(); - } - }; - - - public VisualizationViewer(Network network, Layout layout) { - this(new DefaultVisualizationModel(network, layout)); - } - - public VisualizationViewer(Network network, Layout layout, Dimension preferredSize) { - this(new DefaultVisualizationModel(network, layout, preferredSize), preferredSize); - } - - public VisualizationViewer(VisualizationModel model) { - this(model, new Dimension(600,600)); - } - - public VisualizationViewer(VisualizationModel model, - Dimension preferredSize) { - super(model, preferredSize); - setFocusable(true); - addMouseListener(requestFocusListener); - } - - /** - * a setter for the GraphMouse. This will remove any - * previous GraphMouse (including the one that - * is added in the initMouseClicker method. - * @param graphMouse new value - */ - public void setGraphMouse(GraphMouse graphMouse) { - this.graphMouse = graphMouse; - MouseListener[] ml = getMouseListeners(); - for(int i=0; iGraphMouse - */ - public GraphMouse getGraphMouse() { - return graphMouse; - } - - /** - * This is the interface for adding a mouse listener. The GEL - * will be called back with mouse clicks on vertices. - * @param gel the mouse listener to add - */ - public void addGraphMouseListener( GraphMouseListener gel ) { - addMouseListener( new MouseListenerTranslator( gel, this )); - } - - /** - * Override to request focus on mouse enter, if a key listener is added - * @see java.awt.Component#addKeyListener(java.awt.event.KeyListener) - */ - @Override - public synchronized void addKeyListener(KeyListener l) { - super.addKeyListener(l); - } - - /** - * @param edgeToolTipTransformer the edgeToolTipTransformer to set - */ - public void setEdgeToolTipTransformer( - Function edgeToolTipTransformer) { - this.edgeToolTipTransformer = edgeToolTipTransformer; - ToolTipManager.sharedInstance().registerComponent(this); - } - - /** - * @param mouseEventToolTipTransformer the mouseEventToolTipTransformer to set - */ - public void setMouseEventToolTipTransformer( - Function mouseEventToolTipTransformer) { - this.mouseEventToolTipTransformer = mouseEventToolTipTransformer; - ToolTipManager.sharedInstance().registerComponent(this); - } - - /** - * @param vertexToolTipTransformer the vertexToolTipTransformer to set - */ - public void setVertexToolTipTransformer( - Function vertexToolTipTransformer) { - this.vertexToolTipTransformer = vertexToolTipTransformer; - ToolTipManager.sharedInstance().registerComponent(this); - } - - /** - * called by the superclass to display tooltips - */ - public String getToolTipText(MouseEvent event) { -// Layout layout = getGraphLayout(); - Point2D p = null; - if(vertexToolTipTransformer != null) { - p = event.getPoint(); - //renderContext.getBasicTransformer().inverseViewTransform(event.getPoint()); - V vertex = getPickSupport().getNode(p.getX(), p.getY()); - if(vertex != null) { - return vertexToolTipTransformer.apply(vertex); - } - } - if(edgeToolTipTransformer != null) { - if(p == null) p = renderContext.getMultiLayerTransformer().inverseTransform(Layer.VIEW, event.getPoint()); - E edge = getPickSupport().getEdge(p.getX(), p.getY()); - if(edge != null) { - return edgeToolTipTransformer.apply(edge); - } - } - if(mouseEventToolTipTransformer != null) { - return mouseEventToolTipTransformer.apply(event); +public class VisualizationViewer extends BasicVisualizationServer { + + protected Function vertexToolTipTransformer; + protected Function edgeToolTipTransformer; + protected Function mouseEventToolTipTransformer; + + /** provides MouseListener, MouseMotionListener, and MouseWheelListener events to the graph */ + protected GraphMouse graphMouse; + + protected MouseListener requestFocusListener = + new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + requestFocusInWindow(); } - return super.getToolTipText(event); + }; + + public VisualizationViewer(Network network, Layout layout) { + this(new DefaultVisualizationModel(network, layout)); + } + + public VisualizationViewer(Network network, Layout layout, Dimension preferredSize) { + this(new DefaultVisualizationModel(network, layout, preferredSize), preferredSize); + } + + public VisualizationViewer(VisualizationModel model) { + this(model, new Dimension(600, 600)); + } + + public VisualizationViewer(VisualizationModel model, Dimension preferredSize) { + super(model, preferredSize); + setFocusable(true); + addMouseListener(requestFocusListener); + } + + /** + * a setter for the GraphMouse. This will remove any previous GraphMouse (including the one that + * is added in the initMouseClicker method. + * + * @param graphMouse new value + */ + public void setGraphMouse(GraphMouse graphMouse) { + this.graphMouse = graphMouse; + MouseListener[] ml = getMouseListeners(); + for (int i = 0; i < ml.length; i++) { + if (ml[i] instanceof GraphMouse) { + removeMouseListener(ml[i]); + } + } + MouseMotionListener[] mml = getMouseMotionListeners(); + for (int i = 0; i < mml.length; i++) { + if (mml[i] instanceof GraphMouse) { + removeMouseMotionListener(mml[i]); + } + } + MouseWheelListener[] mwl = getMouseWheelListeners(); + for (int i = 0; i < mwl.length; i++) { + if (mwl[i] instanceof GraphMouse) { + removeMouseWheelListener(mwl[i]); + } + } + addMouseListener(graphMouse); + addMouseMotionListener(graphMouse); + addMouseWheelListener(graphMouse); + } + + /** @return the current GraphMouse */ + public GraphMouse getGraphMouse() { + return graphMouse; + } + + /** + * This is the interface for adding a mouse listener. The GEL will be called back with mouse + * clicks on vertices. + * + * @param gel the mouse listener to add + */ + public void addGraphMouseListener(GraphMouseListener gel) { + addMouseListener(new MouseListenerTranslator(gel, this)); + } + + /** + * Override to request focus on mouse enter, if a key listener is added + * + * @see java.awt.Component#addKeyListener(java.awt.event.KeyListener) + */ + @Override + public synchronized void addKeyListener(KeyListener l) { + super.addKeyListener(l); + } + + /** @param edgeToolTipTransformer the edgeToolTipTransformer to set */ + public void setEdgeToolTipTransformer(Function edgeToolTipTransformer) { + this.edgeToolTipTransformer = edgeToolTipTransformer; + ToolTipManager.sharedInstance().registerComponent(this); + } + + /** @param mouseEventToolTipTransformer the mouseEventToolTipTransformer to set */ + public void setMouseEventToolTipTransformer( + Function mouseEventToolTipTransformer) { + this.mouseEventToolTipTransformer = mouseEventToolTipTransformer; + ToolTipManager.sharedInstance().registerComponent(this); + } + + /** @param vertexToolTipTransformer the vertexToolTipTransformer to set */ + public void setVertexToolTipTransformer(Function vertexToolTipTransformer) { + this.vertexToolTipTransformer = vertexToolTipTransformer; + ToolTipManager.sharedInstance().registerComponent(this); + } + + /** called by the superclass to display tooltips */ + public String getToolTipText(MouseEvent event) { + // Layout layout = getGraphLayout(); + Point2D p = null; + if (vertexToolTipTransformer != null) { + p = event.getPoint(); + //renderContext.getBasicTransformer().inverseViewTransform(event.getPoint()); + V vertex = getPickSupport().getNode(p.getX(), p.getY()); + if (vertex != null) { + return vertexToolTipTransformer.apply(vertex); + } + } + if (edgeToolTipTransformer != null) { + if (p == null) + p = renderContext.getMultiLayerTransformer().inverseTransform(Layer.VIEW, event.getPoint()); + E edge = getPickSupport().getEdge(p.getX(), p.getY()); + if (edge != null) { + return edgeToolTipTransformer.apply(edge); + } + } + if (mouseEventToolTipTransformer != null) { + return mouseEventToolTipTransformer.apply(event); } + return super.getToolTipText(event); + } - /** - * a convenience type to represent a class that - * processes all types of mouse events for the graph - */ - public interface GraphMouse extends MouseListener, MouseMotionListener, MouseWheelListener {} - + /** + * a convenience type to represent a class that processes all types of mouse events for the graph + */ + public interface GraphMouse extends MouseListener, MouseMotionListener, MouseWheelListener {} } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotatingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotatingGraphMousePlugin.java index c9bf5757..bedc2e8c 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotatingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotatingGraphMousePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -10,6 +10,11 @@ */ package edu.uci.ics.jung.visualization.annotations; +import edu.uci.ics.jung.visualization.MultiLayerTransformer; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.VisualizationServer.Paintable; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin; import java.awt.Color; import java.awt.Cursor; import java.awt.Graphics; @@ -22,283 +27,222 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RectangularShape; - import javax.swing.JComponent; import javax.swing.JOptionPane; -import edu.uci.ics.jung.visualization.MultiLayerTransformer; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.VisualizationServer.Paintable; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin; - -/** - * AnnotatingGraphMousePlugin can create Shape and Text annotations - * in a layer of the graph visualization. - * +/** + * AnnotatingGraphMousePlugin can create Shape and Text annotations in a layer of the graph + * visualization. + * * @author Tom Nelson */ public class AnnotatingGraphMousePlugin extends AbstractGraphMousePlugin implements MouseListener, MouseMotionListener { - /** - * additional modifiers for the action of adding to an existing - * selection - */ - protected int additionalModifiers; - - /** - * used to draw a Shape annotation - */ - protected RectangularShape rectangularShape = new Rectangle2D.Float(); - - /** - * the Paintable for the Shape annotation - */ - protected Paintable lensPaintable; - - /** - * a Paintable to store all Annotations - */ - protected AnnotationManager annotationManager; - - /** - * color for annotations - */ - protected Color annotationColor = Color.cyan; - - /** - * layer for annotations - */ - protected Annotation.Layer layer = Annotation.Layer.LOWER; - - protected boolean fill; - - /** - * holds rendering transforms - */ - protected MultiLayerTransformer basicTransformer; - - /** - * holds rendering settings - */ - protected RenderContext rc; - - /** - * set to true when the AnnotationPaintable has been - * added to the view component - */ - protected boolean added = false; - - /** - * Create an instance with defaults for primary (button 1) and secondary - * (button 1 + shift) selection. - * @param rc the RenderContext for which this plugin will be used - */ - public AnnotatingGraphMousePlugin(RenderContext rc) { - this(rc, InputEvent.BUTTON1_MASK, - InputEvent.BUTTON1_MASK | InputEvent.SHIFT_MASK); - } - - /** - * Create an instance with the specified primary and secondary selection - * mechanisms. - * @param rc the RenderContext for which this plugin will be used - * @param selectionModifiers for primary selection - * @param additionalModifiers for additional selection - */ - public AnnotatingGraphMousePlugin(RenderContext rc, - int selectionModifiers, int additionalModifiers) { - super(selectionModifiers); - this.rc = rc; - this.basicTransformer = rc.getMultiLayerTransformer(); - this.additionalModifiers = additionalModifiers; - this.lensPaintable = new LensPaintable(); - this.annotationManager = new AnnotationManager(rc); - this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); - } - - /** - * @return Returns the lensColor. - */ - public Color getAnnotationColor() { - return annotationColor; - } - - /** - * @param lensColor The lensColor to set. - */ - public void setAnnotationColor(Color lensColor) { - this.annotationColor = lensColor; - } - - /** - * the Paintable that draws a Shape annotation - * only while it is being created - * - */ - class LensPaintable implements Paintable { - - public void paint(Graphics g) { - Color oldColor = g.getColor(); - g.setColor(annotationColor); - ((Graphics2D)g).draw(rectangularShape); - g.setColor(oldColor); - } - - public boolean useTransform() { - return false; - } + /** additional modifiers for the action of adding to an existing selection */ + protected int additionalModifiers; + + /** used to draw a Shape annotation */ + protected RectangularShape rectangularShape = new Rectangle2D.Float(); + + /** the Paintable for the Shape annotation */ + protected Paintable lensPaintable; + + /** a Paintable to store all Annotations */ + protected AnnotationManager annotationManager; + + /** color for annotations */ + protected Color annotationColor = Color.cyan; + + /** layer for annotations */ + protected Annotation.Layer layer = Annotation.Layer.LOWER; + + protected boolean fill; + + /** holds rendering transforms */ + protected MultiLayerTransformer basicTransformer; + + /** holds rendering settings */ + protected RenderContext rc; + + /** set to true when the AnnotationPaintable has been added to the view component */ + protected boolean added = false; + + /** + * Create an instance with defaults for primary (button 1) and secondary (button 1 + shift) + * selection. + * + * @param rc the RenderContext for which this plugin will be used + */ + public AnnotatingGraphMousePlugin(RenderContext rc) { + this(rc, InputEvent.BUTTON1_MASK, InputEvent.BUTTON1_MASK | InputEvent.SHIFT_MASK); + } + + /** + * Create an instance with the specified primary and secondary selection mechanisms. + * + * @param rc the RenderContext for which this plugin will be used + * @param selectionModifiers for primary selection + * @param additionalModifiers for additional selection + */ + public AnnotatingGraphMousePlugin( + RenderContext rc, int selectionModifiers, int additionalModifiers) { + super(selectionModifiers); + this.rc = rc; + this.basicTransformer = rc.getMultiLayerTransformer(); + this.additionalModifiers = additionalModifiers; + this.lensPaintable = new LensPaintable(); + this.annotationManager = new AnnotationManager(rc); + this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); + } + + /** @return Returns the lensColor. */ + public Color getAnnotationColor() { + return annotationColor; + } + + /** @param lensColor The lensColor to set. */ + public void setAnnotationColor(Color lensColor) { + this.annotationColor = lensColor; + } + + /** the Paintable that draws a Shape annotation only while it is being created */ + class LensPaintable implements Paintable { + + public void paint(Graphics g) { + Color oldColor = g.getColor(); + g.setColor(annotationColor); + ((Graphics2D) g).draw(rectangularShape); + g.setColor(oldColor); } - /** - * Sets the location for an Annotation. - * Will either pop up a dialog to prompt for text - * input for a text annotation, or begin the process - * of drawing a Shape annotation - * - * @param e the event - */ - @SuppressWarnings("unchecked") - public void mousePressed(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - down = e.getPoint(); - - if(added == false) { - vv.addPreRenderPaintable(annotationManager.getLowerAnnotationPaintable()); - vv.addPostRenderPaintable(annotationManager.getUpperAnnotationPaintable()); - added = true; - } - - - if(e.isPopupTrigger()) { - String annotationString = JOptionPane.showInputDialog(vv,"Annotation:"); - if(annotationString != null && annotationString.length() > 0) { - Point2D p = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(down); - Annotation annotation = - new Annotation(annotationString, layer, annotationColor, fill, p); - annotationManager.add(layer, annotation); - } - } else if(e.getModifiers() == additionalModifiers) { - Annotation annotation = annotationManager.getAnnotation(down); - annotationManager.remove(annotation); - } else if(e.getModifiers() == modifiers) { - rectangularShape.setFrameFromDiagonal(down,down); - vv.addPostRenderPaintable(lensPaintable); - } - vv.repaint(); + public boolean useTransform() { + return false; } - - /** - * Completes the process of adding a Shape annotation - * and removed the transient paintable - * - */ - @SuppressWarnings("unchecked") - public void mouseReleased(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - if(e.isPopupTrigger()) { - String annotationString = JOptionPane.showInputDialog(vv,"Annotation:"); - if(annotationString != null && annotationString.length() > 0) { - Point2D p = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(down); - Annotation annotation = - new Annotation(annotationString, layer, annotationColor, fill, p); - annotationManager.add(layer, annotation); - } - } else if(e.getModifiers() == modifiers) { - if(down != null) { - Point2D out = e.getPoint(); - RectangularShape arect = (RectangularShape)rectangularShape.clone(); - arect.setFrameFromDiagonal(down,out); - Shape s = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(arect); - Annotation annotation = - new Annotation(s, layer, annotationColor, fill, out); - annotationManager.add(layer, annotation); - } - } - down = null; - vv.removePostRenderPaintable(lensPaintable); - vv.repaint(); - } - - /** - * Draws the transient Paintable that will become - * a Shape annotation when the mouse button is - * released - * - */ - @SuppressWarnings("unchecked") - public void mouseDragged(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - - Point2D out = e.getPoint(); - if(e.getModifiers() == additionalModifiers) { - rectangularShape.setFrameFromDiagonal(down,out); - - } else if(e.getModifiers() == modifiers) { - rectangularShape.setFrameFromDiagonal(down,out); - - } - rectangularShape.setFrameFromDiagonal(down,out); - vv.repaint(); - } - - public void mouseClicked(MouseEvent e) { + } + + /** + * Sets the location for an Annotation. Will either pop up a dialog to prompt for text input for a + * text annotation, or begin the process of drawing a Shape annotation + * + * @param e the event + */ + @SuppressWarnings("unchecked") + public void mousePressed(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + down = e.getPoint(); + + if (added == false) { + vv.addPreRenderPaintable(annotationManager.getLowerAnnotationPaintable()); + vv.addPostRenderPaintable(annotationManager.getUpperAnnotationPaintable()); + added = true; } - public void mouseEntered(MouseEvent e) { - JComponent c = (JComponent)e.getSource(); - c.setCursor(cursor); + if (e.isPopupTrigger()) { + String annotationString = JOptionPane.showInputDialog(vv, "Annotation:"); + if (annotationString != null && annotationString.length() > 0) { + Point2D p = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(down); + Annotation annotation = + new Annotation(annotationString, layer, annotationColor, fill, p); + annotationManager.add(layer, annotation); + } + } else if (e.getModifiers() == additionalModifiers) { + Annotation annotation = annotationManager.getAnnotation(down); + annotationManager.remove(annotation); + } else if (e.getModifiers() == modifiers) { + rectangularShape.setFrameFromDiagonal(down, down); + vv.addPostRenderPaintable(lensPaintable); } - - public void mouseExited(MouseEvent e) { - JComponent c = (JComponent)e.getSource(); - c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + vv.repaint(); + } + + /** Completes the process of adding a Shape annotation and removed the transient paintable */ + @SuppressWarnings("unchecked") + public void mouseReleased(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + if (e.isPopupTrigger()) { + String annotationString = JOptionPane.showInputDialog(vv, "Annotation:"); + if (annotationString != null && annotationString.length() > 0) { + Point2D p = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(down); + Annotation annotation = + new Annotation(annotationString, layer, annotationColor, fill, p); + annotationManager.add(layer, annotation); + } + } else if (e.getModifiers() == modifiers) { + if (down != null) { + Point2D out = e.getPoint(); + RectangularShape arect = (RectangularShape) rectangularShape.clone(); + arect.setFrameFromDiagonal(down, out); + Shape s = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(arect); + Annotation annotation = new Annotation(s, layer, annotationColor, fill, out); + annotationManager.add(layer, annotation); + } } - - public void mouseMoved(MouseEvent e) { + down = null; + vv.removePostRenderPaintable(lensPaintable); + vv.repaint(); + } + + /** + * Draws the transient Paintable that will become a Shape annotation when the mouse button is + * released + */ + @SuppressWarnings("unchecked") + public void mouseDragged(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + + Point2D out = e.getPoint(); + if (e.getModifiers() == additionalModifiers) { + rectangularShape.setFrameFromDiagonal(down, out); + + } else if (e.getModifiers() == modifiers) { + rectangularShape.setFrameFromDiagonal(down, out); } - - /** - * @return the rect - */ - public RectangularShape getRectangularShape() { - return rectangularShape; - } - - /** - * @param rect the rect to set - */ - public void setRectangularShape(RectangularShape rect) { - this.rectangularShape = rect; - } - - /** - * @return the layer - */ - public Annotation.Layer getLayer() { - return layer; - } - - /** - * @param layer the layer to set - */ - public void setLayer(Annotation.Layer layer) { - this.layer = layer; - } - - /** - * @return the fill - */ - public boolean isFill() { - return fill; - } - - /** - * @param fill the fill to set - */ - public void setFill(boolean fill) { - this.fill = fill; - } - - } + rectangularShape.setFrameFromDiagonal(down, out); + vv.repaint(); + } + + public void mouseClicked(MouseEvent e) {} + + public void mouseEntered(MouseEvent e) { + JComponent c = (JComponent) e.getSource(); + c.setCursor(cursor); + } + + public void mouseExited(MouseEvent e) { + JComponent c = (JComponent) e.getSource(); + c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + + public void mouseMoved(MouseEvent e) {} + + /** @return the rect */ + public RectangularShape getRectangularShape() { + return rectangularShape; + } + + /** @param rect the rect to set */ + public void setRectangularShape(RectangularShape rect) { + this.rectangularShape = rect; + } + + /** @return the layer */ + public Annotation.Layer getLayer() { + return layer; + } + + /** @param layer the layer to set */ + public void setLayer(Annotation.Layer layer) { + this.layer = layer; + } + + /** @return the fill */ + public boolean isFill() { + return fill; + } + + /** @param fill the fill to set */ + public void setFill(boolean fill) { + this.fill = fill; + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotatingModalGraphMouse.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotatingModalGraphMouse.java index 61e18765..049daa2d 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotatingModalGraphMouse.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotatingModalGraphMouse.java @@ -5,10 +5,21 @@ * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. * - * + * */ package edu.uci.ics.jung.visualization.annotations; +import edu.uci.ics.jung.visualization.MultiLayerTransformer; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.control.AbstractModalGraphMouse; +import edu.uci.ics.jung.visualization.control.AnimatedPickingGraphMousePlugin; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.ModalGraphMouse; +import edu.uci.ics.jung.visualization.control.PickingGraphMousePlugin; +import edu.uci.ics.jung.visualization.control.RotatingGraphMousePlugin; +import edu.uci.ics.jung.visualization.control.ScalingGraphMousePlugin; +import edu.uci.ics.jung.visualization.control.ShearingGraphMousePlugin; +import edu.uci.ics.jung.visualization.control.TranslatingGraphMousePlugin; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; @@ -18,7 +29,6 @@ import java.awt.event.ItemListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; - import javax.swing.ButtonGroup; import javax.swing.Icon; import javax.swing.JComboBox; @@ -26,239 +36,225 @@ import javax.swing.JRadioButtonMenuItem; import javax.swing.plaf.basic.BasicIconFactory; -import edu.uci.ics.jung.visualization.MultiLayerTransformer; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.control.AbstractModalGraphMouse; -import edu.uci.ics.jung.visualization.control.AnimatedPickingGraphMousePlugin; -import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; -import edu.uci.ics.jung.visualization.control.ModalGraphMouse; -import edu.uci.ics.jung.visualization.control.PickingGraphMousePlugin; -import edu.uci.ics.jung.visualization.control.RotatingGraphMousePlugin; -import edu.uci.ics.jung.visualization.control.ScalingGraphMousePlugin; -import edu.uci.ics.jung.visualization.control.ShearingGraphMousePlugin; -import edu.uci.ics.jung.visualization.control.TranslatingGraphMousePlugin; - /** * a graph mouse that supplies an annotations mode - * - * @author Tom Nelson - tomnelson@dev.java.net * + * @author Tom Nelson - tomnelson@dev.java.net * @param the vertex type * @param the edge type */ -public class AnnotatingModalGraphMouse extends AbstractModalGraphMouse - implements ModalGraphMouse, ItemSelectable { +public class AnnotatingModalGraphMouse extends AbstractModalGraphMouse + implements ModalGraphMouse, ItemSelectable { - protected AnnotatingGraphMousePlugin annotatingPlugin; - protected MultiLayerTransformer basicTransformer; - protected RenderContext rc; + protected AnnotatingGraphMousePlugin annotatingPlugin; + protected MultiLayerTransformer basicTransformer; + protected RenderContext rc; - /** - * Create an instance with default values for scale in (1.1) and scale out (1/1.1). - * - * @param rc the RenderContext for which this class will be used - * @param annotatingPlugin the plugin used by this class for annotating - */ - public AnnotatingModalGraphMouse(RenderContext rc, - AnnotatingGraphMousePlugin annotatingPlugin) { - this(rc, annotatingPlugin, 1.1f, 1/1.1f); - } + /** + * Create an instance with default values for scale in (1.1) and scale out (1/1.1). + * + * @param rc the RenderContext for which this class will be used + * @param annotatingPlugin the plugin used by this class for annotating + */ + public AnnotatingModalGraphMouse( + RenderContext rc, AnnotatingGraphMousePlugin annotatingPlugin) { + this(rc, annotatingPlugin, 1.1f, 1 / 1.1f); + } - /** - * Create an instance with the specified scale in and scale out values. - * - * @param rc the RenderContext for which this class will be used - * @param annotatingPlugin the plugin used by this class for annotating - * @param in override value for scale in - * @param out override value for scale out - */ - public AnnotatingModalGraphMouse(RenderContext rc, - AnnotatingGraphMousePlugin annotatingPlugin, - float in, float out) { - super(in,out); - this.rc = rc; - this.basicTransformer = rc.getMultiLayerTransformer(); - this.annotatingPlugin = annotatingPlugin; - loadPlugins(); - setModeKeyListener(new ModeKeyAdapter(this)); - } + /** + * Create an instance with the specified scale in and scale out values. + * + * @param rc the RenderContext for which this class will be used + * @param annotatingPlugin the plugin used by this class for annotating + * @param in override value for scale in + * @param out override value for scale out + */ + public AnnotatingModalGraphMouse( + RenderContext rc, + AnnotatingGraphMousePlugin annotatingPlugin, + float in, + float out) { + super(in, out); + this.rc = rc; + this.basicTransformer = rc.getMultiLayerTransformer(); + this.annotatingPlugin = annotatingPlugin; + loadPlugins(); + setModeKeyListener(new ModeKeyAdapter(this)); + } - /** - * create the plugins, and load the plugins for TRANSFORMING mode - * - */ - @Override - protected void loadPlugins() { - this.pickingPlugin = new PickingGraphMousePlugin(); - this.animatedPickingPlugin = new AnimatedPickingGraphMousePlugin(); - this.translatingPlugin = new TranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); - this.scalingPlugin = new ScalingGraphMousePlugin(new CrossoverScalingControl(), 0, in, out); - this.rotatingPlugin = new RotatingGraphMousePlugin(); - this.shearingPlugin = new ShearingGraphMousePlugin(); - add(scalingPlugin); - setMode(Mode.TRANSFORMING); - } + /** create the plugins, and load the plugins for TRANSFORMING mode */ + @Override + protected void loadPlugins() { + this.pickingPlugin = new PickingGraphMousePlugin(); + this.animatedPickingPlugin = new AnimatedPickingGraphMousePlugin(); + this.translatingPlugin = new TranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); + this.scalingPlugin = new ScalingGraphMousePlugin(new CrossoverScalingControl(), 0, in, out); + this.rotatingPlugin = new RotatingGraphMousePlugin(); + this.shearingPlugin = new ShearingGraphMousePlugin(); + add(scalingPlugin); + setMode(Mode.TRANSFORMING); + } - /** - * setter for the Mode. - */ - @Override - public void setMode(Mode mode) { - if(this.mode != mode) { - fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, - this.mode, ItemEvent.DESELECTED)); - this.mode = mode; - if(mode == Mode.TRANSFORMING) { - setTransformingMode(); - } else if(mode == Mode.PICKING) { - setPickingMode(); - } else if(mode == Mode.ANNOTATING) { - setAnnotatingMode(); - } - if(modeBox != null) { - modeBox.setSelectedItem(mode); - } - fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, mode, ItemEvent.SELECTED)); - } - } - - @Override - protected void setPickingMode() { - remove(translatingPlugin); - remove(rotatingPlugin); - remove(shearingPlugin); - remove(annotatingPlugin); - add(pickingPlugin); - add(animatedPickingPlugin); - } + /** setter for the Mode. */ + @Override + public void setMode(Mode mode) { + if (this.mode != mode) { + fireItemStateChanged( + new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, this.mode, ItemEvent.DESELECTED)); + this.mode = mode; + if (mode == Mode.TRANSFORMING) { + setTransformingMode(); + } else if (mode == Mode.PICKING) { + setPickingMode(); + } else if (mode == Mode.ANNOTATING) { + setAnnotatingMode(); + } + if (modeBox != null) { + modeBox.setSelectedItem(mode); + } + fireItemStateChanged( + new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, mode, ItemEvent.SELECTED)); + } + } + + @Override + protected void setPickingMode() { + remove(translatingPlugin); + remove(rotatingPlugin); + remove(shearingPlugin); + remove(annotatingPlugin); + add(pickingPlugin); + add(animatedPickingPlugin); + } - @Override - protected void setTransformingMode() { - remove(pickingPlugin); - remove(animatedPickingPlugin); - remove(annotatingPlugin); - add(translatingPlugin); - add(rotatingPlugin); - add(shearingPlugin); - } + @Override + protected void setTransformingMode() { + remove(pickingPlugin); + remove(animatedPickingPlugin); + remove(annotatingPlugin); + add(translatingPlugin); + add(rotatingPlugin); + add(shearingPlugin); + } - protected void setEditingMode() { - remove(pickingPlugin); - remove(animatedPickingPlugin); - remove(translatingPlugin); - remove(rotatingPlugin); - remove(shearingPlugin); - remove(annotatingPlugin); - } + protected void setEditingMode() { + remove(pickingPlugin); + remove(animatedPickingPlugin); + remove(translatingPlugin); + remove(rotatingPlugin); + remove(shearingPlugin); + remove(annotatingPlugin); + } - protected void setAnnotatingMode() { - remove(pickingPlugin); - remove(animatedPickingPlugin); - remove(translatingPlugin); - remove(rotatingPlugin); - remove(shearingPlugin); - add(annotatingPlugin); - } + protected void setAnnotatingMode() { + remove(pickingPlugin); + remove(animatedPickingPlugin); + remove(translatingPlugin); + remove(rotatingPlugin); + remove(shearingPlugin); + add(annotatingPlugin); + } + + /** @return Returns the modeBox. */ + @Override + public JComboBox getModeComboBox() { + if (modeBox == null) { + modeBox = new JComboBox(new Mode[] {Mode.TRANSFORMING, Mode.PICKING, Mode.ANNOTATING}); + modeBox.addItemListener(getModeListener()); + } + modeBox.setSelectedItem(mode); + return modeBox; + } + /** + * create (if necessary) and return a menu that will change the mode + * + * @return the menu + */ + @Override + public JMenu getModeMenu() { + if (modeMenu == null) { + modeMenu = new JMenu(); // { + Icon icon = BasicIconFactory.getMenuArrowIcon(); + modeMenu.setIcon(BasicIconFactory.getMenuArrowIcon()); + modeMenu.setPreferredSize(new Dimension(icon.getIconWidth() + 10, icon.getIconHeight() + 10)); - /** - * @return Returns the modeBox. - */ - @Override - public JComboBox getModeComboBox() { - if(modeBox == null) { - modeBox = new JComboBox( - new Mode[]{Mode.TRANSFORMING, Mode.PICKING, Mode.ANNOTATING}); - modeBox.addItemListener(getModeListener()); - } - modeBox.setSelectedItem(mode); - return modeBox; - } + final JRadioButtonMenuItem transformingButton = + new JRadioButtonMenuItem(Mode.TRANSFORMING.toString()); + transformingButton.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + setMode(Mode.TRANSFORMING); + } + } + }); - /** - * create (if necessary) and return a menu that will change - * the mode - * @return the menu - */ - @Override - public JMenu getModeMenu() { - if(modeMenu == null) { - modeMenu = new JMenu();// { - Icon icon = BasicIconFactory.getMenuArrowIcon(); - modeMenu.setIcon(BasicIconFactory.getMenuArrowIcon()); - modeMenu.setPreferredSize(new Dimension(icon.getIconWidth()+10, - icon.getIconHeight()+10)); + final JRadioButtonMenuItem pickingButton = new JRadioButtonMenuItem(Mode.PICKING.toString()); + pickingButton.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + setMode(Mode.PICKING); + } + } + }); - final JRadioButtonMenuItem transformingButton = - new JRadioButtonMenuItem(Mode.TRANSFORMING.toString()); - transformingButton.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - setMode(Mode.TRANSFORMING); - } - }}); + ButtonGroup radio = new ButtonGroup(); + radio.add(transformingButton); + radio.add(pickingButton); + transformingButton.setSelected(true); + modeMenu.add(transformingButton); + modeMenu.add(pickingButton); + modeMenu.setToolTipText("Menu for setting Mouse Mode"); + addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + if (e.getItem() == Mode.TRANSFORMING) { + transformingButton.setSelected(true); + } else if (e.getItem() == Mode.PICKING) { + pickingButton.setSelected(true); + } + } + } + }); + } + return modeMenu; + } - final JRadioButtonMenuItem pickingButton = - new JRadioButtonMenuItem(Mode.PICKING.toString()); - pickingButton.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - setMode(Mode.PICKING); - } - }}); + public static class ModeKeyAdapter extends KeyAdapter { + private char t = 't'; + private char p = 'p'; + private char a = 'a'; + protected ModalGraphMouse graphMouse; - ButtonGroup radio = new ButtonGroup(); - radio.add(transformingButton); - radio.add(pickingButton); - transformingButton.setSelected(true); - modeMenu.add(transformingButton); - modeMenu.add(pickingButton); - modeMenu.setToolTipText("Menu for setting Mouse Mode"); - addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - if(e.getItem() == Mode.TRANSFORMING) { - transformingButton.setSelected(true); - } else if(e.getItem() == Mode.PICKING) { - pickingButton.setSelected(true); - } - } - }}); - } - return modeMenu; - } - - public static class ModeKeyAdapter extends KeyAdapter { - private char t = 't'; - private char p = 'p'; - private char a = 'a'; - protected ModalGraphMouse graphMouse; + public ModeKeyAdapter(ModalGraphMouse graphMouse) { + this.graphMouse = graphMouse; + } - public ModeKeyAdapter(ModalGraphMouse graphMouse) { - this.graphMouse = graphMouse; - } + public ModeKeyAdapter(char t, char p, char a, ModalGraphMouse graphMouse) { + this.t = t; + this.p = p; + this.a = a; + this.graphMouse = graphMouse; + } - public ModeKeyAdapter(char t, char p, char a, ModalGraphMouse graphMouse) { - this.t = t; - this.p = p; - this.a = a; - this.graphMouse = graphMouse; - } - - @Override + @Override public void keyTyped(KeyEvent event) { - char keyChar = event.getKeyChar(); - if(keyChar == t) { - ((Component)event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - graphMouse.setMode(Mode.TRANSFORMING); - } else if(keyChar == p) { - ((Component)event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - graphMouse.setMode(Mode.PICKING); - } else if(keyChar == a) { - ((Component)event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); - graphMouse.setMode(Mode.ANNOTATING); - } - } + char keyChar = event.getKeyChar(); + if (keyChar == t) { + ((Component) event.getSource()) + .setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + graphMouse.setMode(Mode.TRANSFORMING); + } else if (keyChar == p) { + ((Component) event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + graphMouse.setMode(Mode.PICKING); + } else if (keyChar == a) { + ((Component) event.getSource()) + .setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); + graphMouse.setMode(Mode.ANNOTATING); + } } + } } - diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/Annotation.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/Annotation.java index 6a19f413..76423e35 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/Annotation.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/Annotation.java @@ -5,7 +5,7 @@ * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. * - * + * */ package edu.uci.ics.jung.visualization.annotations; @@ -14,86 +14,68 @@ /** * stores an annotation, either a shape or a string - * - * @author Tom Nelson - tomnelson@dev.java.net * + * @author Tom Nelson - tomnelson@dev.java.net * @param the type of the annotation object */ public class Annotation { - - protected T annotation; - protected Paint paint; - protected Point2D location; - protected Layer layer; - protected boolean fill; - public static enum Layer { LOWER, UPPER } - - public Annotation(T annotation, Layer layer, Paint paint, - boolean fill, Point2D location) { - this.annotation = annotation; - this.layer = layer; - this.paint = paint; - this.fill = fill; - this.location = location; - } - /** - * @return the annotation - */ - public T getAnnotation() { - return annotation; - } - /** - * @param annotation the annotation to set - */ - public void setAnnotation(T annotation) { - this.annotation = annotation; - } - /** - * @return the location - */ - public Point2D getLocation() { - return location; - } - /** - * @return the layer - */ - public Layer getLayer() { - return layer; - } - /** - * @param layer the layer to set - */ - public void setLayer(Layer layer) { - this.layer = layer; - } - /** - * @param location the location to set - */ - public void setLocation(Point2D location) { - this.location = location; - } - /** - * @return the paint - */ - public Paint getPaint() { - return paint; - } - /** - * @param paint the paint to set - */ - public void setPaint(Paint paint) { - this.paint = paint; - } - /** - * @return the fill - */ - public boolean isFill() { - return fill; - } - /** - * @param fill the fill to set - */ - public void setFill(boolean fill) { - this.fill = fill; - } + + protected T annotation; + protected Paint paint; + protected Point2D location; + protected Layer layer; + protected boolean fill; + + public static enum Layer { + LOWER, + UPPER + } + + public Annotation(T annotation, Layer layer, Paint paint, boolean fill, Point2D location) { + this.annotation = annotation; + this.layer = layer; + this.paint = paint; + this.fill = fill; + this.location = location; + } + /** @return the annotation */ + public T getAnnotation() { + return annotation; + } + /** @param annotation the annotation to set */ + public void setAnnotation(T annotation) { + this.annotation = annotation; + } + /** @return the location */ + public Point2D getLocation() { + return location; + } + /** @return the layer */ + public Layer getLayer() { + return layer; + } + /** @param layer the layer to set */ + public void setLayer(Layer layer) { + this.layer = layer; + } + /** @param location the location to set */ + public void setLocation(Point2D location) { + this.location = location; + } + /** @return the paint */ + public Paint getPaint() { + return paint; + } + /** @param paint the paint to set */ + public void setPaint(Paint paint) { + this.paint = paint; + } + /** @return the fill */ + public boolean isFill() { + return fill; + } + /** @param fill the fill to set */ + public void setFill(boolean fill) { + this.fill = fill; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationControls.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationControls.java index c4e34de8..e65af795 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationControls.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationControls.java @@ -5,7 +5,7 @@ * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. * - * + * */ package edu.uci.ics.jung.visualization.annotations; @@ -20,7 +20,6 @@ import java.awt.geom.Rectangle2D; import java.awt.geom.RectangularShape; import java.awt.geom.RoundRectangle2D; - import javax.swing.DefaultListCellRenderer; import javax.swing.JButton; import javax.swing.JColorChooser; @@ -30,101 +29,103 @@ import javax.swing.JToolBar; /** - * a collection of controls for annotations. - * allows selection of colors, shapes, etc - * @author Tom Nelson - tomnelson@dev.java.net + * a collection of controls for annotations. allows selection of colors, shapes, etc * + * @author Tom Nelson - tomnelson@dev.java.net */ -public class AnnotationControls { - - protected AnnotatingGraphMousePlugin annotatingPlugin; +public class AnnotationControls { + + protected AnnotatingGraphMousePlugin annotatingPlugin; + + public AnnotationControls(AnnotatingGraphMousePlugin annotatingPlugin) { + this.annotatingPlugin = annotatingPlugin; + } + + @SuppressWarnings("serial") + public JComboBox getShapeBox() { + JComboBox shapeBox = + new JComboBox( + new Shape[] { + new Rectangle2D.Double(), + new RoundRectangle2D.Double(0, 0, 0, 0, 50, 50), + new Ellipse2D.Double() + }); + shapeBox.setRenderer( + new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent( + JList list, Object value, int index, boolean isSelected, boolean hasFocus) { + String valueString = value.toString(); + valueString = valueString.substring(0, valueString.indexOf("2D")); + valueString = valueString.substring(valueString.lastIndexOf('.') + 1); + return super.getListCellRendererComponent( + list, valueString, index, isSelected, hasFocus); + } + }); + shapeBox.addItemListener( + new ItemListener() { - public AnnotationControls(AnnotatingGraphMousePlugin annotatingPlugin) { - this.annotatingPlugin = annotatingPlugin; - } - - @SuppressWarnings("serial") - public JComboBox getShapeBox() { - JComboBox shapeBox = new JComboBox( - new Shape[] { - new Rectangle2D.Double(), - new RoundRectangle2D.Double(0,0,0,0,50,50), - new Ellipse2D.Double() - }); - shapeBox.setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(JList list, Object value, - int index, boolean isSelected, boolean hasFocus) { - String valueString = value.toString(); - valueString = valueString.substring(0,valueString.indexOf("2D")); - valueString = valueString.substring(valueString.lastIndexOf('.')+1); - return super.getListCellRendererComponent(list, valueString, index, - isSelected, hasFocus); - } - }); - shapeBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + annotatingPlugin.setRectangularShape((RectangularShape) e.getItem()); + } + } + }); + return shapeBox; + } - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - annotatingPlugin.setRectangularShape((RectangularShape)e.getItem()); - } - - }}); - return shapeBox; - } - - public JButton getColorChooserButton() { - final JButton colorChooser = new JButton("Color"); - colorChooser.setForeground(annotatingPlugin.getAnnotationColor()); - colorChooser.addActionListener(new ActionListener() { + public JButton getColorChooserButton() { + final JButton colorChooser = new JButton("Color"); + colorChooser.setForeground(annotatingPlugin.getAnnotationColor()); + colorChooser.addActionListener( + new ActionListener() { - public void actionPerformed(ActionEvent e) { - Color color = JColorChooser.showDialog(colorChooser, "Annotation Color", - colorChooser.getForeground()); - annotatingPlugin.setAnnotationColor(color); - colorChooser.setForeground(color); - }}); - return colorChooser; - } - - public JComboBox getLayerBox() { - final JComboBox layerBox = new JComboBox( - new Annotation.Layer[] { - Annotation.Layer.LOWER, Annotation.Layer.UPPER - }); - layerBox.addItemListener(new ItemListener() { + public void actionPerformed(ActionEvent e) { + Color color = + JColorChooser.showDialog( + colorChooser, "Annotation Color", colorChooser.getForeground()); + annotatingPlugin.setAnnotationColor(color); + colorChooser.setForeground(color); + } + }); + return colorChooser; + } - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - annotatingPlugin.setLayer((Annotation.Layer)e.getItem()); - } - - }}); + public JComboBox getLayerBox() { + final JComboBox layerBox = + new JComboBox( + new Annotation.Layer[] {Annotation.Layer.LOWER, Annotation.Layer.UPPER}); + layerBox.addItemListener( + new ItemListener() { - return layerBox; - } + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + annotatingPlugin.setLayer((Annotation.Layer) e.getItem()); + } + } + }); - public JToggleButton getFillButton() { - JToggleButton fillButton = new JToggleButton("Fill"); - fillButton.addItemListener(new ItemListener() { + return layerBox; + } - public void itemStateChanged(ItemEvent e) { - annotatingPlugin.setFill(e.getStateChange() == ItemEvent.SELECTED); - - }}); - return fillButton; - } - - public JToolBar getAnnotationsToolBar() { - JToolBar toolBar = new JToolBar(); - toolBar.add(this.getShapeBox()); - toolBar.add(this.getColorChooserButton()); - toolBar.add(this.getFillButton()); - toolBar.add(this.getLayerBox()); - return toolBar; - - } + public JToggleButton getFillButton() { + JToggleButton fillButton = new JToggleButton("Fill"); + fillButton.addItemListener( + new ItemListener() { - + public void itemStateChanged(ItemEvent e) { + annotatingPlugin.setFill(e.getStateChange() == ItemEvent.SELECTED); + } + }); + return fillButton; + } + public JToolBar getAnnotationsToolBar() { + JToolBar toolBar = new JToolBar(); + toolBar.add(this.getShapeBox()); + toolBar.add(this.getColorChooserButton()); + toolBar.add(this.getFillButton()); + toolBar.add(this.getLayerBox()); + return toolBar; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationManager.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationManager.java index a6292bbb..ddfbfeb6 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationManager.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationManager.java @@ -5,10 +5,15 @@ * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. * - * + * */ package edu.uci.ics.jung.visualization.annotations; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.transform.AffineTransformer; +import edu.uci.ics.jung.visualization.transform.LensTransformer; +import edu.uci.ics.jung.visualization.transform.MutableTransformer; import java.awt.Component; import java.awt.Dimension; import java.awt.Shape; @@ -19,134 +24,131 @@ import java.util.HashSet; import java.util.Set; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.transform.AffineTransformer; -import edu.uci.ics.jung.visualization.transform.LensTransformer; -import edu.uci.ics.jung.visualization.transform.MutableTransformer; - /** - * handles the selection of annotations, and the support for the - * tools to draw them at specific layers. - * - * @author Tom Nelson - tomnelson@dev.java.net + * handles the selection of annotations, and the support for the tools to draw them at specific + * layers. * + * @author Tom Nelson - tomnelson@dev.java.net */ public class AnnotationManager { - - protected AnnotationRenderer annotationRenderer = new AnnotationRenderer(); - protected AnnotationPaintable lowerAnnotationPaintable; - protected AnnotationPaintable upperAnnotationPaintable; - - protected RenderContext rc; - protected AffineTransformer transformer; - - public AnnotationManager(RenderContext rc) { - this.rc = rc; - this.lowerAnnotationPaintable = new AnnotationPaintable(rc, annotationRenderer); - this.upperAnnotationPaintable = new AnnotationPaintable(rc, annotationRenderer); - - MutableTransformer mt = rc.getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - if(mt instanceof AffineTransformer) { - transformer = (AffineTransformer)mt; - } else if(mt instanceof LensTransformer) { - transformer = (AffineTransformer)((LensTransformer)mt).getDelegate(); - } - - } - - public AnnotationPaintable getAnnotationPaintable(Annotation.Layer layer) { - if(layer == Annotation.Layer.LOWER) { - return this.lowerAnnotationPaintable; - } - if(layer == Annotation.Layer.UPPER) { - return this.upperAnnotationPaintable; - } - return null; - } - - public void add(Annotation.Layer layer, Annotation annotation) { - if(layer == Annotation.Layer.LOWER) { - this.lowerAnnotationPaintable.add(annotation); - } - if(layer == Annotation.Layer.UPPER) { - this.upperAnnotationPaintable.add(annotation); - } - } - - public void remove(Annotation annotation) { - this.lowerAnnotationPaintable.remove(annotation); - this.upperAnnotationPaintable.remove(annotation); - } - - protected AnnotationPaintable getLowerAnnotationPaintable() { - return lowerAnnotationPaintable; - } - - protected AnnotationPaintable getUpperAnnotationPaintable() { - return upperAnnotationPaintable; - } - - public Annotation getAnnotation(Point2D p) { - @SuppressWarnings("rawtypes") - Set annotations = new HashSet(lowerAnnotationPaintable.getAnnotations()); - annotations.addAll(upperAnnotationPaintable.getAnnotations()); - return getAnnotation(p, annotations); - } - + + protected AnnotationRenderer annotationRenderer = new AnnotationRenderer(); + protected AnnotationPaintable lowerAnnotationPaintable; + protected AnnotationPaintable upperAnnotationPaintable; + + protected RenderContext rc; + protected AffineTransformer transformer; + + public AnnotationManager(RenderContext rc) { + this.rc = rc; + this.lowerAnnotationPaintable = new AnnotationPaintable(rc, annotationRenderer); + this.upperAnnotationPaintable = new AnnotationPaintable(rc, annotationRenderer); + + MutableTransformer mt = rc.getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + if (mt instanceof AffineTransformer) { + transformer = (AffineTransformer) mt; + } else if (mt instanceof LensTransformer) { + transformer = (AffineTransformer) ((LensTransformer) mt).getDelegate(); + } + } + + public AnnotationPaintable getAnnotationPaintable(Annotation.Layer layer) { + if (layer == Annotation.Layer.LOWER) { + return this.lowerAnnotationPaintable; + } + if (layer == Annotation.Layer.UPPER) { + return this.upperAnnotationPaintable; + } + return null; + } + + public void add(Annotation.Layer layer, Annotation annotation) { + if (layer == Annotation.Layer.LOWER) { + this.lowerAnnotationPaintable.add(annotation); + } + if (layer == Annotation.Layer.UPPER) { + this.upperAnnotationPaintable.add(annotation); + } + } + + public void remove(Annotation annotation) { + this.lowerAnnotationPaintable.remove(annotation); + this.upperAnnotationPaintable.remove(annotation); + } + + protected AnnotationPaintable getLowerAnnotationPaintable() { + return lowerAnnotationPaintable; + } + + protected AnnotationPaintable getUpperAnnotationPaintable() { + return upperAnnotationPaintable; + } + + public Annotation getAnnotation(Point2D p) { @SuppressWarnings("rawtypes") - public Annotation getAnnotation(Point2D p, Collection annotations) { - double closestDistance = Double.MAX_VALUE; - Annotation closestAnnotation = null; - for(Annotation annotation : annotations) { - Object ann = annotation.getAnnotation(); - if(ann instanceof Shape) { - Point2D ip = rc.getMultiLayerTransformer().inverseTransform(p); - Shape shape = (Shape)ann; - if(shape.contains(ip)) { - - Rectangle2D shapeBounds = shape.getBounds2D(); - Point2D shapeCenter = new Point2D.Double(shapeBounds.getCenterX(), shapeBounds.getCenterY()); - double distanceSq = shapeCenter.distanceSq(ip); - if(distanceSq < closestDistance) { - closestDistance = distanceSq; - closestAnnotation = annotation; - } - } - } else if(ann instanceof String) { - - Point2D ip = rc.getMultiLayerTransformer().inverseTransform(Layer.VIEW, p); - Point2D ap = annotation.getLocation(); - String label = (String)ann; - Component component = prepareRenderer(rc, annotationRenderer, label); - - AffineTransform base = new AffineTransform(transformer.getTransform()); - double rotation = transformer.getRotation(); - // unrotate the annotation - AffineTransform unrotate = - AffineTransform.getRotateInstance(-rotation, ap.getX(), ap.getY()); - base.concatenate(unrotate); - - Dimension d = component.getPreferredSize(); - Rectangle2D componentBounds = new Rectangle2D.Double(ap.getX(), ap.getY(), d.width, d.height); - - Shape componentBoundsShape = base.createTransformedShape(componentBounds); - Point2D componentCenter = new Point2D.Double(componentBoundsShape.getBounds().getCenterX(), - componentBoundsShape.getBounds().getCenterY()); - if(componentBoundsShape.contains(ip)) { - double distanceSq = componentCenter.distanceSq(ip); - if(distanceSq < closestDistance) { - closestDistance = distanceSq; - closestAnnotation = annotation; - } - } - - } - } - return closestAnnotation; - } - - public Component prepareRenderer(RenderContext rc, AnnotationRenderer annotationRenderer, Object value) { - return annotationRenderer.getAnnotationRendererComponent(rc.getScreenDevice(), value); - } + Set annotations = + new HashSet(lowerAnnotationPaintable.getAnnotations()); + annotations.addAll(upperAnnotationPaintable.getAnnotations()); + return getAnnotation(p, annotations); + } + + @SuppressWarnings("rawtypes") + public Annotation getAnnotation(Point2D p, Collection annotations) { + double closestDistance = Double.MAX_VALUE; + Annotation closestAnnotation = null; + for (Annotation annotation : annotations) { + Object ann = annotation.getAnnotation(); + if (ann instanceof Shape) { + Point2D ip = rc.getMultiLayerTransformer().inverseTransform(p); + Shape shape = (Shape) ann; + if (shape.contains(ip)) { + + Rectangle2D shapeBounds = shape.getBounds2D(); + Point2D shapeCenter = + new Point2D.Double(shapeBounds.getCenterX(), shapeBounds.getCenterY()); + double distanceSq = shapeCenter.distanceSq(ip); + if (distanceSq < closestDistance) { + closestDistance = distanceSq; + closestAnnotation = annotation; + } + } + } else if (ann instanceof String) { + + Point2D ip = rc.getMultiLayerTransformer().inverseTransform(Layer.VIEW, p); + Point2D ap = annotation.getLocation(); + String label = (String) ann; + Component component = prepareRenderer(rc, annotationRenderer, label); + + AffineTransform base = new AffineTransform(transformer.getTransform()); + double rotation = transformer.getRotation(); + // unrotate the annotation + AffineTransform unrotate = + AffineTransform.getRotateInstance(-rotation, ap.getX(), ap.getY()); + base.concatenate(unrotate); + + Dimension d = component.getPreferredSize(); + Rectangle2D componentBounds = + new Rectangle2D.Double(ap.getX(), ap.getY(), d.width, d.height); + + Shape componentBoundsShape = base.createTransformedShape(componentBounds); + Point2D componentCenter = + new Point2D.Double( + componentBoundsShape.getBounds().getCenterX(), + componentBoundsShape.getBounds().getCenterY()); + if (componentBoundsShape.contains(ip)) { + double distanceSq = componentCenter.distanceSq(ip); + if (distanceSq < closestDistance) { + closestDistance = distanceSq; + closestAnnotation = annotation; + } + } + } + } + return closestAnnotation; + } + + public Component prepareRenderer( + RenderContext rc, AnnotationRenderer annotationRenderer, Object value) { + return annotationRenderer.getAnnotationRendererComponent(rc.getScreenDevice(), value); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationPaintable.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationPaintable.java index 444bcc8a..51d7b504 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationPaintable.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationPaintable.java @@ -5,10 +5,16 @@ * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. * - * + * */ package edu.uci.ics.jung.visualization.annotations; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.VisualizationServer.Paintable; +import edu.uci.ics.jung.visualization.transform.AffineTransformer; +import edu.uci.ics.jung.visualization.transform.LensTransformer; +import edu.uci.ics.jung.visualization.transform.MutableTransformer; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; @@ -21,109 +27,106 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; - import javax.swing.JComponent; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.VisualizationServer.Paintable; -import edu.uci.ics.jung.visualization.transform.AffineTransformer; -import edu.uci.ics.jung.visualization.transform.LensTransformer; -import edu.uci.ics.jung.visualization.transform.MutableTransformer; - /** * handles the actual drawing of annotations - * - * @author Tom Nelson - tomnelson@dev.java.net * + * @author Tom Nelson - tomnelson@dev.java.net */ public class AnnotationPaintable implements Paintable { - - @SuppressWarnings("rawtypes") - protected Set annotations = new HashSet(); - protected AnnotationRenderer annotationRenderer; - protected RenderContext rc; - protected AffineTransformer transformer; - - public AnnotationPaintable(RenderContext rc, AnnotationRenderer annotationRenderer) { - this.rc = rc; - this.annotationRenderer = annotationRenderer; - MutableTransformer mt = rc.getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - if(mt instanceof AffineTransformer) { - transformer = (AffineTransformer)mt; - } else if(mt instanceof LensTransformer) { - transformer = (AffineTransformer)((LensTransformer)mt).getDelegate(); - } - } - - public void add(Annotation annotation) { - annotations.add(annotation); - } - - public void remove(Annotation annotation) { - annotations.remove(annotation); - } - - /** - * @return the annotations - */ - @SuppressWarnings("rawtypes") - public Set getAnnotations() { - return Collections.unmodifiableSet(annotations); - } + @SuppressWarnings("rawtypes") + protected Set annotations = new HashSet(); - public void paint(Graphics g) { - Graphics2D g2d = (Graphics2D)g; - Color oldColor = g.getColor(); - for(Annotation annotation : annotations) { - Object ann = annotation.getAnnotation(); - if(ann instanceof Shape) { - Shape shape = (Shape)ann; - Paint paint = annotation.getPaint(); - Shape s = transformer.transform(shape); - g2d.setPaint(paint); - if(annotation.isFill()) { - g2d.fill(s); - } else { - g2d.draw(s); - } - } else if(ann instanceof String) { - Point2D p = annotation.getLocation(); - String label = (String)ann; - Component component = prepareRenderer(rc, annotationRenderer, label); - component.setForeground((Color)annotation.getPaint()); - if(annotation.isFill()) { - ((JComponent)component).setOpaque(true); - component.setBackground((Color)annotation.getPaint()); - component.setForeground(Color.black); - } - Dimension d = component.getPreferredSize(); - AffineTransform old = g2d.getTransform(); - AffineTransform base = new AffineTransform(old); - AffineTransform xform = transformer.getTransform(); + protected AnnotationRenderer annotationRenderer; - double rotation = transformer.getRotation(); - // unrotate the annotation - AffineTransform unrotate = - AffineTransform.getRotateInstance(-rotation, p.getX(), p.getY()); - base.concatenate(xform); - base.concatenate(unrotate); - g2d.setTransform(base); - rc.getRendererPane().paintComponent(g, component, rc.getScreenDevice(), - (int)p.getX(), (int)p.getY(), - d.width, d.height, true); - g2d.setTransform(old); - } - } - g.setColor(oldColor); + protected RenderContext rc; + protected AffineTransformer transformer; + + public AnnotationPaintable(RenderContext rc, AnnotationRenderer annotationRenderer) { + this.rc = rc; + this.annotationRenderer = annotationRenderer; + MutableTransformer mt = rc.getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + if (mt instanceof AffineTransformer) { + transformer = (AffineTransformer) mt; + } else if (mt instanceof LensTransformer) { + transformer = (AffineTransformer) ((LensTransformer) mt).getDelegate(); } - - public Component prepareRenderer(RenderContext rc, AnnotationRenderer annotationRenderer, Object value) { - return annotationRenderer.getAnnotationRendererComponent(rc.getScreenDevice(), value); - } + } + + public void add(Annotation annotation) { + annotations.add(annotation); + } + + public void remove(Annotation annotation) { + annotations.remove(annotation); + } - public boolean useTransform() { - return true; + /** @return the annotations */ + @SuppressWarnings("rawtypes") + public Set getAnnotations() { + return Collections.unmodifiableSet(annotations); + } + + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + Color oldColor = g.getColor(); + for (Annotation annotation : annotations) { + Object ann = annotation.getAnnotation(); + if (ann instanceof Shape) { + Shape shape = (Shape) ann; + Paint paint = annotation.getPaint(); + Shape s = transformer.transform(shape); + g2d.setPaint(paint); + if (annotation.isFill()) { + g2d.fill(s); + } else { + g2d.draw(s); + } + } else if (ann instanceof String) { + Point2D p = annotation.getLocation(); + String label = (String) ann; + Component component = prepareRenderer(rc, annotationRenderer, label); + component.setForeground((Color) annotation.getPaint()); + if (annotation.isFill()) { + ((JComponent) component).setOpaque(true); + component.setBackground((Color) annotation.getPaint()); + component.setForeground(Color.black); + } + Dimension d = component.getPreferredSize(); + AffineTransform old = g2d.getTransform(); + AffineTransform base = new AffineTransform(old); + AffineTransform xform = transformer.getTransform(); + + double rotation = transformer.getRotation(); + // unrotate the annotation + AffineTransform unrotate = AffineTransform.getRotateInstance(-rotation, p.getX(), p.getY()); + base.concatenate(xform); + base.concatenate(unrotate); + g2d.setTransform(base); + rc.getRendererPane() + .paintComponent( + g, + component, + rc.getScreenDevice(), + (int) p.getX(), + (int) p.getY(), + d.width, + d.height, + true); + g2d.setTransform(old); + } } + g.setColor(oldColor); + } + + public Component prepareRenderer( + RenderContext rc, AnnotationRenderer annotationRenderer, Object value) { + return annotationRenderer.getAnnotationRendererComponent(rc.getScreenDevice(), value); + } + + public boolean useTransform() { + return true; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationRenderer.java index 57036009..27b9eb5e 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/annotations/AnnotationRenderer.java @@ -5,7 +5,7 @@ * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. * - * + * */ package edu.uci.ics.jung.visualization.annotations; @@ -14,179 +14,160 @@ import java.awt.Component; import java.awt.Rectangle; import java.io.Serializable; - import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; /** - * AnnotationRenderer is similar to the cell renderers - * used by the JTable and JTree JFC classes. - * - * @author Tom Nelson + * AnnotationRenderer is similar to the cell renderers used by the JTable and JTree JFC classes. * - * + * @author Tom Nelson */ @SuppressWarnings("serial") -public class AnnotationRenderer extends JLabel implements - Serializable { - - protected static Border noFocusBorder = new EmptyBorder(0,0,0,0); - - /** - * Creates a default table cell renderer. - */ - public AnnotationRenderer() { - setOpaque(true); - setBorder(noFocusBorder); - } +public class AnnotationRenderer extends JLabel implements Serializable { - /** - * Overrides JComponent.setForeground to assign - * the unselected-foreground color to the specified color. - * - * @param c set the foreground color to this value - */ - @Override - public void setForeground(Color c) { - super.setForeground(c); - } - - /** - * Overrides JComponent.setBackground to assign - * the unselected-background color to the specified color. - * - * @param c set the background color to this value - */ - @Override - public void setBackground(Color c) { - super.setBackground(c); - } + protected static Border noFocusBorder = new EmptyBorder(0, 0, 0, 0); - /** - * Notification from the UIManager that the look and feel - * has changed. - * Replaces the current UI object with the latest version from the - * UIManager. - * - * @see JComponent#updateUI - */ - @Override - public void updateUI() { - super.updateUI(); - setForeground(null); - setBackground(null); - } - - /** - * Returns the default label renderer. - * - * @param vv the VisualizationViewer to render on - * @param value the value to assign to the label - * @return the default label renderer - */ - public Component getAnnotationRendererComponent(JComponent vv, Object value) { - - super.setForeground(vv.getForeground()); - super.setBackground(vv.getBackground()); - - setFont(vv.getFont()); - setIcon(null); - setBorder(noFocusBorder); - setValue(value); - return this; - } - - /* - * The following methods are overridden as a performance measure to - * to prune code-paths are often called in the case of renders - * but which we know are unnecessary. Great care should be taken - * when writing your own renderer to weigh the benefits and - * drawbacks of overriding methods like these. - */ - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public boolean isOpaque() { - Color back = getBackground(); - Component p = getParent(); - if (p != null) { - p = p.getParent(); - } - boolean colorMatch = (back != null) && (p != null) && - back.equals(p.getBackground()) && - p.isOpaque(); - return !colorMatch && super.isOpaque(); - } + /** Creates a default table cell renderer. */ + public AnnotationRenderer() { + setOpaque(true); + setBorder(noFocusBorder); + } + + /** + * Overrides JComponent.setForeground to assign the unselected-foreground color to + * the specified color. + * + * @param c set the foreground color to this value + */ + @Override + public void setForeground(Color c) { + super.setForeground(c); + } + + /** + * Overrides JComponent.setBackground to assign the unselected-background color to + * the specified color. + * + * @param c set the background color to this value + */ + @Override + public void setBackground(Color c) { + super.setBackground(c); + } + + /** + * Notification from the UIManager that the look and feel has changed. Replaces the + * current UI object with the latest version from the UIManager. + * + * @see JComponent#updateUI + */ + @Override + public void updateUI() { + super.updateUI(); + setForeground(null); + setBackground(null); + } - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void validate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void revalidate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(long tm, int x, int y, int width, int height) {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(Rectangle r) { } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { - // Strings get interned... - if (propertyName=="text") { - super.firePropertyChange(propertyName, oldValue, newValue); - } + /** + * Returns the default label renderer. + * + * @param vv the VisualizationViewer to render on + * @param value the value to assign to the label + * @return the default label renderer + */ + public Component getAnnotationRendererComponent(JComponent vv, Object value) { + + super.setForeground(vv.getForeground()); + super.setBackground(vv.getBackground()); + + setFont(vv.getFont()); + setIcon(null); + setBorder(noFocusBorder); + setValue(value); + return this; + } + + /* + * The following methods are overridden as a performance measure to + * to prune code-paths are often called in the case of renders + * but which we know are unnecessary. Great care should be taken + * when writing your own renderer to weigh the benefits and + * drawbacks of overriding methods like these. + */ + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public boolean isOpaque() { + Color back = getBackground(); + Component p = getParent(); + if (p != null) { + p = p.getParent(); } + boolean colorMatch = + (back != null) && (p != null) && back.equals(p.getBackground()) && p.isOpaque(); + return !colorMatch && super.isOpaque(); + } + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void validate() {} - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } - - /** - * Sets the String object for the cell being rendered to - * value. - * - * @param value the string value for this cell; if value is - * null it sets the text value to an empty string - * @see JLabel#setText - * - */ - protected void setValue(Object value) { - setText((value == null) ? "" : value.toString()); + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void revalidate() {} + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void repaint(long tm, int x, int y, int width, int height) {} + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void repaint(Rectangle r) {} + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + // Strings get interned... + if (propertyName == "text") { + super.firePropertyChange(propertyName, oldValue, newValue); } + } + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {} + + /** + * Sets the String object for the cell being rendered to value. + * + * @param value the string value for this cell; if value is null it sets the text + * value to an empty string + * @see JLabel#setText + */ + protected void setValue(Object value) { + setText((value == null) ? "" : value.toString()); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbsoluteCrossoverScalingControl.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbsoluteCrossoverScalingControl.java index 157fbe06..e0c63f1a 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbsoluteCrossoverScalingControl.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbsoluteCrossoverScalingControl.java @@ -1,55 +1,52 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.visualization.control; -import java.awt.geom.Point2D; - import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.VisualizationServer; import edu.uci.ics.jung.visualization.transform.MutableTransformer; +import java.awt.geom.Point2D; /** - * Scales to the absolute value passed as an argument. - * It first resets the scaling Functions, then uses - * the relative CrossoverScalingControl to achieve the - * absolute value. - * - * @author Tom Nelson + * Scales to the absolute value passed as an argument. It first resets the scaling Functions, then + * uses the relative CrossoverScalingControl to achieve the absolute value. * + * @author Tom Nelson */ public class AbsoluteCrossoverScalingControl extends CrossoverScalingControl - implements ScalingControl { + implements ScalingControl { + + /** + * Scale to the absolute value passed as 'amount'. + * + * @param vv the VisualizationServer used for rendering; provides the layout and view + * transformers. + * @param amount the amount by which to scale + * @param at the point of reference for scaling + */ + public void scale(VisualizationServer vv, float amount, Point2D at) { + MutableTransformer layoutTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + MutableTransformer viewTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); + double modelScale = layoutTransformer.getScale(); + double viewScale = viewTransformer.getScale(); + double inverseModelScale = Math.sqrt(crossover) / modelScale; + double inverseViewScale = Math.sqrt(crossover) / viewScale; + + Point2D transformedAt = + vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, at); - /** - * Scale to the absolute value passed as 'amount'. - * - * @param vv the VisualizationServer used for rendering; provides the layout and view transformers. - * @param amount the amount by which to scale - * @param at the point of reference for scaling - */ - public void scale(VisualizationServer vv, float amount, Point2D at) { - MutableTransformer layoutTransformer - = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - MutableTransformer viewTransformer - = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); - double modelScale = layoutTransformer.getScale(); - double viewScale = viewTransformer.getScale(); - double inverseModelScale = Math.sqrt(crossover)/modelScale; - double inverseViewScale = Math.sqrt(crossover)/viewScale; - - Point2D transformedAt - = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, at); - - // return the Functions to 1.0 - layoutTransformer.scale(inverseModelScale, inverseModelScale, transformedAt); - viewTransformer.scale(inverseViewScale, inverseViewScale, at); + // return the Functions to 1.0 + layoutTransformer.scale(inverseModelScale, inverseModelScale, transformedAt); + viewTransformer.scale(inverseViewScale, inverseViewScale, at); - super.scale(vv, amount, at); - } + super.scale(vv, amount, at); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbstractGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbstractGraphMousePlugin.java index dfc80f86..77d79c56 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbstractGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbstractGraphMousePlugin.java @@ -15,70 +15,55 @@ import java.awt.event.MouseEvent; /** - * a base class for GraphMousePlugin instances. Holds some members - * common to all GraphMousePlugins - * @author thomasnelson + * a base class for GraphMousePlugin instances. Holds some members common to all GraphMousePlugins * + * @author thomasnelson */ public abstract class AbstractGraphMousePlugin implements GraphMousePlugin { - /** - * modifiers to compare against mouse event modifiers - */ - protected int modifiers; - - /** - * the location in the View where the mouse was pressed - */ - protected Point down; - - /** - * the special cursor that plugins may display - */ - protected Cursor cursor; - - /** - * Creates an instance with the specified mouse event modifiers. - * @param modifiers the mouse event modifiers to use - */ - public AbstractGraphMousePlugin(int modifiers) { - this.modifiers = modifiers; - } - - /** - * getter for mouse modifiers - */ - public int getModifiers() { - return modifiers; - } + /** modifiers to compare against mouse event modifiers */ + protected int modifiers; + + /** the location in the View where the mouse was pressed */ + protected Point down; + + /** the special cursor that plugins may display */ + protected Cursor cursor; + + /** + * Creates an instance with the specified mouse event modifiers. + * + * @param modifiers the mouse event modifiers to use + */ + public AbstractGraphMousePlugin(int modifiers) { + this.modifiers = modifiers; + } + + /** getter for mouse modifiers */ + public int getModifiers() { + return modifiers; + } + + /** setter for mouse modifiers */ + public void setModifiers(int modifiers) { + this.modifiers = modifiers; + } - /** - * setter for mouse modifiers - */ - public void setModifiers(int modifiers) { - this.modifiers = modifiers; - } - - /** - * check the mouse event modifiers against the - * instance member modifiers. Default implementation - * checks equality. Can be overridden to test with a mask - */ - public boolean checkModifiers(MouseEvent e) { - return e.getModifiers() == modifiers; - } + /** + * check the mouse event modifiers against the instance member modifiers. Default implementation + * checks equality. Can be overridden to test with a mask + */ + public boolean checkModifiers(MouseEvent e) { + return e.getModifiers() == modifiers; + } - /** - * @return Returns the cursor. - */ - public Cursor getCursor() { - return cursor; - } + /** @return Returns the cursor. */ + public Cursor getCursor() { + return cursor; + } - /** - * @param cursor The cursor to set. - */ - public void setCursor(Cursor cursor) { - this.cursor = cursor; - } + /** @param cursor The cursor to set. */ + public void setCursor(Cursor cursor) { + this.cursor = cursor; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbstractModalGraphMouse.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbstractModalGraphMouse.java index cb5faa71..de8dbd55 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbstractModalGraphMouse.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbstractModalGraphMouse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -16,7 +16,6 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyListener; - import javax.swing.ButtonGroup; import javax.swing.Icon; import javax.swing.JComboBox; @@ -25,281 +24,242 @@ import javax.swing.event.EventListenerList; import javax.swing.plaf.basic.BasicIconFactory; - -/** - * - * AbstractModalGraphMouse is a PluggableGraphMouse class that - * manages a collection of plugins for picking and - * transforming the graph. Additionally, it carries the notion - * of a Mode: Picking or Translating. Switching between modes - * allows for a more natural choice of mouse modifiers to - * be used for the various plugins. The default modifiers are - * intended to mimick those of mainstream software applications - * in order to be intuitive to users. - * - * To change between modes, two different controls are offered, - * a combo box and a menu system. These controls are lazily created - * in their respective 'getter' methods so they don't impact - * code that does not intend to use them. - * The menu control can be placed in an unused corner of the - * GraphZoomScrollPane, which is a common location for mouse - * mode selection menus in mainstream applications. - * - * Users must implement the loadPlugins() method to create and - * install the GraphMousePlugins. The order of the plugins is - * important, as they are evaluated against the mask parameters - * in the order that they are added. - * +/** + * AbstractModalGraphMouse is a PluggableGraphMouse class that manages a collection of plugins for + * picking and transforming the graph. Additionally, it carries the notion of a Mode: Picking or + * Translating. Switching between modes allows for a more natural choice of mouse modifiers to be + * used for the various plugins. The default modifiers are intended to mimick those of mainstream + * software applications in order to be intuitive to users. + * + *

            To change between modes, two different controls are offered, a combo box and a menu system. + * These controls are lazily created in their respective 'getter' methods so they don't impact code + * that does not intend to use them. The menu control can be placed in an unused corner of the + * GraphZoomScrollPane, which is a common location for mouse mode selection menus in mainstream + * applications. + * + *

            Users must implement the loadPlugins() method to create and install the GraphMousePlugins. The + * order of the plugins is important, as they are evaluated against the mask parameters in the order + * that they are added. + * * @author Tom Nelson */ -public abstract class AbstractModalGraphMouse extends PluggableGraphMouse +public abstract class AbstractModalGraphMouse extends PluggableGraphMouse implements ModalGraphMouse, ItemSelectable { - - /** - * used by the scaling plugins for zoom in - */ - protected float in; - /** - * used by the scaling plugins for zoom out - */ - protected float out; - /** - * a listener for mode changes - */ - protected ItemListener modeListener; - /** - * a JComboBox control available to set the mode - */ - protected JComboBox modeBox; - /** - * a menu available to set the mode - */ - protected JMenu modeMenu; - /** - * the current mode - */ - protected Mode mode; - /** - * listeners for mode changes - */ - protected EventListenerList listenerList = new EventListenerList(); - - protected GraphMousePlugin pickingPlugin; - protected GraphMousePlugin translatingPlugin; - protected GraphMousePlugin animatedPickingPlugin; - protected GraphMousePlugin scalingPlugin; - protected GraphMousePlugin rotatingPlugin; - protected GraphMousePlugin shearingPlugin; - protected KeyListener modeKeyListener; - - protected AbstractModalGraphMouse(float in, float out) { - this.in = in; - this.out = out; - } + /** used by the scaling plugins for zoom in */ + protected float in; + /** used by the scaling plugins for zoom out */ + protected float out; + /** a listener for mode changes */ + protected ItemListener modeListener; + /** a JComboBox control available to set the mode */ + protected JComboBox modeBox; + /** a menu available to set the mode */ + protected JMenu modeMenu; + /** the current mode */ + protected Mode mode; + /** listeners for mode changes */ + protected EventListenerList listenerList = new EventListenerList(); - /** - * create the plugins, and load the plugins for TRANSFORMING mode - * - */ - protected abstract void loadPlugins(); - - /** - * setter for the Mode. - */ - public void setMode(Mode mode) { - if(this.mode != mode) { - fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, - this.mode, ItemEvent.DESELECTED)); - this.mode = mode; - if(mode == Mode.TRANSFORMING) { - setTransformingMode(); - } else if(mode == Mode.PICKING) { - setPickingMode(); - } - if(modeBox != null) { - modeBox.setSelectedItem(mode); - } - fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, mode, ItemEvent.SELECTED)); - } - } - /* (non-Javadoc) - * @see edu.uci.ics.jung.visualization.control.ModalGraphMouse#setPickingMode() - */ - protected void setPickingMode() { - remove(translatingPlugin); - remove(rotatingPlugin); - remove(shearingPlugin); - add(pickingPlugin); - add(animatedPickingPlugin); - } - - /* (non-Javadoc) - * @see edu.uci.ics.jung.visualization.control.ModalGraphMouse#setTransformingMode() - */ - protected void setTransformingMode() { - remove(pickingPlugin); - remove(animatedPickingPlugin); - add(translatingPlugin); - add(rotatingPlugin); - add(shearingPlugin); - } - - /** - * @param zoomAtMouse The zoomAtMouse to set. - */ - public void setZoomAtMouse(boolean zoomAtMouse) { - ((ScalingGraphMousePlugin) scalingPlugin).setZoomAtMouse(zoomAtMouse); - } - - /** - * listener to set the mode from an external event source - */ - class ModeListener implements ItemListener { - public void itemStateChanged(ItemEvent e) { - setMode((Mode) e.getItem()); - } - } + protected GraphMousePlugin pickingPlugin; + protected GraphMousePlugin translatingPlugin; + protected GraphMousePlugin animatedPickingPlugin; + protected GraphMousePlugin scalingPlugin; + protected GraphMousePlugin rotatingPlugin; + protected GraphMousePlugin shearingPlugin; + protected KeyListener modeKeyListener; - /* (non-Javadoc) - * @see edu.uci.ics.jung.visualization.control.ModalGraphMouse#getModeListener() - */ - public ItemListener getModeListener() { - if (modeListener == null) { - modeListener = new ModeListener(); - } - return modeListener; - } - - /** - * @return the modeKeyListener - */ - public KeyListener getModeKeyListener() { - return modeKeyListener; - } + protected AbstractModalGraphMouse(float in, float out) { + this.in = in; + this.out = out; + } - /** - * @param modeKeyListener the modeKeyListener to set - */ - public void setModeKeyListener(KeyListener modeKeyListener) { - this.modeKeyListener = modeKeyListener; - } + /** create the plugins, and load the plugins for TRANSFORMING mode */ + protected abstract void loadPlugins(); - /** - * @return Returns the modeBox. - */ - public JComboBox getModeComboBox() { - if(modeBox == null) { - modeBox = new JComboBox(new Mode[]{Mode.TRANSFORMING, Mode.PICKING}); - modeBox.addItemListener(getModeListener()); - } + /** setter for the Mode. */ + public void setMode(Mode mode) { + if (this.mode != mode) { + fireItemStateChanged( + new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, this.mode, ItemEvent.DESELECTED)); + this.mode = mode; + if (mode == Mode.TRANSFORMING) { + setTransformingMode(); + } else if (mode == Mode.PICKING) { + setPickingMode(); + } + if (modeBox != null) { modeBox.setSelectedItem(mode); - return modeBox; + } + fireItemStateChanged( + new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, mode, ItemEvent.SELECTED)); } - - /** - * create (if necessary) and return a menu that will change - * the mode - * @return the menu - */ - public JMenu getModeMenu() { - if(modeMenu == null) { - modeMenu = new JMenu();// { - Icon icon = BasicIconFactory.getMenuArrowIcon(); - modeMenu.setIcon(BasicIconFactory.getMenuArrowIcon()); - modeMenu.setPreferredSize(new Dimension(icon.getIconWidth()+10, - icon.getIconHeight()+10)); + } + /* (non-Javadoc) + * @see edu.uci.ics.jung.visualization.control.ModalGraphMouse#setPickingMode() + */ + protected void setPickingMode() { + remove(translatingPlugin); + remove(rotatingPlugin); + remove(shearingPlugin); + add(pickingPlugin); + add(animatedPickingPlugin); + } + + /* (non-Javadoc) + * @see edu.uci.ics.jung.visualization.control.ModalGraphMouse#setTransformingMode() + */ + protected void setTransformingMode() { + remove(pickingPlugin); + remove(animatedPickingPlugin); + add(translatingPlugin); + add(rotatingPlugin); + add(shearingPlugin); + } - final JRadioButtonMenuItem transformingButton = - new JRadioButtonMenuItem(Mode.TRANSFORMING.toString()); - transformingButton.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - setMode(Mode.TRANSFORMING); - } - }}); - - final JRadioButtonMenuItem pickingButton = - new JRadioButtonMenuItem(Mode.PICKING.toString()); - pickingButton.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - setMode(Mode.PICKING); - } - }}); - ButtonGroup radio = new ButtonGroup(); - radio.add(transformingButton); - radio.add(pickingButton); - transformingButton.setSelected(true); - modeMenu.add(transformingButton); - modeMenu.add(pickingButton); - modeMenu.setToolTipText("Menu for setting Mouse Mode"); - addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - if(e.getItem() == Mode.TRANSFORMING) { - transformingButton.setSelected(true); - } else if(e.getItem() == Mode.PICKING) { - pickingButton.setSelected(true); - } - } - }}); - } - return modeMenu; + /** @param zoomAtMouse The zoomAtMouse to set. */ + public void setZoomAtMouse(boolean zoomAtMouse) { + ((ScalingGraphMousePlugin) scalingPlugin).setZoomAtMouse(zoomAtMouse); + } + + /** listener to set the mode from an external event source */ + class ModeListener implements ItemListener { + public void itemStateChanged(ItemEvent e) { + setMode((Mode) e.getItem()); } - - /** - * add a listener for mode changes - */ - public void addItemListener(ItemListener aListener) { - listenerList.add(ItemListener.class,aListener); + } + + /* (non-Javadoc) + * @see edu.uci.ics.jung.visualization.control.ModalGraphMouse#getModeListener() + */ + public ItemListener getModeListener() { + if (modeListener == null) { + modeListener = new ModeListener(); } + return modeListener; + } + + /** @return the modeKeyListener */ + public KeyListener getModeKeyListener() { + return modeKeyListener; + } + + /** @param modeKeyListener the modeKeyListener to set */ + public void setModeKeyListener(KeyListener modeKeyListener) { + this.modeKeyListener = modeKeyListener; + } - /** - * remove a listener for mode changes - */ - public void removeItemListener(ItemListener aListener) { - listenerList.remove(ItemListener.class,aListener); + /** @return Returns the modeBox. */ + public JComboBox getModeComboBox() { + if (modeBox == null) { + modeBox = new JComboBox(new Mode[] {Mode.TRANSFORMING, Mode.PICKING}); + modeBox.addItemListener(getModeListener()); } + modeBox.setSelectedItem(mode); + return modeBox; + } + + /** + * create (if necessary) and return a menu that will change the mode + * + * @return the menu + */ + public JMenu getModeMenu() { + if (modeMenu == null) { + modeMenu = new JMenu(); // { + Icon icon = BasicIconFactory.getMenuArrowIcon(); + modeMenu.setIcon(BasicIconFactory.getMenuArrowIcon()); + modeMenu.setPreferredSize(new Dimension(icon.getIconWidth() + 10, icon.getIconHeight() + 10)); + + final JRadioButtonMenuItem transformingButton = + new JRadioButtonMenuItem(Mode.TRANSFORMING.toString()); + transformingButton.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + setMode(Mode.TRANSFORMING); + } + } + }); - /** - * Returns an array of all the ItemListeners added - * to this JComboBox with addItemListener(). - * - * @return all of the ItemListeners added or an empty - * array if no listeners have been added - * @since 1.4 - */ - public ItemListener[] getItemListeners() { - return listenerList.getListeners(ItemListener.class); + final JRadioButtonMenuItem pickingButton = new JRadioButtonMenuItem(Mode.PICKING.toString()); + pickingButton.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + setMode(Mode.PICKING); + } + } + }); + ButtonGroup radio = new ButtonGroup(); + radio.add(transformingButton); + radio.add(pickingButton); + transformingButton.setSelected(true); + modeMenu.add(transformingButton); + modeMenu.add(pickingButton); + modeMenu.setToolTipText("Menu for setting Mouse Mode"); + addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + if (e.getItem() == Mode.TRANSFORMING) { + transformingButton.setSelected(true); + } else if (e.getItem() == Mode.PICKING) { + pickingButton.setSelected(true); + } + } + } + }); } - - public Object[] getSelectedObjects() { - if ( mode == null ) - return new Object[0]; - else { - Object result[] = new Object[1]; - result[0] = mode; - return result; - } + return modeMenu; + } + + /** add a listener for mode changes */ + public void addItemListener(ItemListener aListener) { + listenerList.add(ItemListener.class, aListener); + } + + /** remove a listener for mode changes */ + public void removeItemListener(ItemListener aListener) { + listenerList.remove(ItemListener.class, aListener); + } + + /** + * Returns an array of all the ItemListeners added to this JComboBox with + * addItemListener(). + * + * @return all of the ItemListeners added or an empty array if no listeners have been + * added + * @since 1.4 + */ + public ItemListener[] getItemListeners() { + return listenerList.getListeners(ItemListener.class); + } + + public Object[] getSelectedObjects() { + if (mode == null) return new Object[0]; + else { + Object result[] = new Object[1]; + result[0] = mode; + return result; } + } - /** - * Notifies all listeners that have registered interest for - * notification on this event type. - * @param e the event of interest - * - * @see EventListenerList - */ - protected void fireItemStateChanged(ItemEvent e) { - // Guaranteed to return a non-null array - Object[] listeners = listenerList.getListenerList(); - // Process the listeners last to first, notifying - // those that are interested in this event - for ( int i = listeners.length-2; i>=0; i-=2 ) { - if ( listeners[i]==ItemListener.class ) { - ((ItemListener)listeners[i+1]).itemStateChanged(e); - } - } + /** + * Notifies all listeners that have registered interest for notification on this event type. + * + * @param e the event of interest + * @see EventListenerList + */ + protected void fireItemStateChanged(ItemEvent e) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ItemListener.class) { + ((ItemListener) listeners[i + 1]).itemStateChanged(e); + } } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbstractPopupGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbstractPopupGraphMousePlugin.java index 7a5cc1bd..a4669e6b 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbstractPopupGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AbstractPopupGraphMousePlugin.java @@ -1,51 +1,47 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.visualization.control; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; -public abstract class AbstractPopupGraphMousePlugin extends AbstractGraphMousePlugin +public abstract class AbstractPopupGraphMousePlugin extends AbstractGraphMousePlugin implements MouseListener { - - public AbstractPopupGraphMousePlugin() { - this(MouseEvent.BUTTON3_MASK); - } - public AbstractPopupGraphMousePlugin(int modifiers) { - super(modifiers); - } - public void mousePressed(MouseEvent e) { - if(e.isPopupTrigger()) { - handlePopup(e); - e.consume(); - } - } - - /** - * if this is the popup trigger, process here, otherwise - * defer to the superclass - */ - public void mouseReleased(MouseEvent e) { - if(e.isPopupTrigger()) { - handlePopup(e); - e.consume(); - } - } - - protected abstract void handlePopup(MouseEvent e); - - public void mouseClicked(MouseEvent e) { - } - - public void mouseEntered(MouseEvent e) { + + public AbstractPopupGraphMousePlugin() { + this(MouseEvent.BUTTON3_MASK); + } + + public AbstractPopupGraphMousePlugin(int modifiers) { + super(modifiers); + } + + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + handlePopup(e); + e.consume(); } - - public void mouseExited(MouseEvent e) { + } + + /** if this is the popup trigger, process here, otherwise defer to the superclass */ + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + handlePopup(e); + e.consume(); } + } + + protected abstract void handlePopup(MouseEvent e); + + public void mouseClicked(MouseEvent e) {} + + public void mouseEntered(MouseEvent e) {} + + public void mouseExited(MouseEvent e) {} } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AnimatedPickingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AnimatedPickingGraphMousePlugin.java index 94388c53..aa61742b 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AnimatedPickingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/AnimatedPickingGraphMousePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,144 +11,134 @@ */ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.picking.PickedState; import java.awt.Cursor; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.Point2D; - import javax.swing.JComponent; -import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.picking.PickedState; - -/** - * AnimatedPickingGraphMousePlugin supports the picking of one Graph - * Vertex. When the mouse is released, the graph is translated so that - * the picked Vertex is moved to the center of the view. This translation - * is conducted in an animation Thread so that the graph slides to its - * new position - * +/** + * AnimatedPickingGraphMousePlugin supports the picking of one Graph Vertex. When the mouse is + * released, the graph is translated so that the picked Vertex is moved to the center of the view. + * This translation is conducted in an animation Thread so that the graph slides to its new position + * * @author Tom Nelson */ public class AnimatedPickingGraphMousePlugin extends AbstractGraphMousePlugin implements MouseListener, MouseMotionListener { - /** - * the picked Vertex - */ - protected V vertex; - - /** - * Creates an instance with default modifiers of BUTTON1_MASK and CTRL_MASK - */ - public AnimatedPickingGraphMousePlugin() { - this(InputEvent.BUTTON1_MASK | InputEvent.CTRL_MASK); - } + /** the picked Vertex */ + protected V vertex; - /** - * Creates an instance with the specified mouse event modifiers. - * @param selectionModifiers the mouse event modifiers to use. - */ - public AnimatedPickingGraphMousePlugin(int selectionModifiers) { - super(selectionModifiers); - this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); - } + /** Creates an instance with default modifiers of BUTTON1_MASK and CTRL_MASK */ + public AnimatedPickingGraphMousePlugin() { + this(InputEvent.BUTTON1_MASK | InputEvent.CTRL_MASK); + } - /** - * If the event occurs on a Vertex, pick that single Vertex - * @param e the event - */ - @SuppressWarnings("unchecked") - public void mousePressed(MouseEvent e) { - if (e.getModifiers() == modifiers) { - VisualizationViewer vv = (VisualizationViewer) e.getSource(); - NetworkElementAccessor pickSupport = vv.getPickSupport(); - PickedState pickedVertexState = vv.getPickedVertexState(); - if (pickSupport != null && pickedVertexState != null) { - // p is the screen point for the mouse event - Point2D p = e.getPoint(); - vertex = pickSupport.getNode(p.getX(), p.getY()); - if (vertex != null) { - if (pickedVertexState.isPicked(vertex) == false) { - pickedVertexState.clear(); - pickedVertexState.pick(vertex, true); - } - } - } - e.consume(); - } - } + /** + * Creates an instance with the specified mouse event modifiers. + * + * @param selectionModifiers the mouse event modifiers to use. + */ + public AnimatedPickingGraphMousePlugin(int selectionModifiers) { + super(selectionModifiers); + this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); + } + /** + * If the event occurs on a Vertex, pick that single Vertex + * + * @param e the event + */ + @SuppressWarnings("unchecked") + public void mousePressed(MouseEvent e) { + if (e.getModifiers() == modifiers) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + NetworkElementAccessor pickSupport = vv.getPickSupport(); + PickedState pickedVertexState = vv.getPickedVertexState(); + if (pickSupport != null && pickedVertexState != null) { + // p is the screen point for the mouse event + Point2D p = e.getPoint(); + vertex = pickSupport.getNode(p.getX(), p.getY()); + if (vertex != null) { + if (pickedVertexState.isPicked(vertex) == false) { + pickedVertexState.clear(); + pickedVertexState.pick(vertex, true); + } + } + } + e.consume(); + } + } -/** - * If a Vertex was picked in the mousePressed event, start a Thread - * to animate the translation of the graph so that the picked Vertex - * moves to the center of the view - * - * @param e the event - */ - @SuppressWarnings("unchecked") - public void mouseReleased(MouseEvent e) { - if (e.getModifiers() == modifiers) { - final VisualizationViewer vv = (VisualizationViewer) e.getSource(); - Point2D newCenter = null; - if (vertex != null) { - // center the picked vertex - Layout layout = vv.getGraphLayout(); - newCenter = layout.apply(vertex); - } else { - // they did not pick a vertex to center, so - // just center the graph - newCenter = vv.getCenter(); - } - Point2D lvc = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(vv.getCenter()); - final double dx = (lvc.getX() - newCenter.getX()) / 10; - final double dy = (lvc.getY() - newCenter.getY()) / 10; + /** + * If a Vertex was picked in the mousePressed event, start a Thread to animate the translation of + * the graph so that the picked Vertex moves to the center of the view + * + * @param e the event + */ + @SuppressWarnings("unchecked") + public void mouseReleased(MouseEvent e) { + if (e.getModifiers() == modifiers) { + final VisualizationViewer vv = (VisualizationViewer) e.getSource(); + Point2D newCenter = null; + if (vertex != null) { + // center the picked vertex + Layout layout = vv.getGraphLayout(); + newCenter = layout.apply(vertex); + } else { + // they did not pick a vertex to center, so + // just center the graph + newCenter = vv.getCenter(); + } + Point2D lvc = + vv.getRenderContext().getMultiLayerTransformer().inverseTransform(vv.getCenter()); + final double dx = (lvc.getX() - newCenter.getX()) / 10; + final double dy = (lvc.getY() - newCenter.getY()) / 10; - Runnable animator = new Runnable() { + Runnable animator = + new Runnable() { - public void run() { - for (int i = 0; i < 10; i++) { - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).translate(dx, dy); - try { - Thread.sleep(100); - } catch (InterruptedException ex) { - } - } - } - }; - Thread thread = new Thread(animator); - thread.start(); - } - } - - public void mouseClicked(MouseEvent e) { + public void run() { + for (int i = 0; i < 10; i++) { + vv.getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .translate(dx, dy); + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + } + } + } + }; + Thread thread = new Thread(animator); + thread.start(); } + } - /** - * show a special cursor while the mouse is inside the window - */ - public void mouseEntered(MouseEvent e) { - JComponent c = (JComponent)e.getSource(); - c.setCursor(cursor); - } + public void mouseClicked(MouseEvent e) {} - /** - * revert to the default cursor when the mouse leaves this window - */ - public void mouseExited(MouseEvent e) { - JComponent c = (JComponent)e.getSource(); - c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } + /** show a special cursor while the mouse is inside the window */ + public void mouseEntered(MouseEvent e) { + JComponent c = (JComponent) e.getSource(); + c.setCursor(cursor); + } - public void mouseMoved(MouseEvent e) { - } + /** revert to the default cursor when the mouse leaves this window */ + public void mouseExited(MouseEvent e) { + JComponent c = (JComponent) e.getSource(); + c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + + public void mouseMoved(MouseEvent e) {} - public void mouseDragged(MouseEvent arg0) { - } + public void mouseDragged(MouseEvent arg0) {} } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/CrossoverScalingControl.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/CrossoverScalingControl.java index 8a58f6e8..e6ae0794 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/CrossoverScalingControl.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/CrossoverScalingControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,71 +11,67 @@ */ package edu.uci.ics.jung.visualization.control; -import java.awt.geom.Point2D; - import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.VisualizationServer; import edu.uci.ics.jung.visualization.transform.MutableTransformer; +import java.awt.geom.Point2D; -/** - * A scaling control that has a crossover point. - * When the overall scale of the view and - * model is less than the crossover point, the scaling is applied - * to the view's transform and the graph nodes, labels, etc grow - * smaller. This preserves the overall shape of the graph. - * When the scale is larger than the crossover, the scaling is - * applied to the graph layout. The graph spreads out, but the - * vertices and labels grow no larger than their original size. - * +/** + * A scaling control that has a crossover point. When the overall scale of the view and model is + * less than the crossover point, the scaling is applied to the view's transform and the graph + * nodes, labels, etc grow smaller. This preserves the overall shape of the graph. When the scale is + * larger than the crossover, the scaling is applied to the graph layout. The graph spreads out, but + * the vertices and labels grow no larger than their original size. + * * @author Tom Nelson */ public class CrossoverScalingControl implements ScalingControl { - /** - * Point where scale crosses over from view to layout. - */ - protected double crossover = 1.0; - - /** - * Sets the crossover point to the specified value. - * @param crossover the crossover point to use (defaults to 1.0) - */ - public void setCrossover(double crossover) { - this.crossover = crossover; - } + /** Point where scale crosses over from view to layout. */ + protected double crossover = 1.0; + + /** + * Sets the crossover point to the specified value. + * + * @param crossover the crossover point to use (defaults to 1.0) + */ + public void setCrossover(double crossover) { + this.crossover = crossover; + } + + /** @return the current crossover value */ + public double getCrossover() { + return crossover; + } + + public void scale(VisualizationServer vv, float amount, Point2D at) { + + MutableTransformer layoutTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + MutableTransformer viewTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); + double modelScale = layoutTransformer.getScale(); + double viewScale = viewTransformer.getScale(); + double inverseModelScale = Math.sqrt(crossover) / modelScale; + double inverseViewScale = Math.sqrt(crossover) / viewScale; + double scale = modelScale * viewScale; + + Point2D transformedAt = + vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, at); - /** - * @return the current crossover value - */ - public double getCrossover() { - return crossover; + if ((scale * amount - crossover) * (scale * amount - crossover) < 0.001) { + // close to the control point, return both Functions to a scale of sqrt crossover value + layoutTransformer.scale(inverseModelScale, inverseModelScale, transformedAt); + viewTransformer.scale(inverseViewScale, inverseViewScale, at); + } else if (scale * amount < crossover) { + // scale the viewTransformer, return the layoutTransformer to sqrt crossover value + viewTransformer.scale(amount, amount, at); + layoutTransformer.scale(inverseModelScale, inverseModelScale, transformedAt); + } else { + // scale the layoutTransformer, return the viewTransformer to crossover value + layoutTransformer.scale(amount, amount, transformedAt); + viewTransformer.scale(inverseViewScale, inverseViewScale, at); } - - public void scale(VisualizationServer vv, float amount, Point2D at) { - - MutableTransformer layoutTransformer = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - MutableTransformer viewTransformer = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); - double modelScale = layoutTransformer.getScale(); - double viewScale = viewTransformer.getScale(); - double inverseModelScale = Math.sqrt(crossover)/modelScale; - double inverseViewScale = Math.sqrt(crossover)/viewScale; - double scale = modelScale * viewScale; - - Point2D transformedAt = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, at); - - if((scale*amount - crossover)*(scale*amount - crossover) < 0.001) { - // close to the control point, return both Functions to a scale of sqrt crossover value - layoutTransformer.scale(inverseModelScale, inverseModelScale, transformedAt); - viewTransformer.scale(inverseViewScale, inverseViewScale, at); - } else if(scale*amount < crossover) { - // scale the viewTransformer, return the layoutTransformer to sqrt crossover value - viewTransformer.scale(amount, amount, at); - layoutTransformer.scale(inverseModelScale, inverseModelScale, transformedAt); - } else { - // scale the layoutTransformer, return the viewTransformer to crossover value - layoutTransformer.scale(amount, amount, transformedAt); - viewTransformer.scale(inverseViewScale, inverseViewScale, at); - } - vv.repaint(); - } + vv.repaint(); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/CubicCurveEdgeEffects.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/CubicCurveEdgeEffects.java index 362d8ee1..1e1f78dc 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/CubicCurveEdgeEffects.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/CubicCurveEdgeEffects.java @@ -1,5 +1,8 @@ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.visualization.BasicVisualizationServer; +import edu.uci.ics.jung.visualization.VisualizationServer; +import edu.uci.ics.jung.visualization.util.ArrowFactory; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; @@ -8,133 +11,117 @@ import java.awt.geom.CubicCurve2D; import java.awt.geom.Point2D; -import edu.uci.ics.jung.visualization.BasicVisualizationServer; -import edu.uci.ics.jung.visualization.VisualizationServer; -import edu.uci.ics.jung.visualization.util.ArrowFactory; +public class CubicCurveEdgeEffects implements EdgeEffects { + + protected CubicCurve2D rawEdge = new CubicCurve2D.Float(); + protected Shape edgeShape; + protected Shape rawArrowShape; + protected Shape arrowShape; + protected VisualizationServer.Paintable edgePaintable; + protected VisualizationServer.Paintable arrowPaintable; + + public CubicCurveEdgeEffects() { + this.rawEdge.setCurve(0.0f, 0.0f, 0.33f, 100, 0.66f, -50, 1.0f, 0.0f); + rawArrowShape = ArrowFactory.getNotchedArrow(20, 16, 8); + this.edgePaintable = new EdgePaintable(); + this.arrowPaintable = new ArrowPaintable(); + } + + // @Override + public void startEdgeEffects(BasicVisualizationServer vv, Point2D down, Point2D out) { + transformEdgeShape(down, out); + vv.addPostRenderPaintable(edgePaintable); + } + + // @Override + public void midEdgeEffects(BasicVisualizationServer vv, Point2D down, Point2D out) { + transformEdgeShape(down, out); + } + + // @Override + public void endEdgeEffects(BasicVisualizationServer vv) { + vv.removePostRenderPaintable(edgePaintable); + } + + // @Override + public void startArrowEffects(BasicVisualizationServer vv, Point2D down, Point2D out) { + transformArrowShape(down, out); + vv.addPostRenderPaintable(arrowPaintable); + } -public class CubicCurveEdgeEffects implements EdgeEffects { - - protected CubicCurve2D rawEdge = new CubicCurve2D.Float(); - protected Shape edgeShape; - protected Shape rawArrowShape; - protected Shape arrowShape; - protected VisualizationServer.Paintable edgePaintable; - protected VisualizationServer.Paintable arrowPaintable; - - - public CubicCurveEdgeEffects() { - this.rawEdge.setCurve(0.0f, 0.0f, 0.33f, 100, 0.66f, -50, 1.0f, 0.0f); - rawArrowShape = ArrowFactory.getNotchedArrow(20, 16, 8); - this.edgePaintable = new EdgePaintable(); - this.arrowPaintable = new ArrowPaintable(); - } - -// @Override - public void startEdgeEffects(BasicVisualizationServer vv, - Point2D down, Point2D out) { - transformEdgeShape(down, out); - vv.addPostRenderPaintable(edgePaintable); - } - -// @Override - public void midEdgeEffects(BasicVisualizationServer vv, - Point2D down, Point2D out) { - transformEdgeShape(down, out); - } - -// @Override - public void endEdgeEffects(BasicVisualizationServer vv) { - vv.removePostRenderPaintable(edgePaintable); - } - -// @Override - public void startArrowEffects(BasicVisualizationServer vv, - Point2D down, Point2D out) { - transformArrowShape(down, out); - vv.addPostRenderPaintable(arrowPaintable); - } - -// @Override - public void midArrowEffects(BasicVisualizationServer vv, - Point2D down, Point2D out) { - transformArrowShape(down, out); - } - -// @Override - public void endArrowEffects(BasicVisualizationServer vv) { - vv.removePostRenderPaintable(arrowPaintable); - } - - /** - * code lifted from PluggableRenderer to move an edge shape into an - * arbitrary position - */ - private void transformEdgeShape(Point2D down, Point2D out) { - float x1 = (float) down.getX(); - float y1 = (float) down.getY(); - float x2 = (float) out.getX(); - float y2 = (float) out.getY(); - - AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); - - float dx = x2-x1; - float dy = y2-y1; - float thetaRadians = (float) Math.atan2(dy, dx); - xform.rotate(thetaRadians); - float dist = (float) Math.sqrt(dx*dx + dy*dy); - xform.scale(dist / rawEdge.getBounds().getWidth(), 1.0); - edgeShape = xform.createTransformedShape(rawEdge); + // @Override + public void midArrowEffects(BasicVisualizationServer vv, Point2D down, Point2D out) { + transformArrowShape(down, out); + } + + // @Override + public void endArrowEffects(BasicVisualizationServer vv) { + vv.removePostRenderPaintable(arrowPaintable); + } + + /** code lifted from PluggableRenderer to move an edge shape into an arbitrary position */ + private void transformEdgeShape(Point2D down, Point2D out) { + float x1 = (float) down.getX(); + float y1 = (float) down.getY(); + float x2 = (float) out.getX(); + float y2 = (float) out.getY(); + + AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); + + float dx = x2 - x1; + float dy = y2 - y1; + float thetaRadians = (float) Math.atan2(dy, dx); + xform.rotate(thetaRadians); + float dist = (float) Math.sqrt(dx * dx + dy * dy); + xform.scale(dist / rawEdge.getBounds().getWidth(), 1.0); + edgeShape = xform.createTransformedShape(rawEdge); + } + + private void transformArrowShape(Point2D down, Point2D out) { + float x1 = (float) down.getX(); + float y1 = (float) down.getY(); + float x2 = (float) out.getX(); + float y2 = (float) out.getY(); + + AffineTransform xform = AffineTransform.getTranslateInstance(x2, y2); + + float dx = x2 - x1; + float dy = y2 - y1; + float thetaRadians = (float) Math.atan2(dy, dx); + xform.rotate(thetaRadians); + arrowShape = xform.createTransformedShape(rawArrowShape); + } + /** Used for the edge creation visual effect during mouse drag */ + class EdgePaintable implements VisualizationServer.Paintable { + + public void paint(Graphics g) { + if (edgeShape != null) { + Color oldColor = g.getColor(); + g.setColor(Color.black); + ((Graphics2D) g).draw(edgeShape); + g.setColor(oldColor); + } } - - private void transformArrowShape(Point2D down, Point2D out) { - float x1 = (float) down.getX(); - float y1 = (float) down.getY(); - float x2 = (float) out.getX(); - float y2 = (float) out.getY(); - - AffineTransform xform = AffineTransform.getTranslateInstance(x2, y2); - - float dx = x2-x1; - float dy = y2-y1; - float thetaRadians = (float) Math.atan2(dy, dx); - xform.rotate(thetaRadians); - arrowShape = xform.createTransformedShape(rawArrowShape); + + public boolean useTransform() { + return false; } - /** - * Used for the edge creation visual effect during mouse drag - */ - class EdgePaintable implements VisualizationServer.Paintable { - - public void paint(Graphics g) { - if(edgeShape != null) { - Color oldColor = g.getColor(); - g.setColor(Color.black); - ((Graphics2D)g).draw(edgeShape); - g.setColor(oldColor); - } - } - - public boolean useTransform() { - return false; - } + } + + /** Used for the directed edge creation visual effect during mouse drag */ + class ArrowPaintable implements VisualizationServer.Paintable { + + public void paint(Graphics g) { + if (arrowShape != null) { + Color oldColor = g.getColor(); + g.setColor(Color.black); + ((Graphics2D) g).fill(arrowShape); + g.setColor(oldColor); + } } - - /** - * Used for the directed edge creation visual effect during mouse drag - */ - class ArrowPaintable implements VisualizationServer.Paintable { - - public void paint(Graphics g) { - if(arrowShape != null) { - Color oldColor = g.getColor(); - g.setColor(Color.black); - ((Graphics2D)g).fill(arrowShape); - g.setColor(oldColor); - } - } - - public boolean useTransform() { - return false; - } + + public boolean useTransform() { + return false; } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/DefaultModalGraphMouse.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/DefaultModalGraphMouse.java index 91dfeecb..a8a69e1b 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/DefaultModalGraphMouse.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/DefaultModalGraphMouse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -18,92 +18,81 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; - -/** - * - * DefaultModalGraphMouse is a PluggableGraphMouse class that - * pre-installs a large collection of plugins for picking and - * transforming the graph. Additionally, it carries the notion - * of a Mode: Picking or Translating. Switching between modes - * allows for a more natural choice of mouse modifiers to - * be used for the various plugins. The default modifiers are - * intended to mimick those of mainstream software applications - * in order to be intuitive to users. - * - * To change between modes, two different controls are offered, - * a combo box and a menu system. These controls are lazily created - * in their respective 'getter' methods so they don't impact - * code that does not intend to use them. - * The menu control can be placed in an unused corner of the - * GraphZoomScrollPane, which is a common location for mouse - * mode selection menus in mainstream applications. - * +/** + * DefaultModalGraphMouse is a PluggableGraphMouse class that pre-installs a large collection of + * plugins for picking and transforming the graph. Additionally, it carries the notion of a Mode: + * Picking or Translating. Switching between modes allows for a more natural choice of mouse + * modifiers to be used for the various plugins. The default modifiers are intended to mimick those + * of mainstream software applications in order to be intuitive to users. + * + *

            To change between modes, two different controls are offered, a combo box and a menu system. + * These controls are lazily created in their respective 'getter' methods so they don't impact code + * that does not intend to use them. The menu control can be placed in an unused corner of the + * GraphZoomScrollPane, which is a common location for mouse mode selection menus in mainstream + * applications. + * * @author Tom Nelson */ -public class DefaultModalGraphMouse extends AbstractModalGraphMouse +public class DefaultModalGraphMouse extends AbstractModalGraphMouse implements ModalGraphMouse, ItemSelectable { - - /** - * create an instance with default values - * - */ - public DefaultModalGraphMouse() { - this(1.1f, 1/1.1f); - } - - /** - * create an instance with passed values - * @param in override value for scale in - * @param out override value for scale out - */ - public DefaultModalGraphMouse(float in, float out) { - super(in,out); - loadPlugins(); - setModeKeyListener(new ModeKeyAdapter(this)); - } - - /** - * create the plugins, and load the plugins for TRANSFORMING mode - * - */ - @Override - protected void loadPlugins() { - pickingPlugin = new PickingGraphMousePlugin(); - animatedPickingPlugin = new AnimatedPickingGraphMousePlugin(); - translatingPlugin = new TranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); - scalingPlugin = new ScalingGraphMousePlugin(new CrossoverScalingControl(), 0, in, out); - rotatingPlugin = new RotatingGraphMousePlugin(); - shearingPlugin = new ShearingGraphMousePlugin(); - add(scalingPlugin); - setMode(Mode.TRANSFORMING); + /** create an instance with default values */ + public DefaultModalGraphMouse() { + this(1.1f, 1 / 1.1f); + } + + /** + * create an instance with passed values + * + * @param in override value for scale in + * @param out override value for scale out + */ + public DefaultModalGraphMouse(float in, float out) { + super(in, out); + loadPlugins(); + setModeKeyListener(new ModeKeyAdapter(this)); + } + + /** create the plugins, and load the plugins for TRANSFORMING mode */ + @Override + protected void loadPlugins() { + pickingPlugin = new PickingGraphMousePlugin(); + animatedPickingPlugin = new AnimatedPickingGraphMousePlugin(); + translatingPlugin = new TranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); + scalingPlugin = new ScalingGraphMousePlugin(new CrossoverScalingControl(), 0, in, out); + rotatingPlugin = new RotatingGraphMousePlugin(); + shearingPlugin = new ShearingGraphMousePlugin(); + + add(scalingPlugin); + setMode(Mode.TRANSFORMING); + } + + public static class ModeKeyAdapter extends KeyAdapter { + private char t = 't'; + private char p = 'p'; + protected ModalGraphMouse graphMouse; + + public ModeKeyAdapter(ModalGraphMouse graphMouse) { + this.graphMouse = graphMouse; } - - public static class ModeKeyAdapter extends KeyAdapter { - private char t = 't'; - private char p = 'p'; - protected ModalGraphMouse graphMouse; - public ModeKeyAdapter(ModalGraphMouse graphMouse) { - this.graphMouse = graphMouse; - } + public ModeKeyAdapter(char t, char p, ModalGraphMouse graphMouse) { + this.t = t; + this.p = p; + this.graphMouse = graphMouse; + } - public ModeKeyAdapter(char t, char p, ModalGraphMouse graphMouse) { - this.t = t; - this.p = p; - this.graphMouse = graphMouse; - } - - @Override - public void keyTyped(KeyEvent event) { - char keyChar = event.getKeyChar(); - if(keyChar == t) { - ((Component)event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - graphMouse.setMode(Mode.TRANSFORMING); - } else if(keyChar == p) { - ((Component)event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - graphMouse.setMode(Mode.PICKING); - } - } + @Override + public void keyTyped(KeyEvent event) { + char keyChar = event.getKeyChar(); + if (keyChar == t) { + ((Component) event.getSource()) + .setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + graphMouse.setMode(Mode.TRANSFORMING); + } else if (keyChar == p) { + ((Component) event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + graphMouse.setMode(Mode.PICKING); + } } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/EdgeEffects.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/EdgeEffects.java index c1cf61a4..7b155514 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/EdgeEffects.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/EdgeEffects.java @@ -1,25 +1,19 @@ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.visualization.BasicVisualizationServer; import java.awt.geom.Point2D; -import edu.uci.ics.jung.visualization.BasicVisualizationServer; +public interface EdgeEffects { + + void startEdgeEffects(BasicVisualizationServer vv, Point2D down, Point2D out); + + void midEdgeEffects(BasicVisualizationServer vv, Point2D down, Point2D out); + + void endEdgeEffects(BasicVisualizationServer vv); + + void startArrowEffects(BasicVisualizationServer vv, Point2D down, Point2D out); -public interface EdgeEffects { - - void startEdgeEffects(BasicVisualizationServer vv, - Point2D down, Point2D out); - - void midEdgeEffects(BasicVisualizationServer vv, - Point2D down, Point2D out); - - void endEdgeEffects(BasicVisualizationServer vv); - - void startArrowEffects(BasicVisualizationServer vv, - Point2D down, Point2D out); - - void midArrowEffects(BasicVisualizationServer vv, - Point2D down, Point2D out); - - void endArrowEffects(BasicVisualizationServer vv); + void midArrowEffects(BasicVisualizationServer vv, Point2D down, Point2D out); + void endArrowEffects(BasicVisualizationServer vv); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/EditingModalGraphMouse.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/EditingModalGraphMouse.java index c95268fe..4ed66f17 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/EditingModalGraphMouse.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/EditingModalGraphMouse.java @@ -1,5 +1,11 @@ package edu.uci.ics.jung.visualization.control; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.graph.MutableNetwork; +import edu.uci.ics.jung.visualization.MultiLayerTransformer; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.annotations.AnnotatingGraphMousePlugin; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; @@ -9,7 +15,6 @@ import java.awt.event.ItemListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; - import javax.swing.ButtonGroup; import javax.swing.Icon; import javax.swing.JComboBox; @@ -17,300 +22,292 @@ import javax.swing.JRadioButtonMenuItem; import javax.swing.plaf.basic.BasicIconFactory; -import com.google.common.base.Preconditions; -import com.google.common.base.Supplier; -import com.google.common.graph.MutableNetwork; +public class EditingModalGraphMouse extends AbstractModalGraphMouse + implements ModalGraphMouse, ItemSelectable { -import edu.uci.ics.jung.visualization.MultiLayerTransformer; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.annotations.AnnotatingGraphMousePlugin; + protected Supplier vertexFactory; + protected Supplier edgeFactory; + protected EditingGraphMousePlugin editingPlugin; + protected LabelEditingGraphMousePlugin labelEditingPlugin; + protected EditingPopupGraphMousePlugin popupEditingPlugin; + protected AnnotatingGraphMousePlugin annotatingPlugin; + protected MultiLayerTransformer basicTransformer; + protected RenderContext rc; -public class EditingModalGraphMouse extends AbstractModalGraphMouse - implements ModalGraphMouse, ItemSelectable { + /** + * Creates an instance with the specified rendering context and vertex/edge factories, and with + * default zoom in/out values of 1.1 and 1/1.1. + * + * @param rc the rendering context + * @param vertexFactory used to construct vertices + * @param edgeFactory used to construct edges + */ + public EditingModalGraphMouse( + RenderContext rc, Supplier vertexFactory, Supplier edgeFactory) { + this(rc, vertexFactory, edgeFactory, 1.1f, 1 / 1.1f); + } - protected Supplier vertexFactory; - protected Supplier edgeFactory; - protected EditingGraphMousePlugin editingPlugin; - protected LabelEditingGraphMousePlugin labelEditingPlugin; - protected EditingPopupGraphMousePlugin popupEditingPlugin; - protected AnnotatingGraphMousePlugin annotatingPlugin; - protected MultiLayerTransformer basicTransformer; - protected RenderContext rc; + /** + * Creates an instance with the specified rendering context and vertex/edge factories, and with + * the specified zoom in/out values. + * + * @param rc the rendering context + * @param vertexFactory used to construct vertices + * @param edgeFactory used to construct edges + * @param in amount to zoom in by for each action + * @param out amount to zoom out by for each action + */ + public EditingModalGraphMouse( + RenderContext rc, + Supplier vertexFactory, + Supplier edgeFactory, + float in, + float out) { + super(in, out); + Preconditions.checkArgument( + rc.getNetwork() instanceof MutableNetwork, + "Supplied Network instance must be a MutableNetwork"); + this.vertexFactory = vertexFactory; + this.edgeFactory = edgeFactory; + this.rc = rc; + this.basicTransformer = rc.getMultiLayerTransformer(); + loadPlugins(); + setModeKeyListener(new ModeKeyAdapter(this)); + } - /** - * Creates an instance with the specified rendering context and vertex/edge factories, - * and with default zoom in/out values of 1.1 and 1/1.1. - * @param rc the rendering context - * @param vertexFactory used to construct vertices - * @param edgeFactory used to construct edges - */ - public EditingModalGraphMouse(RenderContext rc, - Supplier vertexFactory, Supplier edgeFactory) { - this(rc, vertexFactory, edgeFactory, 1.1f, 1/1.1f); - } + /** create the plugins, and load the plugins for TRANSFORMING mode */ + @Override + protected void loadPlugins() { + pickingPlugin = new PickingGraphMousePlugin(); + animatedPickingPlugin = new AnimatedPickingGraphMousePlugin(); + translatingPlugin = new TranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); + scalingPlugin = new ScalingGraphMousePlugin(new CrossoverScalingControl(), 0, in, out); + rotatingPlugin = new RotatingGraphMousePlugin(); + shearingPlugin = new ShearingGraphMousePlugin(); + editingPlugin = new EditingGraphMousePlugin(vertexFactory, edgeFactory); + labelEditingPlugin = new LabelEditingGraphMousePlugin(); + annotatingPlugin = new AnnotatingGraphMousePlugin(rc); + popupEditingPlugin = new EditingPopupGraphMousePlugin(vertexFactory, edgeFactory); + add(scalingPlugin); + setMode(Mode.EDITING); + } - /** - * Creates an instance with the specified rendering context and vertex/edge factories, - * and with the specified zoom in/out values. - * @param rc the rendering context - * @param vertexFactory used to construct vertices - * @param edgeFactory used to construct edges - * @param in amount to zoom in by for each action - * @param out amount to zoom out by for each action - */ - public EditingModalGraphMouse(RenderContext rc, - Supplier vertexFactory, Supplier edgeFactory, float in, float out) { - super(in,out); - Preconditions.checkArgument(rc.getNetwork() instanceof MutableNetwork, - "Supplied Network instance must be a MutableNetwork"); - this.vertexFactory = vertexFactory; - this.edgeFactory = edgeFactory; - this.rc = rc; - this.basicTransformer = rc.getMultiLayerTransformer(); - loadPlugins(); - setModeKeyListener(new ModeKeyAdapter(this)); - } + /** setter for the Mode. */ + @Override + public void setMode(Mode mode) { + if (this.mode != mode) { + fireItemStateChanged( + new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, this.mode, ItemEvent.DESELECTED)); + this.mode = mode; + if (mode == Mode.TRANSFORMING) { + setTransformingMode(); + } else if (mode == Mode.PICKING) { + setPickingMode(); + } else if (mode == Mode.EDITING) { + setEditingMode(); + } else if (mode == Mode.ANNOTATING) { + setAnnotatingMode(); + } + if (modeBox != null) { + modeBox.setSelectedItem(mode); + } + fireItemStateChanged( + new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, mode, ItemEvent.SELECTED)); + } + } - /** - * create the plugins, and load the plugins for TRANSFORMING mode - * - */ - @Override - protected void loadPlugins() { - pickingPlugin = new PickingGraphMousePlugin(); - animatedPickingPlugin = new AnimatedPickingGraphMousePlugin(); - translatingPlugin = new TranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); - scalingPlugin = new ScalingGraphMousePlugin(new CrossoverScalingControl(), 0, in, out); - rotatingPlugin = new RotatingGraphMousePlugin(); - shearingPlugin = new ShearingGraphMousePlugin(); - editingPlugin = new EditingGraphMousePlugin(vertexFactory, edgeFactory); - labelEditingPlugin = new LabelEditingGraphMousePlugin(); - annotatingPlugin = new AnnotatingGraphMousePlugin(rc); - popupEditingPlugin = new EditingPopupGraphMousePlugin(vertexFactory, edgeFactory); - add(scalingPlugin); - setMode(Mode.EDITING); - } + @Override + protected void setPickingMode() { + remove(translatingPlugin); + remove(rotatingPlugin); + remove(shearingPlugin); + remove(editingPlugin); + remove(annotatingPlugin); + add(pickingPlugin); + add(animatedPickingPlugin); + add(labelEditingPlugin); + add(popupEditingPlugin); + } - /** - * setter for the Mode. - */ - @Override - public void setMode(Mode mode) { - if(this.mode != mode) { - fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, - this.mode, ItemEvent.DESELECTED)); - this.mode = mode; - if(mode == Mode.TRANSFORMING) { - setTransformingMode(); - } else if(mode == Mode.PICKING) { - setPickingMode(); - } else if(mode == Mode.EDITING) { - setEditingMode(); - } else if(mode == Mode.ANNOTATING) { - setAnnotatingMode(); - } - if(modeBox != null) { - modeBox.setSelectedItem(mode); - } - fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, mode, ItemEvent.SELECTED)); - } - } - - @Override - protected void setPickingMode() { - remove(translatingPlugin); - remove(rotatingPlugin); - remove(shearingPlugin); - remove(editingPlugin); - remove(annotatingPlugin); - add(pickingPlugin); - add(animatedPickingPlugin); - add(labelEditingPlugin); - add(popupEditingPlugin); - } + @Override + protected void setTransformingMode() { + remove(pickingPlugin); + remove(animatedPickingPlugin); + remove(editingPlugin); + remove(annotatingPlugin); + add(translatingPlugin); + add(rotatingPlugin); + add(shearingPlugin); + add(labelEditingPlugin); + add(popupEditingPlugin); + } - @Override - protected void setTransformingMode() { - remove(pickingPlugin); - remove(animatedPickingPlugin); - remove(editingPlugin); - remove(annotatingPlugin); - add(translatingPlugin); - add(rotatingPlugin); - add(shearingPlugin); - add(labelEditingPlugin); - add(popupEditingPlugin); - } + protected void setEditingMode() { + remove(pickingPlugin); + remove(animatedPickingPlugin); + remove(translatingPlugin); + remove(rotatingPlugin); + remove(shearingPlugin); + remove(labelEditingPlugin); + remove(annotatingPlugin); + add(editingPlugin); + add(popupEditingPlugin); + } - protected void setEditingMode() { - remove(pickingPlugin); - remove(animatedPickingPlugin); - remove(translatingPlugin); - remove(rotatingPlugin); - remove(shearingPlugin); - remove(labelEditingPlugin); - remove(annotatingPlugin); - add(editingPlugin); - add(popupEditingPlugin); - } + protected void setAnnotatingMode() { + remove(pickingPlugin); + remove(animatedPickingPlugin); + remove(translatingPlugin); + remove(rotatingPlugin); + remove(shearingPlugin); + remove(labelEditingPlugin); + remove(editingPlugin); + remove(popupEditingPlugin); + add(annotatingPlugin); + } - protected void setAnnotatingMode() { - remove(pickingPlugin); - remove(animatedPickingPlugin); - remove(translatingPlugin); - remove(rotatingPlugin); - remove(shearingPlugin); - remove(labelEditingPlugin); - remove(editingPlugin); - remove(popupEditingPlugin); - add(annotatingPlugin); - } + /** @return the modeBox. */ + @Override + public JComboBox getModeComboBox() { + if (modeBox == null) { + modeBox = + new JComboBox( + new Mode[] {Mode.TRANSFORMING, Mode.PICKING, Mode.EDITING, Mode.ANNOTATING}); + modeBox.addItemListener(getModeListener()); + } + modeBox.setSelectedItem(mode); + return modeBox; + } + /** + * create (if necessary) and return a menu that will change the mode + * + * @return the menu + */ + @Override + public JMenu getModeMenu() { + if (modeMenu == null) { + modeMenu = new JMenu(); // { + Icon icon = BasicIconFactory.getMenuArrowIcon(); + modeMenu.setIcon(BasicIconFactory.getMenuArrowIcon()); + modeMenu.setPreferredSize(new Dimension(icon.getIconWidth() + 10, icon.getIconHeight() + 10)); - /** - * @return the modeBox. - */ - @Override - public JComboBox getModeComboBox() { - if(modeBox == null) { - modeBox = new JComboBox( - new Mode[]{Mode.TRANSFORMING, Mode.PICKING, Mode.EDITING, Mode.ANNOTATING}); - modeBox.addItemListener(getModeListener()); - } - modeBox.setSelectedItem(mode); - return modeBox; - } + final JRadioButtonMenuItem transformingButton = + new JRadioButtonMenuItem(Mode.TRANSFORMING.toString()); + transformingButton.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + setMode(Mode.TRANSFORMING); + } + } + }); - /** - * create (if necessary) and return a menu that will change - * the mode - * @return the menu - */ - @Override - public JMenu getModeMenu() { - if(modeMenu == null) { - modeMenu = new JMenu();// { - Icon icon = BasicIconFactory.getMenuArrowIcon(); - modeMenu.setIcon(BasicIconFactory.getMenuArrowIcon()); - modeMenu.setPreferredSize(new Dimension(icon.getIconWidth()+10, - icon.getIconHeight()+10)); + final JRadioButtonMenuItem pickingButton = new JRadioButtonMenuItem(Mode.PICKING.toString()); + pickingButton.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + setMode(Mode.PICKING); + } + } + }); - final JRadioButtonMenuItem transformingButton = - new JRadioButtonMenuItem(Mode.TRANSFORMING.toString()); - transformingButton.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - setMode(Mode.TRANSFORMING); - } - }}); + final JRadioButtonMenuItem editingButton = new JRadioButtonMenuItem(Mode.EDITING.toString()); + editingButton.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + setMode(Mode.EDITING); + } + } + }); - final JRadioButtonMenuItem pickingButton = - new JRadioButtonMenuItem(Mode.PICKING.toString()); - pickingButton.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - setMode(Mode.PICKING); - } - }}); + ButtonGroup radio = new ButtonGroup(); + radio.add(transformingButton); + radio.add(pickingButton); + radio.add(editingButton); + transformingButton.setSelected(true); + modeMenu.add(transformingButton); + modeMenu.add(pickingButton); + modeMenu.add(editingButton); + modeMenu.setToolTipText("Menu for setting Mouse Mode"); + addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + if (e.getItem() == Mode.TRANSFORMING) { + transformingButton.setSelected(true); + } else if (e.getItem() == Mode.PICKING) { + pickingButton.setSelected(true); + } else if (e.getItem() == Mode.EDITING) { + editingButton.setSelected(true); + } + } + } + }); + } + return modeMenu; + } - final JRadioButtonMenuItem editingButton = - new JRadioButtonMenuItem(Mode.EDITING.toString()); - editingButton.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - setMode(Mode.EDITING); - } - }}); + public static class ModeKeyAdapter extends KeyAdapter { + private char t = 't'; + private char p = 'p'; + private char e = 'e'; + private char a = 'a'; + protected ModalGraphMouse graphMouse; - ButtonGroup radio = new ButtonGroup(); - radio.add(transformingButton); - radio.add(pickingButton); - radio.add(editingButton); - transformingButton.setSelected(true); - modeMenu.add(transformingButton); - modeMenu.add(pickingButton); - modeMenu.add(editingButton); - modeMenu.setToolTipText("Menu for setting Mouse Mode"); - addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if(e.getStateChange() == ItemEvent.SELECTED) { - if(e.getItem() == Mode.TRANSFORMING) { - transformingButton.setSelected(true); - } else if(e.getItem() == Mode.PICKING) { - pickingButton.setSelected(true); - } else if(e.getItem() == Mode.EDITING) { - editingButton.setSelected(true); - } - } - }}); - } - return modeMenu; - } - - public static class ModeKeyAdapter extends KeyAdapter { - private char t = 't'; - private char p = 'p'; - private char e = 'e'; - private char a = 'a'; - protected ModalGraphMouse graphMouse; + public ModeKeyAdapter(ModalGraphMouse graphMouse) { + this.graphMouse = graphMouse; + } - public ModeKeyAdapter(ModalGraphMouse graphMouse) { - this.graphMouse = graphMouse; - } + public ModeKeyAdapter(char t, char p, char e, char a, ModalGraphMouse graphMouse) { + this.t = t; + this.p = p; + this.e = e; + this.a = a; + this.graphMouse = graphMouse; + } - public ModeKeyAdapter(char t, char p, char e, char a, ModalGraphMouse graphMouse) { - this.t = t; - this.p = p; - this.e = e; - this.a = a; - this.graphMouse = graphMouse; - } - - @Override - public void keyTyped(KeyEvent event) { - char keyChar = event.getKeyChar(); - if(keyChar == t) { - ((Component)event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - graphMouse.setMode(Mode.TRANSFORMING); - } else if(keyChar == p) { - ((Component)event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - graphMouse.setMode(Mode.PICKING); - } else if(keyChar == e) { - ((Component)event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); - graphMouse.setMode(Mode.EDITING); - } else if(keyChar == a) { - ((Component)event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); - graphMouse.setMode(Mode.ANNOTATING); - } - } + @Override + public void keyTyped(KeyEvent event) { + char keyChar = event.getKeyChar(); + if (keyChar == t) { + ((Component) event.getSource()) + .setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + graphMouse.setMode(Mode.TRANSFORMING); + } else if (keyChar == p) { + ((Component) event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + graphMouse.setMode(Mode.PICKING); + } else if (keyChar == e) { + ((Component) event.getSource()) + .setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); + graphMouse.setMode(Mode.EDITING); + } else if (keyChar == a) { + ((Component) event.getSource()) + .setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); + graphMouse.setMode(Mode.ANNOTATING); + } } + } - /** - * @return the annotatingPlugin - */ - public AnnotatingGraphMousePlugin getAnnotatingPlugin() { - return annotatingPlugin; - } + /** @return the annotatingPlugin */ + public AnnotatingGraphMousePlugin getAnnotatingPlugin() { + return annotatingPlugin; + } - /** - * @return the editingPlugin - */ - public EditingGraphMousePlugin getEditingPlugin() { - return editingPlugin; - } + /** @return the editingPlugin */ + public EditingGraphMousePlugin getEditingPlugin() { + return editingPlugin; + } - /** - * @return the labelEditingPlugin - */ - public LabelEditingGraphMousePlugin getLabelEditingPlugin() { - return labelEditingPlugin; - } + /** @return the labelEditingPlugin */ + public LabelEditingGraphMousePlugin getLabelEditingPlugin() { + return labelEditingPlugin; + } - /** - * @return the popupEditingPlugin - */ - public EditingPopupGraphMousePlugin getPopupEditingPlugin() { - return popupEditingPlugin; - } + /** @return the popupEditingPlugin */ + public EditingPopupGraphMousePlugin getPopupEditingPlugin() { + return popupEditingPlugin; + } } - diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/EditingPopupGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/EditingPopupGraphMousePlugin.java index 0cc68b8d..1a83a98a 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/EditingPopupGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/EditingPopupGraphMousePlugin.java @@ -1,97 +1,97 @@ package edu.uci.ics.jung.visualization.control; +import com.google.common.base.Supplier; +import com.google.common.graph.MutableNetwork; +import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.picking.PickedState; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.util.Set; - import javax.swing.AbstractAction; import javax.swing.JMenu; import javax.swing.JPopupMenu; -import com.google.common.base.Supplier; -import com.google.common.graph.MutableNetwork; - -import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.picking.PickedState; - /** - * a plugin that uses popup menus to create vertices, undirected edges, - * and directed edges. - * - * @author Tom Nelson + * a plugin that uses popup menus to create vertices, undirected edges, and directed edges. * + * @author Tom Nelson */ -public class EditingPopupGraphMousePlugin extends AbstractPopupGraphMousePlugin { - - protected Supplier vertexFactory; - protected Supplier edgeFactory; +public class EditingPopupGraphMousePlugin extends AbstractPopupGraphMousePlugin { - public EditingPopupGraphMousePlugin(Supplier vertexFactory, Supplier edgeFactory) { - this.vertexFactory = vertexFactory; - this.edgeFactory = edgeFactory; - } - - @SuppressWarnings({ "unchecked", "serial" }) - protected void handlePopup(MouseEvent e) { - final VisualizationViewer vv = - (VisualizationViewer)e.getSource(); - final MutableNetwork graph = (MutableNetwork) vv.getModel().getNetwork(); - final Point2D p = e.getPoint(); - NetworkElementAccessor pickSupport = vv.getPickSupport(); - if(pickSupport != null) { - - final V vertex = pickSupport.getNode(p.getX(), p.getY()); - final E edge = pickSupport.getEdge(p.getX(), p.getY()); - final PickedState pickedVertexState = vv.getPickedVertexState(); - final PickedState pickedEdgeState = vv.getPickedEdgeState(); - - JPopupMenu popup = new JPopupMenu(); - if(vertex != null) { - Set picked = pickedVertexState.getPicked(); - if(picked.size() > 0) { - JMenu menu = new JMenu("Create " - + (graph.isDirected() ? "Directed" : "Undirected") - + " Edge"); - popup.add(menu); - for(final V other : picked) { - menu.add(new AbstractAction("["+other+","+vertex+"]") { - public void actionPerformed(ActionEvent e) { - graph.addEdge(other, vertex, edgeFactory.get()); - vv.repaint(); - } - }); - } - } - popup.add(new AbstractAction("Delete Vertex") { - public void actionPerformed(ActionEvent e) { - pickedVertexState.pick(vertex, false); - graph.removeNode(vertex); - vv.repaint(); - }}); - } else if(edge != null) { - popup.add(new AbstractAction("Delete Edge") { - public void actionPerformed(ActionEvent e) { - pickedEdgeState.pick(edge, false); - graph.removeEdge(edge); - vv.repaint(); - }}); - } else { - popup.add(new AbstractAction("Create Vertex") { - public void actionPerformed(ActionEvent e) { - V newVertex = vertexFactory.get(); - graph.addNode(newVertex); - vv.getGraphLayout().setLocation(newVertex, - vv.getRenderContext().getMultiLayerTransformer().inverseTransform(p)); - vv.repaint(); - } + protected Supplier vertexFactory; + protected Supplier edgeFactory; + + public EditingPopupGraphMousePlugin(Supplier vertexFactory, Supplier edgeFactory) { + this.vertexFactory = vertexFactory; + this.edgeFactory = edgeFactory; + } + + @SuppressWarnings({"unchecked", "serial"}) + protected void handlePopup(MouseEvent e) { + final VisualizationViewer vv = (VisualizationViewer) e.getSource(); + final MutableNetwork graph = (MutableNetwork) vv.getModel().getNetwork(); + final Point2D p = e.getPoint(); + NetworkElementAccessor pickSupport = vv.getPickSupport(); + if (pickSupport != null) { + + final V vertex = pickSupport.getNode(p.getX(), p.getY()); + final E edge = pickSupport.getEdge(p.getX(), p.getY()); + final PickedState pickedVertexState = vv.getPickedVertexState(); + final PickedState pickedEdgeState = vv.getPickedEdgeState(); + + JPopupMenu popup = new JPopupMenu(); + if (vertex != null) { + Set picked = pickedVertexState.getPicked(); + if (picked.size() > 0) { + JMenu menu = + new JMenu("Create " + (graph.isDirected() ? "Directed" : "Undirected") + " Edge"); + popup.add(menu); + for (final V other : picked) { + menu.add( + new AbstractAction("[" + other + "," + vertex + "]") { + public void actionPerformed(ActionEvent e) { + graph.addEdge(other, vertex, edgeFactory.get()); + vv.repaint(); + } }); - } - if(popup.getComponentCount() > 0) { - popup.show(vv, e.getX(), e.getY()); - } + } } + popup.add( + new AbstractAction("Delete Vertex") { + public void actionPerformed(ActionEvent e) { + pickedVertexState.pick(vertex, false); + graph.removeNode(vertex); + vv.repaint(); + } + }); + } else if (edge != null) { + popup.add( + new AbstractAction("Delete Edge") { + public void actionPerformed(ActionEvent e) { + pickedEdgeState.pick(edge, false); + graph.removeEdge(edge); + vv.repaint(); + } + }); + } else { + popup.add( + new AbstractAction("Create Vertex") { + public void actionPerformed(ActionEvent e) { + V newVertex = vertexFactory.get(); + graph.addNode(newVertex); + vv.getGraphLayout() + .setLocation( + newVertex, + vv.getRenderContext().getMultiLayerTransformer().inverseTransform(p)); + vv.repaint(); + } + }); + } + if (popup.getComponentCount() > 0) { + popup.show(vv, e.getX(), e.getY()); + } } + } } - diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/GraphMouseAdapter.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/GraphMouseAdapter.java index 82c1b406..6b40d46f 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/GraphMouseAdapter.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/GraphMouseAdapter.java @@ -14,29 +14,27 @@ import java.awt.event.MouseEvent; /** - * Simple extension of MouseAdapter that supplies modifier - * checking - * - * @author Tom Nelson + * Simple extension of MouseAdapter that supplies modifier checking * + * @author Tom Nelson */ public class GraphMouseAdapter extends MouseAdapter { - protected int modifiers; - - public GraphMouseAdapter(int modifiers) { - this.modifiers = modifiers; - } - - public int getModifiers() { - return modifiers; - } - - public void setModifiers(int modifiers) { - this.modifiers = modifiers; - } - - protected boolean checkModifiers(MouseEvent e) { - return e.getModifiers() == modifiers; - } + protected int modifiers; + + public GraphMouseAdapter(int modifiers) { + this.modifiers = modifiers; + } + + public int getModifiers() { + return modifiers; + } + + public void setModifiers(int modifiers) { + this.modifiers = modifiers; + } + + protected boolean checkModifiers(MouseEvent e) { + return e.getModifiers() == modifiers; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/GraphMouseListener.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/GraphMouseListener.java index 7d1cbc86..86d7e490 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/GraphMouseListener.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/GraphMouseListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, The JUNG Authors + * Copyright (c) 2003, The JUNG Authors * * All rights reserved. * @@ -15,15 +15,15 @@ import java.awt.event.MouseEvent; /** - * This interface allows users to register listeners to register to receive - * vertex clicks. - * + * This interface allows users to register listeners to register to receive vertex clicks. + * * @author danyelf */ public interface GraphMouseListener { - void graphClicked(V v, MouseEvent me); - void graphPressed(V v, MouseEvent me); - void graphReleased(V v, MouseEvent me); + void graphClicked(V v, MouseEvent me); + + void graphPressed(V v, MouseEvent me); + void graphReleased(V v, MouseEvent me); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/GraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/GraphMousePlugin.java index 0cdb2816..93e47ece 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/GraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/GraphMousePlugin.java @@ -14,25 +14,22 @@ /** * the interface for all plugins to the PluggableGraphMouse - * @author Tom Nelson * + * @author Tom Nelson */ public interface GraphMousePlugin { - /** - * @return the mouse event modifiers that will activate this plugin - */ - int getModifiers(); + /** @return the mouse event modifiers that will activate this plugin */ + int getModifiers(); - /** - * @param modifiers the mouse event modifiers that will activate this plugin - */ - void setModifiers(int modifiers); - - /** - * compare the set modifiers against those of the supplied event - * @param e an event to compare to - * @return whether the member modifiers match the event modifiers - */ - boolean checkModifiers(MouseEvent e); + /** @param modifiers the mouse event modifiers that will activate this plugin */ + void setModifiers(int modifiers); + + /** + * compare the set modifiers against those of the supplied event + * + * @param e an event to compare to + * @return whether the member modifiers match the event modifiers + */ + boolean checkModifiers(MouseEvent e); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LabelEditingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LabelEditingGraphMousePlugin.java index 9e7a056b..c49154e0 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LabelEditingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LabelEditingGraphMousePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,145 +11,118 @@ */ package edu.uci.ics.jung.visualization.control; +import com.google.common.base.Function; +import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; +import edu.uci.ics.jung.algorithms.util.MapSettableTransformer; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationViewer; import java.awt.Cursor; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.geom.Point2D; - import javax.swing.JOptionPane; -import com.google.common.base.Function; - -import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; -import edu.uci.ics.jung.algorithms.util.MapSettableTransformer; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationViewer; - -/** - * - * - * @author Tom Nelson - */ +/** @author Tom Nelson */ public class LabelEditingGraphMousePlugin extends AbstractGraphMousePlugin implements MouseListener { - /** - * the picked Vertex, if any - */ - protected V vertex; - - /** - * the picked Edge, if any - */ - protected E edge; - - /** - * create an instance with default settings - */ - public LabelEditingGraphMousePlugin() { - this(InputEvent.BUTTON1_MASK); - } + /** the picked Vertex, if any */ + protected V vertex; - /** - * create an instance with overrides - * @param selectionModifiers for primary selection - */ - public LabelEditingGraphMousePlugin(int selectionModifiers) { - super(selectionModifiers); - this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); - } - - /** - * For primary modifiers (default, MouseButton1): - * pick a single Vertex or Edge that - * is under the mouse pointer. If no Vertex or edge is under - * the pointer, unselect all picked Vertices and edges, and - * set up to draw a rectangle for multiple selection - * of contained Vertices. - * For additional selection (default Shift+MouseButton1): - * Add to the selection, a single Vertex or Edge that is - * under the mouse pointer. If a previously picked Vertex - * or Edge is under the pointer, it is un-picked. - * If no vertex or Edge is under the pointer, set up - * to draw a multiple selection rectangle (as above) - * but do not unpick previously picked elements. - * - * @param e the event - */ - @SuppressWarnings("unchecked") - public void mouseClicked(MouseEvent e) { - if (e.getModifiers() == modifiers && e.getClickCount() == 2) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - NetworkElementAccessor pickSupport = vv.getPickSupport(); - if (pickSupport != null) { - Function vs = vv.getRenderContext().getVertexLabelTransformer(); - if (vs instanceof MapSettableTransformer) { - MapSettableTransformer mst = - (MapSettableTransformer)vs; -// Layout layout = vv.getGraphLayout(); - // p is the screen point for the mouse event - Point2D p = e.getPoint(); + /** the picked Edge, if any */ + protected E edge; - V vertex = pickSupport.getNode(p.getX(), p.getY()); - if(vertex != null) { - String newLabel = vs.apply(vertex); - newLabel = JOptionPane.showInputDialog("New Vertex Label for "+vertex); - if(newLabel != null) { - mst.set(vertex, newLabel); - vv.repaint(); - } - return; - } - } - Function es = vv.getRenderContext().getEdgeLabelTransformer(); - if (es instanceof MapSettableTransformer) { - MapSettableTransformer mst = - (MapSettableTransformer)es; -// Layout layout = vv.getGraphLayout(); - // p is the screen point for the mouse event - Point2D p = e.getPoint(); - // take away the view transform - Point2D ip = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, p); - E edge = pickSupport.getEdge(ip.getX(), ip.getY()); - if(edge != null) { - String newLabel = JOptionPane.showInputDialog("New Edge Label for "+edge); - if(newLabel != null) { - mst.set(edge, newLabel); - vv.repaint(); - } - return; - } - } - } - e.consume(); - } - } + /** create an instance with default settings */ + public LabelEditingGraphMousePlugin() { + this(InputEvent.BUTTON1_MASK); + } - /** - * If the mouse is dragging a rectangle, pick the - * Vertices contained in that rectangle - * - * clean up settings from mousePressed - */ - public void mouseReleased(MouseEvent e) { - } - - /** - * If the mouse is over a picked vertex, drag all picked - * vertices with the mouse. - * If the mouse is not over a Vertex, draw the rectangle - * to select multiple Vertices - * - */ + /** + * create an instance with overrides + * + * @param selectionModifiers for primary selection + */ + public LabelEditingGraphMousePlugin(int selectionModifiers) { + super(selectionModifiers); + this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); + } - public void mousePressed(MouseEvent e) { + /** + * For primary modifiers (default, MouseButton1): pick a single Vertex or Edge that is under the + * mouse pointer. If no Vertex or edge is under the pointer, unselect all picked Vertices and + * edges, and set up to draw a rectangle for multiple selection of contained Vertices. For + * additional selection (default Shift+MouseButton1): Add to the selection, a single Vertex or + * Edge that is under the mouse pointer. If a previously picked Vertex or Edge is under the + * pointer, it is un-picked. If no vertex or Edge is under the pointer, set up to draw a multiple + * selection rectangle (as above) but do not unpick previously picked elements. + * + * @param e the event + */ + @SuppressWarnings("unchecked") + public void mouseClicked(MouseEvent e) { + if (e.getModifiers() == modifiers && e.getClickCount() == 2) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + NetworkElementAccessor pickSupport = vv.getPickSupport(); + if (pickSupport != null) { + Function vs = vv.getRenderContext().getVertexLabelTransformer(); + if (vs instanceof MapSettableTransformer) { + MapSettableTransformer mst = + (MapSettableTransformer) vs; + // Layout layout = vv.getGraphLayout(); + // p is the screen point for the mouse event + Point2D p = e.getPoint(); + + V vertex = pickSupport.getNode(p.getX(), p.getY()); + if (vertex != null) { + String newLabel = vs.apply(vertex); + newLabel = JOptionPane.showInputDialog("New Vertex Label for " + vertex); + if (newLabel != null) { + mst.set(vertex, newLabel); + vv.repaint(); + } + return; + } + } + Function es = vv.getRenderContext().getEdgeLabelTransformer(); + if (es instanceof MapSettableTransformer) { + MapSettableTransformer mst = + (MapSettableTransformer) es; + // Layout layout = vv.getGraphLayout(); + // p is the screen point for the mouse event + Point2D p = e.getPoint(); + // take away the view transform + Point2D ip = + vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, p); + E edge = pickSupport.getEdge(ip.getX(), ip.getY()); + if (edge != null) { + String newLabel = JOptionPane.showInputDialog("New Edge Label for " + edge); + if (newLabel != null) { + mst.set(edge, newLabel); + vv.repaint(); + } + return; + } + } + } + e.consume(); } + } + + /** + * If the mouse is dragging a rectangle, pick the Vertices contained in that rectangle + * + *

            clean up settings from mousePressed + */ + public void mouseReleased(MouseEvent e) {} - public void mouseEntered(MouseEvent e) { - } + /** + * If the mouse is over a picked vertex, drag all picked vertices with the mouse. If the mouse is + * not over a Vertex, draw the rectangle to select multiple Vertices + */ + public void mousePressed(MouseEvent e) {} - public void mouseExited(MouseEvent e) { - } + public void mouseEntered(MouseEvent e) {} + public void mouseExited(MouseEvent e) {} } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LayoutScalingControl.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LayoutScalingControl.java index aa92d7fb..64862437 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LayoutScalingControl.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LayoutScalingControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,32 +11,28 @@ */ package edu.uci.ics.jung.visualization.control; -import java.awt.geom.Point2D; - import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.VisualizationServer; import edu.uci.ics.jung.visualization.transform.MutableTransformer; +import java.awt.geom.Point2D; -/** - * LayoutScalingControl applies a scaling transformation to the graph layout. - * The Vertices get closer or farther apart, but do not themselves change - * size. ScalingGraphMouse uses MouseWheelEvents to apply the scaling. - * +/** + * LayoutScalingControl applies a scaling transformation to the graph layout. The Vertices get + * closer or farther apart, but do not themselves change size. ScalingGraphMouse uses + * MouseWheelEvents to apply the scaling. + * * @author Tom Nelson */ public class LayoutScalingControl implements ScalingControl { - /** - * zoom the display in or out, depending on the direction of the - * mouse wheel motion. - */ - public void scale(VisualizationServer vv, float amount, Point2D from) { - - Point2D ivtfrom = vv.getRenderContext().getMultiLayerTransformer() - .inverseTransform(Layer.VIEW, from); - MutableTransformer modelTransformer = vv.getRenderContext().getMultiLayerTransformer() - .getTransformer(Layer.LAYOUT); - modelTransformer.scale(amount, amount, ivtfrom); - vv.repaint(); - } + /** zoom the display in or out, depending on the direction of the mouse wheel motion. */ + public void scale(VisualizationServer vv, float amount, Point2D from) { + + Point2D ivtfrom = + vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, from); + MutableTransformer modelTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + modelTransformer.scale(amount, amount, ivtfrom); + vv.repaint(); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LensMagnificationGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LensMagnificationGraphMousePlugin.java index 43d68898..0331abd1 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LensMagnificationGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LensMagnificationGraphMousePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,106 +11,103 @@ */ package edu.uci.ics.jung.visualization.control; -import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; -import java.awt.event.MouseWheelListener; - import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.VisualizationViewer; import edu.uci.ics.jung.visualization.transform.LensTransformer; import edu.uci.ics.jung.visualization.transform.MutableTransformer; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; -/** - * HyperbolicMagnificationGraphMousePlugin changes the magnification - * within the Hyperbolic projection of the HyperbolicTransformer. - * +/** + * HyperbolicMagnificationGraphMousePlugin changes the magnification within the Hyperbolic + * projection of the HyperbolicTransformer. + * * @author Tom Nelson */ public class LensMagnificationGraphMousePlugin extends AbstractGraphMousePlugin implements MouseWheelListener { - protected final float floor; - protected final float ceiling; - protected final float delta; - - /** - * Creates an instance with modifier of CTRL_MASK, and default min/max/delta zoom values - * of 1/4/0.2. - */ - public LensMagnificationGraphMousePlugin() { - this(MouseEvent.CTRL_MASK); - } - - /** - * Creates an instance with modifier of CTRL_MASK, and the specified zoom parameters. - * @param floor the minimum zoom value - * @param ceiling the maximum zoom value - * @param delta the change in zoom value caused by each mouse event - */ - public LensMagnificationGraphMousePlugin(float floor, float ceiling, float delta) { - this(MouseEvent.CTRL_MASK, floor, ceiling, delta); - } - - /** - * Creates an instance with the specified modifiers and the default min/max/delta zoom values - * of 1/4/0.2. - * @param modifiers the mouse event modifiers to specify - */ - public LensMagnificationGraphMousePlugin(int modifiers) { - this(modifiers, 1.0f, 4.0f, .2f); - } - - /** - * Creates an instance with the specified mouse event modifiers and zoom parameters. - * @param modifiers the mouse event modifiers to specify - * @param floor the minimum zoom value - * @param ceiling the maximum zoom value - * @param delta the change in zoom value caused by each mouse event - */ - public LensMagnificationGraphMousePlugin(int modifiers, float floor, float ceiling, float delta) { - super(modifiers); - this.floor = floor; - this.ceiling = ceiling; - this.delta = delta; - } + protected final float floor; + protected final float ceiling; + protected final float delta; - /** - * override to check equality with a mask - */ - public boolean checkModifiers(MouseEvent e) { - return (e.getModifiers() & modifiers) != 0; - } + /** + * Creates an instance with modifier of CTRL_MASK, and default min/max/delta zoom values of + * 1/4/0.2. + */ + public LensMagnificationGraphMousePlugin() { + this(MouseEvent.CTRL_MASK); + } + + /** + * Creates an instance with modifier of CTRL_MASK, and the specified zoom parameters. + * + * @param floor the minimum zoom value + * @param ceiling the maximum zoom value + * @param delta the change in zoom value caused by each mouse event + */ + public LensMagnificationGraphMousePlugin(float floor, float ceiling, float delta) { + this(MouseEvent.CTRL_MASK, floor, ceiling, delta); + } + + /** + * Creates an instance with the specified modifiers and the default min/max/delta zoom values of + * 1/4/0.2. + * + * @param modifiers the mouse event modifiers to specify + */ + public LensMagnificationGraphMousePlugin(int modifiers) { + this(modifiers, 1.0f, 4.0f, .2f); + } + + /** + * Creates an instance with the specified mouse event modifiers and zoom parameters. + * + * @param modifiers the mouse event modifiers to specify + * @param floor the minimum zoom value + * @param ceiling the maximum zoom value + * @param delta the change in zoom value caused by each mouse event + */ + public LensMagnificationGraphMousePlugin(int modifiers, float floor, float ceiling, float delta) { + super(modifiers); + this.floor = floor; + this.ceiling = ceiling; + this.delta = delta; + } + + /** override to check equality with a mask */ + public boolean checkModifiers(MouseEvent e) { + return (e.getModifiers() & modifiers) != 0; + } - private void changeMagnification(MutableTransformer transformer, float delta) { - if(transformer instanceof LensTransformer) { - LensTransformer ht = (LensTransformer)transformer; - float magnification = ht.getMagnification() + delta; - magnification = Math.max(floor, magnification); - magnification = Math.min(magnification, ceiling); - ht.setMagnification(magnification); - } + private void changeMagnification(MutableTransformer transformer, float delta) { + if (transformer instanceof LensTransformer) { + LensTransformer ht = (LensTransformer) transformer; + float magnification = ht.getMagnification() + delta; + magnification = Math.max(floor, magnification); + magnification = Math.min(magnification, ceiling); + ht.setMagnification(magnification); } - /** - * zoom the display in or out, depending on the direction of the - * mouse wheel motion. - */ - public void mouseWheelMoved(MouseWheelEvent e) { - boolean accepted = checkModifiers(e); - float delta = this.delta; - if(accepted == true) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - MutableTransformer modelTransformer = vv.getRenderContext().getMultiLayerTransformer() - .getTransformer(Layer.LAYOUT); - MutableTransformer viewTransformer = vv.getRenderContext().getMultiLayerTransformer() - .getTransformer(Layer.VIEW); - int amount = e.getWheelRotation(); - if(amount < 0) { - delta = -delta; - } - changeMagnification(modelTransformer, delta); - changeMagnification(viewTransformer, delta); - vv.repaint(); - e.consume(); - } + } + /** zoom the display in or out, depending on the direction of the mouse wheel motion. */ + public void mouseWheelMoved(MouseWheelEvent e) { + boolean accepted = checkModifiers(e); + float delta = this.delta; + if (accepted == true) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + MutableTransformer modelTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + MutableTransformer viewTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); + int amount = e.getWheelRotation(); + if (amount < 0) { + delta = -delta; + } + changeMagnification(modelTransformer, delta); + changeMagnification(viewTransformer, delta); + vv.repaint(); + e.consume(); } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LensTranslatingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LensTranslatingGraphMousePlugin.java index 83d202fb..756504b1 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LensTranslatingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/LensTranslatingGraphMousePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,174 +11,175 @@ */ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.transform.LensTransformer; +import edu.uci.ics.jung.visualization.transform.MutableTransformer; import java.awt.Cursor; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.Point2D; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.transform.LensTransformer; -import edu.uci.ics.jung.visualization.transform.MutableTransformer; - -/** - * Extends TranslatingGraphMousePlugin and adds the capability - * to drag and resize the viewing - * lens in the graph view. Mouse1 in the center moves the lens, - * mouse1 on the edge resizes the lens. The default mouse button and - * modifiers can be overridden in the constructor. - * - * +/** + * Extends TranslatingGraphMousePlugin and adds the capability to drag and resize the viewing lens + * in the graph view. Mouse1 in the center moves the lens, mouse1 on the edge resizes the lens. The + * default mouse button and modifiers can be overridden in the constructor. + * * @author Tom Nelson */ public class LensTranslatingGraphMousePlugin extends TranslatingGraphMousePlugin -implements MouseListener, MouseMotionListener { - - protected boolean dragOnLens; - protected boolean dragOnEdge; - protected double edgeOffset; - /** - * create an instance with default modifiers - */ - public LensTranslatingGraphMousePlugin() { - this(MouseEvent.BUTTON1_MASK); - } - - /** - * create an instance with passed modifer value - * @param modifiers the mouse event modifier to activate this function - */ - public LensTranslatingGraphMousePlugin(int modifiers) { - super(modifiers); + implements MouseListener, MouseMotionListener { + + protected boolean dragOnLens; + protected boolean dragOnEdge; + protected double edgeOffset; + /** create an instance with default modifiers */ + public LensTranslatingGraphMousePlugin() { + this(MouseEvent.BUTTON1_MASK); + } + + /** + * create an instance with passed modifer value + * + * @param modifiers the mouse event modifier to activate this function + */ + public LensTranslatingGraphMousePlugin(int modifiers) { + super(modifiers); + } + + /** + * Check the event modifiers. Set the 'down' point for later use. If this event satisfies the + * modifiers, change the cursor to the system 'move cursor' + * + * @param e the event + */ + public void mousePressed(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + MutableTransformer vt = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); + if (vt instanceof LensTransformer) { + vt = ((LensTransformer) vt).getDelegate(); } - - /** - * Check the event modifiers. Set the 'down' point for later - * use. If this event satisfies the modifiers, change the cursor - * to the system 'move cursor' - * @param e the event - */ - public void mousePressed(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - MutableTransformer vt = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); - if(vt instanceof LensTransformer) { - vt = ((LensTransformer)vt).getDelegate(); - } - Point2D p = vt.inverseTransform(e.getPoint()); - boolean accepted = checkModifiers(e); - if(accepted) { - vv.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); - testViewCenter(vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT), p); - testViewCenter(vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW), p); - vv.repaint(); - } - super.mousePressed(e); + Point2D p = vt.inverseTransform(e.getPoint()); + boolean accepted = checkModifiers(e); + if (accepted) { + vv.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + testViewCenter( + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT), p); + testViewCenter( + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW), p); + vv.repaint(); } - - /** - * called to change the location of the lens - * @param Function - * @param point - */ - private void setViewCenter(MutableTransformer transformer, Point2D point) { - if(transformer instanceof LensTransformer) { - LensTransformer ht = - (LensTransformer)transformer; - ht.setViewCenter(point); - } + super.mousePressed(e); + } + + /** + * called to change the location of the lens + * + * @param Function + * @param point + */ + private void setViewCenter(MutableTransformer transformer, Point2D point) { + if (transformer instanceof LensTransformer) { + LensTransformer ht = (LensTransformer) transformer; + ht.setViewCenter(point); } - - /** - * called to change the radius of the lens - * @param Function - * @param point - */ - private void setViewRadius(MutableTransformer transformer, Point2D point) { - if(transformer instanceof LensTransformer) { - LensTransformer ht = - (LensTransformer)transformer; - double distanceFromCenter = ht.getDistanceFromCenter(point); - ht.setViewRadius(distanceFromCenter+edgeOffset); - } + } + + /** + * called to change the radius of the lens + * + * @param Function + * @param point + */ + private void setViewRadius(MutableTransformer transformer, Point2D point) { + if (transformer instanceof LensTransformer) { + LensTransformer ht = (LensTransformer) transformer; + double distanceFromCenter = ht.getDistanceFromCenter(point); + ht.setViewRadius(distanceFromCenter + edgeOffset); } - - /** - * called to set up translating the lens center or changing the size - * @param Function - * @param point - */ - private void testViewCenter(MutableTransformer transformer, Point2D point) { - if(transformer instanceof LensTransformer) { - LensTransformer ht = - (LensTransformer)transformer; - double distanceFromCenter = ht.getDistanceFromCenter(point); - if(distanceFromCenter < 10) { - ht.setViewCenter(point); - dragOnLens = true; - } else if(Math.abs(distanceFromCenter - ht.getViewRadius()) < 10) { - edgeOffset = ht.getViewRadius() - distanceFromCenter; - ht.setViewRadius(distanceFromCenter+edgeOffset); - dragOnEdge = true; - } - } + } + + /** + * called to set up translating the lens center or changing the size + * + * @param Function + * @param point + */ + private void testViewCenter(MutableTransformer transformer, Point2D point) { + if (transformer instanceof LensTransformer) { + LensTransformer ht = (LensTransformer) transformer; + double distanceFromCenter = ht.getDistanceFromCenter(point); + if (distanceFromCenter < 10) { + ht.setViewCenter(point); + dragOnLens = true; + } else if (Math.abs(distanceFromCenter - ht.getViewRadius()) < 10) { + edgeOffset = ht.getViewRadius() - distanceFromCenter; + ht.setViewRadius(distanceFromCenter + edgeOffset); + dragOnEdge = true; + } } - - /** - * unset the 'down' point and change the cursoe back to the system - * default cursor - */ - public void mouseReleased(MouseEvent e) { - super.mouseReleased(e); - dragOnLens = false; - dragOnEdge = false; - edgeOffset = 0; + } + + /** unset the 'down' point and change the cursoe back to the system default cursor */ + public void mouseReleased(MouseEvent e) { + super.mouseReleased(e); + dragOnLens = false; + dragOnEdge = false; + edgeOffset = 0; + } + + /** + * check the modifiers. If accepted, move or resize the lens according to the dragging of the + * mouse pointer + * + * @param e the event + */ + public void mouseDragged(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + MutableTransformer vt = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); + if (vt instanceof LensTransformer) { + vt = ((LensTransformer) vt).getDelegate(); } - - /** - * check the modifiers. If accepted, move or resize the lens according - * to the dragging of the mouse pointer - * @param e the event - */ - public void mouseDragged(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - MutableTransformer vt = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); - if(vt instanceof LensTransformer) { - vt = ((LensTransformer)vt).getDelegate(); - } - Point2D p = vt.inverseTransform(e.getPoint()); - boolean accepted = checkModifiers(e); - - if(accepted ) { - MutableTransformer modelTransformer = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - vv.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); - if(dragOnLens) { - setViewCenter(modelTransformer, p); - setViewCenter(vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW), p); - e.consume(); - vv.repaint(); - - } else if(dragOnEdge) { - - setViewRadius(modelTransformer, p); - setViewRadius(vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW), p); - e.consume(); - vv.repaint(); - - } else { - - MutableTransformer mt = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - Point2D iq = vt.inverseTransform(down); - iq = mt.inverseTransform(iq); - Point2D ip = vt.inverseTransform(e.getPoint()); - ip = mt.inverseTransform(ip); - float dx = (float) (ip.getX()-iq.getX()); - float dy = (float) (ip.getY()-iq.getY()); - - modelTransformer.translate(dx, dy); - down.x = e.getX(); - down.y = e.getY(); - } - } + Point2D p = vt.inverseTransform(e.getPoint()); + boolean accepted = checkModifiers(e); + + if (accepted) { + MutableTransformer modelTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + vv.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + if (dragOnLens) { + setViewCenter(modelTransformer, p); + setViewCenter( + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW), p); + e.consume(); + vv.repaint(); + + } else if (dragOnEdge) { + + setViewRadius(modelTransformer, p); + setViewRadius( + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW), p); + e.consume(); + vv.repaint(); + + } else { + + MutableTransformer mt = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + Point2D iq = vt.inverseTransform(down); + iq = mt.inverseTransform(iq); + Point2D ip = vt.inverseTransform(e.getPoint()); + ip = mt.inverseTransform(ip); + float dx = (float) (ip.getX() - iq.getX()); + float dy = (float) (ip.getY() - iq.getY()); + + modelTransformer.translate(dx, dy); + down.x = e.getX(); + down.y = e.getY(); + } } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ModalGraphMouse.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ModalGraphMouse.java index cfa70d64..cd161fd0 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ModalGraphMouse.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ModalGraphMouse.java @@ -10,27 +10,26 @@ package edu.uci.ics.jung.visualization.control; -import java.awt.event.ItemListener; - import edu.uci.ics.jung.visualization.VisualizationViewer.GraphMouse; +import java.awt.event.ItemListener; /** * Interface for a GraphMouse that supports modality. - * - * @author Tom Nelson * + * @author Tom Nelson */ public interface ModalGraphMouse extends GraphMouse { - - void setMode(Mode mode); - - /** - * @return Returns the modeListener. - */ - ItemListener getModeListener(); - - /** - */ - enum Mode { TRANSFORMING, PICKING, ANNOTATING, EDITING } - -} \ No newline at end of file + + void setMode(Mode mode); + + /** @return Returns the modeListener. */ + ItemListener getModeListener(); + + /** */ + enum Mode { + TRANSFORMING, + PICKING, + ANNOTATING, + EDITING + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ModalLensGraphMouse.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ModalLensGraphMouse.java index e28b14eb..d0789cda 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ModalLensGraphMouse.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ModalLensGraphMouse.java @@ -17,79 +17,77 @@ import java.awt.event.KeyEvent; /** - * an implementation of the AbstractModalGraphMouse that includes plugins for - * manipulating a view that is using a LensTransformer. - * - * @author Tom Nelson + * an implementation of the AbstractModalGraphMouse that includes plugins for manipulating a view + * that is using a LensTransformer. * + * @author Tom Nelson */ -public class ModalLensGraphMouse extends AbstractModalGraphMouse implements - ModalGraphMouse { +public class ModalLensGraphMouse extends AbstractModalGraphMouse implements ModalGraphMouse { - /** - * not included in the base class - */ - protected LensMagnificationGraphMousePlugin magnificationPlugin; - - public ModalLensGraphMouse() { - this(1.1f, 1/1.1f); - } + /** not included in the base class */ + protected LensMagnificationGraphMousePlugin magnificationPlugin; - public ModalLensGraphMouse(float in, float out) { - this(in, out, new LensMagnificationGraphMousePlugin()); - } + public ModalLensGraphMouse() { + this(1.1f, 1 / 1.1f); + } - public ModalLensGraphMouse(LensMagnificationGraphMousePlugin magnificationPlugin) { - this(1.1f, 1/1.1f, magnificationPlugin); - } - - public ModalLensGraphMouse(float in, float out, LensMagnificationGraphMousePlugin magnificationPlugin) { - super(in,out); - this.in = in; - this.out = out; - this.magnificationPlugin = magnificationPlugin; - loadPlugins(); - setModeKeyListener(new ModeKeyAdapter(this)); - } - - protected void loadPlugins() { - pickingPlugin = new PickingGraphMousePlugin(); - animatedPickingPlugin = new AnimatedPickingGraphMousePlugin(); - translatingPlugin = new LensTranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); - scalingPlugin = new ScalingGraphMousePlugin(new CrossoverScalingControl(), 0, in, out); - rotatingPlugin = new RotatingGraphMousePlugin(); - shearingPlugin = new ShearingGraphMousePlugin(); - - add(magnificationPlugin); - add(scalingPlugin); + public ModalLensGraphMouse(float in, float out) { + this(in, out, new LensMagnificationGraphMousePlugin()); + } - setMode(Mode.TRANSFORMING); - } - public static class ModeKeyAdapter extends KeyAdapter { - private char t = 't'; - private char p = 'p'; - protected ModalGraphMouse graphMouse; + public ModalLensGraphMouse(LensMagnificationGraphMousePlugin magnificationPlugin) { + this(1.1f, 1 / 1.1f, magnificationPlugin); + } + + public ModalLensGraphMouse( + float in, float out, LensMagnificationGraphMousePlugin magnificationPlugin) { + super(in, out); + this.in = in; + this.out = out; + this.magnificationPlugin = magnificationPlugin; + loadPlugins(); + setModeKeyListener(new ModeKeyAdapter(this)); + } - public ModeKeyAdapter(ModalGraphMouse graphMouse) { - this.graphMouse = graphMouse; - } + protected void loadPlugins() { + pickingPlugin = new PickingGraphMousePlugin(); + animatedPickingPlugin = new AnimatedPickingGraphMousePlugin(); + translatingPlugin = new LensTranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); + scalingPlugin = new ScalingGraphMousePlugin(new CrossoverScalingControl(), 0, in, out); + rotatingPlugin = new RotatingGraphMousePlugin(); + shearingPlugin = new ShearingGraphMousePlugin(); - public ModeKeyAdapter(char t, char p, ModalGraphMouse graphMouse) { - this.t = t; - this.p = p; - this.graphMouse = graphMouse; - } - - public void keyTyped(KeyEvent event) { - char keyChar = event.getKeyChar(); - if(keyChar == t) { - ((Component)event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - graphMouse.setMode(Mode.TRANSFORMING); - } else if(keyChar == p) { - ((Component)event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - graphMouse.setMode(Mode.PICKING); - } - } + add(magnificationPlugin); + add(scalingPlugin); + + setMode(Mode.TRANSFORMING); + } + + public static class ModeKeyAdapter extends KeyAdapter { + private char t = 't'; + private char p = 'p'; + protected ModalGraphMouse graphMouse; + + public ModeKeyAdapter(ModalGraphMouse graphMouse) { + this.graphMouse = graphMouse; } + public ModeKeyAdapter(char t, char p, ModalGraphMouse graphMouse) { + this.t = t; + this.p = p; + this.graphMouse = graphMouse; + } + + public void keyTyped(KeyEvent event) { + char keyChar = event.getKeyChar(); + if (keyChar == t) { + ((Component) event.getSource()) + .setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + graphMouse.setMode(Mode.TRANSFORMING); + } else if (keyChar == p) { + ((Component) event.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + graphMouse.setMode(Mode.PICKING); + } + } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ModalSatelliteGraphMouse.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ModalSatelliteGraphMouse.java index 134dde80..d1bfa58e 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ModalSatelliteGraphMouse.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ModalSatelliteGraphMouse.java @@ -11,33 +11,28 @@ package edu.uci.ics.jung.visualization.control; import java.awt.event.InputEvent; -/** - * - * @author Tom Nelson - * - */ +/** @author Tom Nelson */ @SuppressWarnings("rawtypes") -public class ModalSatelliteGraphMouse extends DefaultModalGraphMouse implements - ModalGraphMouse { +public class ModalSatelliteGraphMouse extends DefaultModalGraphMouse implements ModalGraphMouse { + + public ModalSatelliteGraphMouse() { + this(1.1f, 1 / 1.1f); + } + + public ModalSatelliteGraphMouse(float in, float out) { + super(in, out); + } + + protected void loadPlugins() { + pickingPlugin = new PickingGraphMousePlugin(); + animatedPickingPlugin = new SatelliteAnimatedPickingGraphMousePlugin(); + translatingPlugin = new SatelliteTranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); + scalingPlugin = new SatelliteScalingGraphMousePlugin(new CrossoverScalingControl(), 0); + rotatingPlugin = new SatelliteRotatingGraphMousePlugin(); + shearingPlugin = new SatelliteShearingGraphMousePlugin(); - public ModalSatelliteGraphMouse() { - this(1.1f, 1/1.1f); - } + add(scalingPlugin); - public ModalSatelliteGraphMouse(float in, float out) { - super(in, out); - } - - protected void loadPlugins() { - pickingPlugin = new PickingGraphMousePlugin(); - animatedPickingPlugin = new SatelliteAnimatedPickingGraphMousePlugin(); - translatingPlugin = new SatelliteTranslatingGraphMousePlugin(InputEvent.BUTTON1_MASK); - scalingPlugin = new SatelliteScalingGraphMousePlugin(new CrossoverScalingControl(), 0); - rotatingPlugin = new SatelliteRotatingGraphMousePlugin(); - shearingPlugin = new SatelliteShearingGraphMousePlugin(); - - add(scalingPlugin); - - setMode(Mode.TRANSFORMING); - } + setMode(Mode.TRANSFORMING); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/MouseListenerTranslator.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/MouseListenerTranslator.java index 22d880d6..9ee96ec1 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/MouseListenerTranslator.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/MouseListenerTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, The JUNG Authors + * Copyright (c) 2003, The JUNG Authors * * All rights reserved. * @@ -12,78 +12,71 @@ */ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; +import edu.uci.ics.jung.visualization.VisualizationViewer; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; -import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; -import edu.uci.ics.jung.visualization.VisualizationViewer; - /** * This class translates mouse clicks into vertex clicks - * + * * @author danyelf */ public class MouseListenerTranslator extends MouseAdapter { - private VisualizationViewer vv; - private GraphMouseListener gel; + private VisualizationViewer vv; + private GraphMouseListener gel; + + /** + * @param gel listens for mouse events + * @param vv the viewer used for visualization + */ + public MouseListenerTranslator(GraphMouseListener gel, VisualizationViewer vv) { + this.gel = gel; + this.vv = vv; + } - /** - * @param gel listens for mouse events - * @param vv the viewer used for visualization - */ - public MouseListenerTranslator(GraphMouseListener gel, VisualizationViewer vv) { - this.gel = gel; - this.vv = vv; - } - - /** - * Transform the point to the coordinate system in the - * VisualizationViewer, then use either PickSuuport - * (if available) or Layout to find a Vertex - * @param point - * @return - */ - private V getVertex(Point2D point) { - // adjust for scale and offset in the VisualizationViewer - Point2D p = point; - //vv.getRenderContext().getBasicTransformer().inverseViewTransform(point); - NetworkElementAccessor pickSupport = vv.getPickSupport(); -// Layout layout = vv.getGraphLayout(); - V v = null; - if(pickSupport != null) { - v = pickSupport.getNode(p.getX(), p.getY()); - } - return v; - } - /** - * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent) - */ - public void mouseClicked(MouseEvent e) { - V v = getVertex(e.getPoint()); - if ( v != null ) { - gel.graphClicked(v, e ); - } - } + /** + * Transform the point to the coordinate system in the VisualizationViewer, then use either + * PickSuuport (if available) or Layout to find a Vertex + * + * @param point + * @return + */ + private V getVertex(Point2D point) { + // adjust for scale and offset in the VisualizationViewer + Point2D p = point; + //vv.getRenderContext().getBasicTransformer().inverseViewTransform(point); + NetworkElementAccessor pickSupport = vv.getPickSupport(); + // Layout layout = vv.getGraphLayout(); + V v = null; + if (pickSupport != null) { + v = pickSupport.getNode(p.getX(), p.getY()); + } + return v; + } + /** @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent) */ + public void mouseClicked(MouseEvent e) { + V v = getVertex(e.getPoint()); + if (v != null) { + gel.graphClicked(v, e); + } + } - /** - * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent) - */ - public void mousePressed(MouseEvent e) { - V v = getVertex(e.getPoint()); - if ( v != null ) { - gel.graphPressed(v, e ); - } - } + /** @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent) */ + public void mousePressed(MouseEvent e) { + V v = getVertex(e.getPoint()); + if (v != null) { + gel.graphPressed(v, e); + } + } - /** - * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent) - */ - public void mouseReleased(MouseEvent e) { - V v = getVertex(e.getPoint()); - if ( v != null ) { - gel.graphReleased(v, e ); - } - } + /** @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent) */ + public void mouseReleased(MouseEvent e) { + V v = getVertex(e.getPoint()); + if (v != null) { + gel.graphReleased(v, e); + } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/PickingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/PickingGraphMousePlugin.java index 3137609e..66e5974c 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/PickingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/PickingGraphMousePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,6 +11,12 @@ */ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationServer.Paintable; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.picking.PickedState; import java.awt.Color; import java.awt.Cursor; import java.awt.Graphics; @@ -23,352 +29,302 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Collection; - import javax.swing.JComponent; -import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationServer.Paintable; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.picking.PickedState; - -/** - * PickingGraphMousePlugin supports the picking of graph elements - * with the mouse. MouseButtonOne picks a single vertex - * or edge, and MouseButtonTwo adds to the set of selected Vertices - * or EdgeType. If a Vertex is selected and the mouse is dragged while - * on the selected Vertex, then that Vertex will be repositioned to - * follow the mouse until the button is released. - * +/** + * PickingGraphMousePlugin supports the picking of graph elements with the mouse. MouseButtonOne + * picks a single vertex or edge, and MouseButtonTwo adds to the set of selected Vertices or + * EdgeType. If a Vertex is selected and the mouse is dragged while on the selected Vertex, then + * that Vertex will be repositioned to follow the mouse until the button is released. + * * @author Tom Nelson */ public class PickingGraphMousePlugin extends AbstractGraphMousePlugin implements MouseListener, MouseMotionListener { - /** - * the picked Vertex, if any - */ - protected V vertex; - - /** - * the picked Edge, if any - */ - protected E edge; - - /** - * the x distance from the picked vertex center to the mouse point - */ - protected double offsetx; - - /** - * the y distance from the picked vertex center to the mouse point - */ - protected double offsety; - - /** - * controls whether the Vertices may be moved with the mouse - */ - protected boolean locked; - - /** - * additional modifiers for the action of adding to an existing - * selection - */ - protected int addToSelectionModifiers; - - /** - * used to draw a rectangle to contain picked vertices - */ - protected Rectangle2D rect = new Rectangle2D.Float(); - - /** - * the Paintable for the lens picking rectangle - */ - protected Paintable lensPaintable; - - /** - * color for the picking rectangle - */ - protected Color lensColor = Color.cyan; - - /** - * create an instance with default settings - */ - public PickingGraphMousePlugin() { - this(InputEvent.BUTTON1_MASK, InputEvent.BUTTON1_MASK | InputEvent.SHIFT_MASK); - } - - /** - * create an instance with overides - * @param selectionModifiers for primary selection - * @param addToSelectionModifiers for additional selection - */ - public PickingGraphMousePlugin(int selectionModifiers, int addToSelectionModifiers) { - super(selectionModifiers); - this.addToSelectionModifiers = addToSelectionModifiers; - this.lensPaintable = new LensPaintable(); - this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); - } - - /** - * @return Returns the lensColor. - */ - public Color getLensColor() { - return lensColor; + /** the picked Vertex, if any */ + protected V vertex; + + /** the picked Edge, if any */ + protected E edge; + + /** the x distance from the picked vertex center to the mouse point */ + protected double offsetx; + + /** the y distance from the picked vertex center to the mouse point */ + protected double offsety; + + /** controls whether the Vertices may be moved with the mouse */ + protected boolean locked; + + /** additional modifiers for the action of adding to an existing selection */ + protected int addToSelectionModifiers; + + /** used to draw a rectangle to contain picked vertices */ + protected Rectangle2D rect = new Rectangle2D.Float(); + + /** the Paintable for the lens picking rectangle */ + protected Paintable lensPaintable; + + /** color for the picking rectangle */ + protected Color lensColor = Color.cyan; + + /** create an instance with default settings */ + public PickingGraphMousePlugin() { + this(InputEvent.BUTTON1_MASK, InputEvent.BUTTON1_MASK | InputEvent.SHIFT_MASK); + } + + /** + * create an instance with overides + * + * @param selectionModifiers for primary selection + * @param addToSelectionModifiers for additional selection + */ + public PickingGraphMousePlugin(int selectionModifiers, int addToSelectionModifiers) { + super(selectionModifiers); + this.addToSelectionModifiers = addToSelectionModifiers; + this.lensPaintable = new LensPaintable(); + this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); + } + + /** @return Returns the lensColor. */ + public Color getLensColor() { + return lensColor; + } + + /** @param lensColor The lensColor to set. */ + public void setLensColor(Color lensColor) { + this.lensColor = lensColor; + } + + /** + * a Paintable to draw the rectangle used to pick multiple Vertices + * + * @author Tom Nelson + */ + class LensPaintable implements Paintable { + + public void paint(Graphics g) { + Color oldColor = g.getColor(); + g.setColor(lensColor); + ((Graphics2D) g).draw(rect); + g.setColor(oldColor); } - /** - * @param lensColor The lensColor to set. - */ - public void setLensColor(Color lensColor) { - this.lensColor = lensColor; + public boolean useTransform() { + return false; } + } - /** - * a Paintable to draw the rectangle used to pick multiple - * Vertices - * @author Tom Nelson - * - */ - class LensPaintable implements Paintable { - - public void paint(Graphics g) { - Color oldColor = g.getColor(); - g.setColor(lensColor); - ((Graphics2D)g).draw(rect); - g.setColor(oldColor); + /** + * For primary modifiers (default, MouseButton1): pick a single Vertex or Edge that is under the + * mouse pointer. If no Vertex or edge is under the pointer, unselect all picked Vertices and + * edges, and set up to draw a rectangle for multiple selection of contained Vertices. For + * additional selection (default Shift+MouseButton1): Add to the selection, a single Vertex or + * Edge that is under the mouse pointer. If a previously picked Vertex or Edge is under the + * pointer, it is un-picked. If no vertex or Edge is under the pointer, set up to draw a multiple + * selection rectangle (as above) but do not unpick previously picked elements. + * + * @param e the event + */ + @SuppressWarnings("unchecked") + public void mousePressed(MouseEvent e) { + down = e.getPoint(); + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + NetworkElementAccessor pickSupport = vv.getPickSupport(); + PickedState pickedVertexState = vv.getPickedVertexState(); + PickedState pickedEdgeState = vv.getPickedEdgeState(); + if (pickSupport != null && pickedVertexState != null) { + Layout layout = vv.getGraphLayout(); + if (e.getModifiers() == modifiers) { + rect.setFrameFromDiagonal(down, down); + // p is the screen point for the mouse event + Point2D ip = e.getPoint(); + + vertex = pickSupport.getNode(ip.getX(), ip.getY()); + if (vertex != null) { + if (pickedVertexState.isPicked(vertex) == false) { + pickedVertexState.clear(); + pickedVertexState.pick(vertex, true); + } + // layout.getLocation applies the layout Function so + // q is transformed by the layout Function only + Point2D q = layout.apply(vertex); + // transform the mouse point to graph coordinate system + Point2D gp = + vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.LAYOUT, ip); + + offsetx = (float) (gp.getX() - q.getX()); + offsety = (float) (gp.getY() - q.getY()); + } else if ((edge = pickSupport.getEdge(ip.getX(), ip.getY())) != null) { + pickedEdgeState.clear(); + pickedEdgeState.pick(edge, true); + } else { + vv.addPostRenderPaintable(lensPaintable); + pickedEdgeState.clear(); + pickedVertexState.clear(); } - public boolean useTransform() { - return false; + } else if (e.getModifiers() == addToSelectionModifiers) { + vv.addPostRenderPaintable(lensPaintable); + rect.setFrameFromDiagonal(down, down); + Point2D ip = e.getPoint(); + vertex = pickSupport.getNode(ip.getX(), ip.getY()); + if (vertex != null) { + boolean wasThere = pickedVertexState.pick(vertex, !pickedVertexState.isPicked(vertex)); + if (wasThere) { + vertex = null; + } else { + + // layout.getLocation applies the layout Function so + // q is transformed by the layout Function only + Point2D q = layout.apply(vertex); + // translate mouse point to graph coord system + Point2D gp = + vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.LAYOUT, ip); + + offsetx = (float) (gp.getX() - q.getX()); + offsety = (float) (gp.getY() - q.getY()); + } + } else if ((edge = pickSupport.getEdge(ip.getX(), ip.getY())) != null) { + pickedEdgeState.pick(edge, !pickedEdgeState.isPicked(edge)); } + } } + if (vertex != null) e.consume(); + } + + /** + * If the mouse is dragging a rectangle, pick the Vertices contained in that rectangle + * + *

            clean up settings from mousePressed + */ + @SuppressWarnings("unchecked") + public void mouseReleased(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + if (e.getModifiers() == modifiers) { + if (down != null) { + Point2D out = e.getPoint(); - /** - * For primary modifiers (default, MouseButton1): - * pick a single Vertex or Edge that - * is under the mouse pointer. If no Vertex or edge is under - * the pointer, unselect all picked Vertices and edges, and - * set up to draw a rectangle for multiple selection - * of contained Vertices. - * For additional selection (default Shift+MouseButton1): - * Add to the selection, a single Vertex or Edge that is - * under the mouse pointer. If a previously picked Vertex - * or Edge is under the pointer, it is un-picked. - * If no vertex or Edge is under the pointer, set up - * to draw a multiple selection rectangle (as above) - * but do not unpick previously picked elements. - * - * @param e the event - */ - @SuppressWarnings("unchecked") - public void mousePressed(MouseEvent e) { - down = e.getPoint(); - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - NetworkElementAccessor pickSupport = vv.getPickSupport(); - PickedState pickedVertexState = vv.getPickedVertexState(); - PickedState pickedEdgeState = vv.getPickedEdgeState(); - if(pickSupport != null && pickedVertexState != null) { - Layout layout = vv.getGraphLayout(); - if(e.getModifiers() == modifiers) { - rect.setFrameFromDiagonal(down,down); - // p is the screen point for the mouse event - Point2D ip = e.getPoint(); - - vertex = pickSupport.getNode(ip.getX(), ip.getY()); - if(vertex != null) { - if(pickedVertexState.isPicked(vertex) == false) { - pickedVertexState.clear(); - pickedVertexState.pick(vertex, true); - } - // layout.getLocation applies the layout Function so - // q is transformed by the layout Function only - Point2D q = layout.apply(vertex); - // transform the mouse point to graph coordinate system - Point2D gp = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.LAYOUT, ip); - - offsetx = (float) (gp.getX()-q.getX()); - offsety = (float) (gp.getY()-q.getY()); - } else if((edge = pickSupport.getEdge(ip.getX(), ip.getY())) != null) { - pickedEdgeState.clear(); - pickedEdgeState.pick(edge, true); - } else { - vv.addPostRenderPaintable(lensPaintable); - pickedEdgeState.clear(); - pickedVertexState.clear(); - } - - } else if(e.getModifiers() == addToSelectionModifiers) { - vv.addPostRenderPaintable(lensPaintable); - rect.setFrameFromDiagonal(down,down); - Point2D ip = e.getPoint(); - vertex = pickSupport.getNode(ip.getX(), ip.getY()); - if(vertex != null) { - boolean wasThere = pickedVertexState.pick(vertex, !pickedVertexState.isPicked(vertex)); - if(wasThere) { - vertex = null; - } else { - - // layout.getLocation applies the layout Function so - // q is transformed by the layout Function only - Point2D q = layout.apply(vertex); - // translate mouse point to graph coord system - Point2D gp = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.LAYOUT, ip); - - offsetx = (float) (gp.getX()-q.getX()); - offsety = (float) (gp.getY()-q.getY()); - } - } else if((edge = pickSupport.getEdge(ip.getX(), ip.getY())) != null) { - pickedEdgeState.pick(edge, !pickedEdgeState.isPicked(edge)); - } - } + if (vertex == null && heyThatsTooClose(down, out, 5) == false) { + pickContainedVertices(vv, down, out, true); } - if(vertex != null) e.consume(); - } + } + } else if (e.getModifiers() == this.addToSelectionModifiers) { + if (down != null) { + Point2D out = e.getPoint(); - /** - * If the mouse is dragging a rectangle, pick the - * Vertices contained in that rectangle - * - * clean up settings from mousePressed - */ - @SuppressWarnings("unchecked") - public void mouseReleased(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - if(e.getModifiers() == modifiers) { - if(down != null) { - Point2D out = e.getPoint(); - - if(vertex == null && heyThatsTooClose(down, out, 5) == false) { - pickContainedVertices(vv, down, out, true); - } - } - } else if(e.getModifiers() == this.addToSelectionModifiers) { - if(down != null) { - Point2D out = e.getPoint(); - - if(vertex == null && heyThatsTooClose(down,out,5) == false) { - pickContainedVertices(vv, down, out, false); - } - } + if (vertex == null && heyThatsTooClose(down, out, 5) == false) { + pickContainedVertices(vv, down, out, false); } - down = null; - vertex = null; - edge = null; - rect.setFrame(0,0,0,0); - vv.removePostRenderPaintable(lensPaintable); - vv.repaint(); + } } - - /** - * If the mouse is over a picked vertex, drag all picked - * vertices with the mouse. - * If the mouse is not over a Vertex, draw the rectangle - * to select multiple Vertices - * - */ - @SuppressWarnings("unchecked") - public void mouseDragged(MouseEvent e) { - if(locked == false) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - if(vertex != null) { - Point p = e.getPoint(); - Point2D graphPoint = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(p); - Point2D graphDown = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(down); - Layout layout = vv.getGraphLayout(); - double dx = graphPoint.getX()-graphDown.getX(); - double dy = graphPoint.getY()-graphDown.getY(); - PickedState ps = vv.getPickedVertexState(); - - for(V v : ps.getPicked()) { - Point2D vp = layout.apply(v); - vp.setLocation(vp.getX()+dx, vp.getY()+dy); - layout.setLocation(v, vp); - } - down = p; - - } else { - Point2D out = e.getPoint(); - if(e.getModifiers() == this.addToSelectionModifiers || - e.getModifiers() == modifiers) { - rect.setFrameFromDiagonal(down,out); - } - } - if(vertex != null) e.consume(); - vv.repaint(); + down = null; + vertex = null; + edge = null; + rect.setFrame(0, 0, 0, 0); + vv.removePostRenderPaintable(lensPaintable); + vv.repaint(); + } + + /** + * If the mouse is over a picked vertex, drag all picked vertices with the mouse. If the mouse is + * not over a Vertex, draw the rectangle to select multiple Vertices + */ + @SuppressWarnings("unchecked") + public void mouseDragged(MouseEvent e) { + if (locked == false) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + if (vertex != null) { + Point p = e.getPoint(); + Point2D graphPoint = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(p); + Point2D graphDown = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(down); + Layout layout = vv.getGraphLayout(); + double dx = graphPoint.getX() - graphDown.getX(); + double dy = graphPoint.getY() - graphDown.getY(); + PickedState ps = vv.getPickedVertexState(); + + for (V v : ps.getPicked()) { + Point2D vp = layout.apply(v); + vp.setLocation(vp.getX() + dx, vp.getY() + dy); + layout.setLocation(v, vp); } - } - - /** - * rejects picking if the rectangle is too small, like - * if the user meant to select one vertex but moved the - * mouse slightly - * @param p - * @param q - * @param min - * @return - */ - private boolean heyThatsTooClose(Point2D p, Point2D q, double min) { - return Math.abs(p.getX()-q.getX()) < min && - Math.abs(p.getY()-q.getY()) < min; - } - - /** - * pick the vertices inside the rectangle created from points 'down' and 'out' (two diagonally - * opposed corners of the rectangle) - * - * @param vv the viewer containing the layout and picked state - * @param down one corner of the rectangle - * @param out the other corner of the rectangle - * @param clear whether to reset existing picked state - */ - protected void pickContainedVertices(VisualizationViewer vv, Point2D down, Point2D out, boolean clear) { - PickedState pickedVertexState = vv.getPickedVertexState(); - - Rectangle2D pickRectangle = new Rectangle2D.Double(); - pickRectangle.setFrameFromDiagonal(down,out); - - if(pickedVertexState != null) { - if(clear) { - pickedVertexState.clear(); - } - NetworkElementAccessor pickSupport = vv.getPickSupport(); - - Collection picked = pickSupport.getNodes(pickRectangle); - for(V v : picked) { - pickedVertexState.pick(v, true); - } + down = p; + + } else { + Point2D out = e.getPoint(); + if (e.getModifiers() == this.addToSelectionModifiers || e.getModifiers() == modifiers) { + rect.setFrameFromDiagonal(down, out); } + } + if (vertex != null) e.consume(); + vv.repaint(); } + } - public void mouseClicked(MouseEvent e) { - } + /** + * rejects picking if the rectangle is too small, like if the user meant to select one vertex but + * moved the mouse slightly + * + * @param p + * @param q + * @param min + * @return + */ + private boolean heyThatsTooClose(Point2D p, Point2D q, double min) { + return Math.abs(p.getX() - q.getX()) < min && Math.abs(p.getY() - q.getY()) < min; + } - public void mouseEntered(MouseEvent e) { - JComponent c = (JComponent)e.getSource(); - c.setCursor(cursor); - } + /** + * pick the vertices inside the rectangle created from points 'down' and 'out' (two diagonally + * opposed corners of the rectangle) + * + * @param vv the viewer containing the layout and picked state + * @param down one corner of the rectangle + * @param out the other corner of the rectangle + * @param clear whether to reset existing picked state + */ + protected void pickContainedVertices( + VisualizationViewer vv, Point2D down, Point2D out, boolean clear) { + PickedState pickedVertexState = vv.getPickedVertexState(); - public void mouseExited(MouseEvent e) { - JComponent c = (JComponent)e.getSource(); - c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } + Rectangle2D pickRectangle = new Rectangle2D.Double(); + pickRectangle.setFrameFromDiagonal(down, out); - public void mouseMoved(MouseEvent e) { - } + if (pickedVertexState != null) { + if (clear) { + pickedVertexState.clear(); + } + NetworkElementAccessor pickSupport = vv.getPickSupport(); - /** - * @return Returns the locked. - */ - public boolean isLocked() { - return locked; + Collection picked = pickSupport.getNodes(pickRectangle); + for (V v : picked) { + pickedVertexState.pick(v, true); + } } + } - /** - * @param locked The locked to set. - */ - public void setLocked(boolean locked) { - this.locked = locked; - } + public void mouseClicked(MouseEvent e) {} + + public void mouseEntered(MouseEvent e) { + JComponent c = (JComponent) e.getSource(); + c.setCursor(cursor); + } + + public void mouseExited(MouseEvent e) { + JComponent c = (JComponent) e.getSource(); + c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + + public void mouseMoved(MouseEvent e) {} + + /** @return Returns the locked. */ + public boolean isLocked() { + return locked; + } + + /** @param locked The locked to set. */ + public void setLocked(boolean locked) { + this.locked = locked; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/PluggableGraphMouse.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/PluggableGraphMouse.java index 879427d5..36cdefb5 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/PluggableGraphMouse.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/PluggableGraphMouse.java @@ -10,6 +10,7 @@ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.visualization.VisualizationViewer; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; @@ -18,136 +19,134 @@ import java.util.LinkedHashSet; import java.util.Set; -import edu.uci.ics.jung.visualization.VisualizationViewer; - /** * a GraphMouse that accepts plugins for various mouse events. - * - * @author Tom Nelson - * * + * @author Tom Nelson */ public class PluggableGraphMouse implements VisualizationViewer.GraphMouse { - MouseListener[] mouseListeners; - MouseMotionListener[] mouseMotionListeners; - MouseWheelListener[] mouseWheelListeners; - Set mousePluginList = new LinkedHashSet(); - Set mouseMotionPluginList = new LinkedHashSet(); - Set mouseWheelPluginList = new LinkedHashSet(); - - public void add(GraphMousePlugin plugin) { - if(plugin instanceof MouseListener) { - mousePluginList.add(plugin); - mouseListeners = null; - } - if(plugin instanceof MouseMotionListener) { - mouseMotionPluginList.add((MouseMotionListener)plugin); - mouseMotionListeners = null; - } - if(plugin instanceof MouseWheelListener) { - mouseWheelPluginList.add((MouseWheelListener)plugin); - mouseWheelListeners = null; - } - } - - public void remove(GraphMousePlugin plugin) { - if(plugin instanceof MouseListener) { - boolean wasThere = mousePluginList.remove(plugin); - if(wasThere) mouseListeners = null; - } - if(plugin instanceof MouseMotionListener) { - boolean wasThere = mouseMotionPluginList.remove(plugin); - if(wasThere) mouseMotionListeners = null; - } - if(plugin instanceof MouseWheelListener) { - boolean wasThere = mouseWheelPluginList.remove(plugin); - if(wasThere) mouseWheelListeners = null; - } - } - - private void checkMouseListeners() { - if(mouseListeners == null) { - mouseListeners = (MouseListener[]) - mousePluginList.toArray(new MouseListener[mousePluginList.size()]); - } - } - - private void checkMouseMotionListeners() { - if(mouseMotionListeners == null){ - mouseMotionListeners = (MouseMotionListener[]) - mouseMotionPluginList.toArray(new MouseMotionListener[mouseMotionPluginList.size()]); - } - } - - private void checkMouseWheelListeners() { - if(mouseWheelListeners == null) { - mouseWheelListeners = (MouseWheelListener[]) - mouseWheelPluginList.toArray(new MouseWheelListener[mouseWheelPluginList.size()]); - } - } - - public void mouseClicked(MouseEvent e) { - checkMouseListeners(); - for(int i=0; i mousePluginList = new LinkedHashSet(); + Set mouseMotionPluginList = new LinkedHashSet(); + Set mouseWheelPluginList = new LinkedHashSet(); + + public void add(GraphMousePlugin plugin) { + if (plugin instanceof MouseListener) { + mousePluginList.add(plugin); + mouseListeners = null; + } + if (plugin instanceof MouseMotionListener) { + mouseMotionPluginList.add((MouseMotionListener) plugin); + mouseMotionListeners = null; + } + if (plugin instanceof MouseWheelListener) { + mouseWheelPluginList.add((MouseWheelListener) plugin); + mouseWheelListeners = null; + } + } + + public void remove(GraphMousePlugin plugin) { + if (plugin instanceof MouseListener) { + boolean wasThere = mousePluginList.remove(plugin); + if (wasThere) mouseListeners = null; + } + if (plugin instanceof MouseMotionListener) { + boolean wasThere = mouseMotionPluginList.remove(plugin); + if (wasThere) mouseMotionListeners = null; + } + if (plugin instanceof MouseWheelListener) { + boolean wasThere = mouseWheelPluginList.remove(plugin); + if (wasThere) mouseWheelListeners = null; + } + } + + private void checkMouseListeners() { + if (mouseListeners == null) { + mouseListeners = + (MouseListener[]) mousePluginList.toArray(new MouseListener[mousePluginList.size()]); + } + } + + private void checkMouseMotionListeners() { + if (mouseMotionListeners == null) { + mouseMotionListeners = + (MouseMotionListener[]) + mouseMotionPluginList.toArray(new MouseMotionListener[mouseMotionPluginList.size()]); + } + } + + private void checkMouseWheelListeners() { + if (mouseWheelListeners == null) { + mouseWheelListeners = + (MouseWheelListener[]) + mouseWheelPluginList.toArray(new MouseWheelListener[mouseWheelPluginList.size()]); + } + } + + public void mouseClicked(MouseEvent e) { + checkMouseListeners(); + for (int i = 0; i < mouseListeners.length; i++) { + mouseListeners[i].mouseClicked(e); + if (e.isConsumed()) break; + } + } + + public void mousePressed(MouseEvent e) { + checkMouseListeners(); + for (int i = 0; i < mouseListeners.length; i++) { + mouseListeners[i].mousePressed(e); + if (e.isConsumed()) break; + } + } + + public void mouseReleased(MouseEvent e) { + checkMouseListeners(); + for (int i = 0; i < mouseListeners.length; i++) { + mouseListeners[i].mouseReleased(e); + if (e.isConsumed()) break; + } + } + + public void mouseEntered(MouseEvent e) { + checkMouseListeners(); + for (int i = 0; i < mouseListeners.length; i++) { + mouseListeners[i].mouseEntered(e); + if (e.isConsumed()) break; + } + } + + public void mouseExited(MouseEvent e) { + checkMouseListeners(); + for (int i = 0; i < mouseListeners.length; i++) { + mouseListeners[i].mouseExited(e); + if (e.isConsumed()) break; + } + } + + public void mouseDragged(MouseEvent e) { + checkMouseMotionListeners(); + for (int i = 0; i < mouseMotionListeners.length; i++) { + mouseMotionListeners[i].mouseDragged(e); + if (e.isConsumed()) break; + } + } + + public void mouseMoved(MouseEvent e) { + checkMouseMotionListeners(); + for (int i = 0; i < mouseMotionListeners.length; i++) { + mouseMotionListeners[i].mouseMoved(e); + if (e.isConsumed()) break; + } + } + + public void mouseWheelMoved(MouseWheelEvent e) { + checkMouseWheelListeners(); + for (int i = 0; i < mouseWheelListeners.length; i++) { + mouseWheelListeners[i].mouseWheelMoved(e); + if (e.isConsumed()) break; } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/RotatingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/RotatingGraphMousePlugin.java index edb6fc71..7977de44 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/RotatingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/RotatingGraphMousePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,6 +11,9 @@ */ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.transform.MutableTransformer; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Cursor; @@ -26,172 +29,162 @@ import java.awt.image.BufferedImage; import java.util.Collections; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.transform.MutableTransformer; - -/** - * RotatingGraphMouse provides the abiity to rotate the graph using - * the mouse. By default, it is activated by mouse button one drag - * with the shift key pressed. The modifiers can be overridden so that - * a different mouse/key combination activates the rotation - * +/** + * RotatingGraphMouse provides the abiity to rotate the graph using the mouse. By default, it is + * activated by mouse button one drag with the shift key pressed. The modifiers can be overridden so + * that a different mouse/key combination activates the rotation + * * @author Tom Nelson */ public class RotatingGraphMousePlugin extends AbstractGraphMousePlugin implements MouseListener, MouseMotionListener { - /** - * create an instance with default modifier values - */ - public RotatingGraphMousePlugin() { - this(MouseEvent.BUTTON1_MASK | MouseEvent.SHIFT_MASK); - } - - /** - * create an instance with passed zoom in/out values - * @param modifiers the event modifiers to trigger rotation - */ - public RotatingGraphMousePlugin(int modifiers) { - super(modifiers); - Dimension cd = Toolkit.getDefaultToolkit().getBestCursorSize(16,16); - BufferedImage cursorImage = - new BufferedImage(cd.width,cd.height,BufferedImage.TYPE_INT_ARGB); - Graphics2D g = cursorImage.createGraphics(); - g.addRenderingHints(Collections.singletonMap(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); - g.setColor(new Color(0,0,0,0)); - g.fillRect(0,0,16,16); - - int left = 0; - int top = 0; - int right = 15; - int bottom = 15; - - g.setColor(Color.white); - g.setStroke(new BasicStroke(3)); - // top bent line - g.drawLine(left+2,top+6,right/2+1,top); - g.drawLine(right/2+1,top,right-2,top+5); - // bottom bent line - g.drawLine(left+2,bottom-6,right/2,bottom); - g.drawLine(right/2,bottom,right-2,bottom-6); - // top arrow - g.drawLine(left+2,top+6,left+5,top+6); - g.drawLine(left+2,top+6,left+2,top+3); - // bottom arrow - g.drawLine(right-2,bottom-6,right-6,bottom-6); - g.drawLine(right-2, bottom-6,right-2,bottom-3); - - - g.setColor(Color.black); - g.setStroke(new BasicStroke(1)); - // top bent line - g.drawLine(left+2,top+6,right/2+1,top); - g.drawLine(right/2+1,top,right-2,top+5); - // bottom bent line - g.drawLine(left+2,bottom-6,right/2,bottom); - g.drawLine(right/2,bottom,right-2,bottom-6); - // top arrow - g.drawLine(left+2,top+6,left+5,top+6); - g.drawLine(left+2,top+6,left+2,top+3); - // bottom arrow - g.drawLine(right-2,bottom-6,right-6,bottom-6); - g.drawLine(right-2, bottom-6,right-2,bottom-3); - - g.dispose(); - - cursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(), "RotateCursor"); - } - - /** - * save the 'down' point and check the modifiers. If the - * modifiers are accepted, set the cursor to the 'hand' cursor - * @param e the event - */ - public void mousePressed(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - boolean accepted = checkModifiers(e); - down = e.getPoint(); - if(accepted) { - vv.setCursor(cursor); - } + /** create an instance with default modifier values */ + public RotatingGraphMousePlugin() { + this(MouseEvent.BUTTON1_MASK | MouseEvent.SHIFT_MASK); + } + + /** + * create an instance with passed zoom in/out values + * + * @param modifiers the event modifiers to trigger rotation + */ + public RotatingGraphMousePlugin(int modifiers) { + super(modifiers); + Dimension cd = Toolkit.getDefaultToolkit().getBestCursorSize(16, 16); + BufferedImage cursorImage = new BufferedImage(cd.width, cd.height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = cursorImage.createGraphics(); + g.addRenderingHints( + Collections.singletonMap( + RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); + g.setColor(new Color(0, 0, 0, 0)); + g.fillRect(0, 0, 16, 16); + + int left = 0; + int top = 0; + int right = 15; + int bottom = 15; + + g.setColor(Color.white); + g.setStroke(new BasicStroke(3)); + // top bent line + g.drawLine(left + 2, top + 6, right / 2 + 1, top); + g.drawLine(right / 2 + 1, top, right - 2, top + 5); + // bottom bent line + g.drawLine(left + 2, bottom - 6, right / 2, bottom); + g.drawLine(right / 2, bottom, right - 2, bottom - 6); + // top arrow + g.drawLine(left + 2, top + 6, left + 5, top + 6); + g.drawLine(left + 2, top + 6, left + 2, top + 3); + // bottom arrow + g.drawLine(right - 2, bottom - 6, right - 6, bottom - 6); + g.drawLine(right - 2, bottom - 6, right - 2, bottom - 3); + + g.setColor(Color.black); + g.setStroke(new BasicStroke(1)); + // top bent line + g.drawLine(left + 2, top + 6, right / 2 + 1, top); + g.drawLine(right / 2 + 1, top, right - 2, top + 5); + // bottom bent line + g.drawLine(left + 2, bottom - 6, right / 2, bottom); + g.drawLine(right / 2, bottom, right - 2, bottom - 6); + // top arrow + g.drawLine(left + 2, top + 6, left + 5, top + 6); + g.drawLine(left + 2, top + 6, left + 2, top + 3); + // bottom arrow + g.drawLine(right - 2, bottom - 6, right - 6, bottom - 6); + g.drawLine(right - 2, bottom - 6, right - 2, bottom - 3); + + g.dispose(); + + cursor = + Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(), "RotateCursor"); + } + + /** + * save the 'down' point and check the modifiers. If the modifiers are accepted, set the cursor to + * the 'hand' cursor + * + * @param e the event + */ + public void mousePressed(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + boolean accepted = checkModifiers(e); + down = e.getPoint(); + if (accepted) { + vv.setCursor(cursor); } - - /** - * unset the down point and change the cursor back to the default - */ - public void mouseReleased(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - down = null; - vv.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + + /** unset the down point and change the cursor back to the default */ + public void mouseReleased(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + down = null; + vv.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + + /** check the modifiers. If accepted, use the mouse drag motion to rotate the graph */ + public void mouseDragged(MouseEvent e) { + if (down == null) return; + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + boolean accepted = checkModifiers(e); + if (accepted) { + MutableTransformer modelTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + // rotate + vv.setCursor(cursor); + + Point2D center = vv.getCenter(); + Point2D q = down; + Point2D p = e.getPoint(); + Point2D v1 = new Point2D.Double(center.getX() - p.getX(), center.getY() - p.getY()); + Point2D v2 = new Point2D.Double(center.getX() - q.getX(), center.getY() - q.getY()); + double theta = angleBetween(v1, v2); + modelTransformer.rotate( + theta, + vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, center)); + down.x = e.getX(); + down.y = e.getY(); + + e.consume(); } - - /** - * check the modifiers. If accepted, use the mouse drag motion - * to rotate the graph - */ - public void mouseDragged(MouseEvent e) { - if(down == null) return; - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - boolean accepted = checkModifiers(e); - if(accepted) { - MutableTransformer modelTransformer = - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - // rotate - vv.setCursor(cursor); - - Point2D center = vv.getCenter(); - Point2D q = down; - Point2D p = e.getPoint(); - Point2D v1 = new Point2D.Double(center.getX()-p.getX(), center.getY()-p.getY()); - Point2D v2 = new Point2D.Double(center.getX()-q.getX(), center.getY()-q.getY()); - double theta = angleBetween(v1, v2); - modelTransformer.rotate(theta, vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, center)); - down.x = e.getX(); - down.y = e.getY(); - - e.consume(); - } + } + + /** + * Returns the angle between two vectors from the origin to points v1 and v2. + * + * @param v1 the first point + * @param v2 the second point + * @return the angle between two vectors from the origin through points v1 and v2 + */ + protected double angleBetween(Point2D v1, Point2D v2) { + double x1 = v1.getX(); + double y1 = v1.getY(); + double x2 = v2.getX(); + double y2 = v2.getY(); + // cross product for direction + double cross = x1 * y2 - x2 * y1; + int cw = 1; + if (cross > 0) { + cw = -1; } - - /** - * Returns the angle between two vectors from the origin - * to points v1 and v2. - * @param v1 the first point - * @param v2 the second point - * @return the angle between two vectors from the origin through points v1 and v2 - */ - protected double angleBetween(Point2D v1, Point2D v2) { - double x1 = v1.getX(); - double y1 = v1.getY(); - double x2 = v2.getX(); - double y2 = v2.getY(); - // cross product for direction - double cross = x1*y2 - x2*y1; - int cw = 1; - if(cross > 0) { - cw = -1; - } - // dot product for angle - double angle = - cw*Math.acos( ( x1*x2 + y1*y2 ) / - ( Math.sqrt( x1*x1 + y1*y1 ) * - Math.sqrt( x2*x2 + y2*y2 ) ) ); - if(Double.isNaN(angle)) { - angle = 0; - } - return angle; + // dot product for angle + double angle = + cw + * Math.acos( + (x1 * x2 + y1 * y2) + / (Math.sqrt(x1 * x1 + y1 * y1) * Math.sqrt(x2 * x2 + y2 * y2))); + if (Double.isNaN(angle)) { + angle = 0; } + return angle; + } - public void mouseClicked(MouseEvent e) { - } + public void mouseClicked(MouseEvent e) {} - public void mouseEntered(MouseEvent e) { - } + public void mouseEntered(MouseEvent e) {} - public void mouseExited(MouseEvent e) { - } + public void mouseExited(MouseEvent e) {} - public void mouseMoved(MouseEvent e) { - } + public void mouseMoved(MouseEvent e) {} } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteAnimatedPickingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteAnimatedPickingGraphMousePlugin.java index 603dd446..8e343c59 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteAnimatedPickingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteAnimatedPickingGraphMousePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,77 +11,76 @@ */ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationViewer; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.Point2D; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationViewer; - -/** - * A version of the AnimatedPickingGraphMousePlugin that is for - * the SatelliteVisualizationViewer. The difference it that when - * you pick a Vertex in the Satellite View, the 'master view' is +/** + * A version of the AnimatedPickingGraphMousePlugin that is for the SatelliteVisualizationViewer. + * The difference it that when you pick a Vertex in the Satellite View, the 'master view' is * translated to move that Vertex to the center. + * * @see AnimatedPickingGraphMousePlugin * @author Tom Nelson */ -public class SatelliteAnimatedPickingGraphMousePlugin extends AnimatedPickingGraphMousePlugin - implements MouseListener, MouseMotionListener { +public class SatelliteAnimatedPickingGraphMousePlugin + extends AnimatedPickingGraphMousePlugin implements MouseListener, MouseMotionListener { - /** - * create an instance - * - */ - public SatelliteAnimatedPickingGraphMousePlugin() { - this(InputEvent.BUTTON1_MASK | InputEvent.CTRL_MASK); - } + /** create an instance */ + public SatelliteAnimatedPickingGraphMousePlugin() { + this(InputEvent.BUTTON1_MASK | InputEvent.CTRL_MASK); + } - public SatelliteAnimatedPickingGraphMousePlugin(int selectionModifiers) { - super(selectionModifiers); - } + public SatelliteAnimatedPickingGraphMousePlugin(int selectionModifiers) { + super(selectionModifiers); + } - /** - * override subclass method to translate the master view instead - * of this satellite view - * - */ - @SuppressWarnings("unchecked") - public void mouseReleased(MouseEvent e) { - if (e.getModifiers() == modifiers) { - final VisualizationViewer vv = (VisualizationViewer) e.getSource(); - if (vv instanceof SatelliteVisualizationViewer) { - final VisualizationViewer vvMaster = - ((SatelliteVisualizationViewer) vv).getMaster(); + /** override subclass method to translate the master view instead of this satellite view */ + @SuppressWarnings("unchecked") + public void mouseReleased(MouseEvent e) { + if (e.getModifiers() == modifiers) { + final VisualizationViewer vv = (VisualizationViewer) e.getSource(); + if (vv instanceof SatelliteVisualizationViewer) { + final VisualizationViewer vvMaster = + ((SatelliteVisualizationViewer) vv).getMaster(); - if (vertex != null) { - Layout layout = vvMaster.getGraphLayout(); - Point2D q = layout.apply(vertex); - Point2D lvc = - vvMaster.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.LAYOUT, vvMaster.getCenter()); - final double dx = (lvc.getX() - q.getX()) / 10; - final double dy = (lvc.getY() - q.getY()) / 10; + if (vertex != null) { + Layout layout = vvMaster.getGraphLayout(); + Point2D q = layout.apply(vertex); + Point2D lvc = + vvMaster + .getRenderContext() + .getMultiLayerTransformer() + .inverseTransform(Layer.LAYOUT, vvMaster.getCenter()); + final double dx = (lvc.getX() - q.getX()) / 10; + final double dy = (lvc.getY() - q.getY()) / 10; - Runnable animator = new Runnable() { + Runnable animator = + new Runnable() { - public void run() { - for (int i = 0; i < 10; i++) { - vvMaster.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).translate(dx, - dy); - try { - Thread.sleep(100); - } catch (InterruptedException ex) { - } - } - } - }; - Thread thread = new Thread(animator); - thread.start(); - } - } - } - } + public void run() { + for (int i = 0; i < 10; i++) { + vvMaster + .getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .translate(dx, dy); + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + } + } + } + }; + Thread thread = new Thread(animator); + thread.start(); + } + } + } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteRotatingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteRotatingGraphMousePlugin.java index 20434bb3..2f8ca1ff 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteRotatingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteRotatingGraphMousePlugin.java @@ -10,64 +10,71 @@ package edu.uci.ics.jung.visualization.control; -import java.awt.event.MouseEvent; -import java.awt.geom.Point2D; - import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.VisualizationViewer; import edu.uci.ics.jung.visualization.transform.MutableTransformer; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; /** - * Mouse events in the SatelliteView that match the modifiers - * will cause the Main view to rotate - * @see RotatingGraphMousePlugin - * @author Tom Nelson + * Mouse events in the SatelliteView that match the modifiers will cause the Main view to rotate * + * @see RotatingGraphMousePlugin + * @author Tom Nelson */ public class SatelliteRotatingGraphMousePlugin extends RotatingGraphMousePlugin { - public SatelliteRotatingGraphMousePlugin() { - super(); - } + public SatelliteRotatingGraphMousePlugin() { + super(); + } - public SatelliteRotatingGraphMousePlugin(int modifiers) { - super(modifiers); - } - /** - * check the modifiers. If accepted, use the mouse drag motion - * to rotate the graph in the master view - */ - public void mouseDragged(MouseEvent e) { - if(down == null) return; - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - boolean accepted = checkModifiers(e); - if(accepted) { - if(vv instanceof SatelliteVisualizationViewer) { - VisualizationViewer vvMaster = - ((SatelliteVisualizationViewer)vv).getMaster(); - - MutableTransformer modelTransformerMaster = - vvMaster.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + public SatelliteRotatingGraphMousePlugin(int modifiers) { + super(modifiers); + } + /** + * check the modifiers. If accepted, use the mouse drag motion to rotate the graph in the master + * view + */ + public void mouseDragged(MouseEvent e) { + if (down == null) return; + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + boolean accepted = checkModifiers(e); + if (accepted) { + if (vv instanceof SatelliteVisualizationViewer) { + VisualizationViewer vvMaster = ((SatelliteVisualizationViewer) vv).getMaster(); - // rotate - vv.setCursor(cursor); - // I want to compute rotation based on the view coordinates of the - // lens center in the satellite view. - // translate the master view center to layout coords, then translate - // that point to the satellite view's view coordinate system.... - Point2D center = vv.getRenderContext().getMultiLayerTransformer().transform(vvMaster.getRenderContext().getMultiLayerTransformer().inverseTransform(vvMaster.getCenter())); - Point2D q = down; - Point2D p = e.getPoint(); - Point2D v1 = new Point2D.Double(center.getX()-p.getX(), center.getY()-p.getY()); - Point2D v2 = new Point2D.Double(center.getX()-q.getX(), center.getY()-q.getY()); - double theta = angleBetween(v1, v2); - modelTransformerMaster.rotate(-theta, - vvMaster.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, vvMaster.getCenter())); - down.x = e.getX(); - down.y = e.getY(); - } - e.consume(); - } - } + MutableTransformer modelTransformerMaster = + vvMaster.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + // rotate + vv.setCursor(cursor); + // I want to compute rotation based on the view coordinates of the + // lens center in the satellite view. + // translate the master view center to layout coords, then translate + // that point to the satellite view's view coordinate system.... + Point2D center = + vv.getRenderContext() + .getMultiLayerTransformer() + .transform( + vvMaster + .getRenderContext() + .getMultiLayerTransformer() + .inverseTransform(vvMaster.getCenter())); + Point2D q = down; + Point2D p = e.getPoint(); + Point2D v1 = new Point2D.Double(center.getX() - p.getX(), center.getY() - p.getY()); + Point2D v2 = new Point2D.Double(center.getX() - q.getX(), center.getY() - q.getY()); + double theta = angleBetween(v1, v2); + modelTransformerMaster.rotate( + -theta, + vvMaster + .getRenderContext() + .getMultiLayerTransformer() + .inverseTransform(Layer.VIEW, vvMaster.getCenter())); + down.x = e.getX(); + down.y = e.getY(); + } + e.consume(); + } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteScalingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteScalingGraphMousePlugin.java index 2972aae0..3bc64cc0 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteScalingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteScalingGraphMousePlugin.java @@ -10,54 +10,49 @@ package edu.uci.ics.jung.visualization.control; -import java.awt.event.MouseWheelEvent; - import edu.uci.ics.jung.visualization.VisualizationViewer; +import java.awt.event.MouseWheelEvent; /** - * Overrides ScalingGraphMousePlugin so that mouse events in the - * satellite view will cause scaling in the main view - * - * @see ScalingGraphMousePlugin - * @author Tom Nelson + * Overrides ScalingGraphMousePlugin so that mouse events in the satellite view will cause scaling + * in the main view * + * @see ScalingGraphMousePlugin + * @author Tom Nelson */ public class SatelliteScalingGraphMousePlugin extends ScalingGraphMousePlugin { - public SatelliteScalingGraphMousePlugin(ScalingControl scaler, int modifiers) { - super(scaler, modifiers); - } + public SatelliteScalingGraphMousePlugin(ScalingControl scaler, int modifiers) { + super(scaler, modifiers); + } - public SatelliteScalingGraphMousePlugin(ScalingControl scaler, int modifiers, float in, float out) { - super(scaler, modifiers, in, out); - } - - /** - * zoom the master view display in or out, depending on the direction of the - * mouse wheel motion. - */ - public void mouseWheelMoved(MouseWheelEvent e) { - boolean accepted = checkModifiers(e); - if(accepted == true) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - - if(vv instanceof SatelliteVisualizationViewer) { - VisualizationViewer vvMaster = - ((SatelliteVisualizationViewer)vv).getMaster(); - - int amount = e.getWheelRotation(); - - if(amount > 0) { - scaler.scale(vvMaster, in, vvMaster.getCenter()); - - } else if(amount < 0) { - scaler.scale(vvMaster, out, vvMaster.getCenter()); - } - e.consume(); - vv.repaint(); - } - } - } + public SatelliteScalingGraphMousePlugin( + ScalingControl scaler, int modifiers, float in, float out) { + super(scaler, modifiers, in, out); + } + + /** + * zoom the master view display in or out, depending on the direction of the mouse wheel motion. + */ + public void mouseWheelMoved(MouseWheelEvent e) { + boolean accepted = checkModifiers(e); + if (accepted == true) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + + if (vv instanceof SatelliteVisualizationViewer) { + VisualizationViewer vvMaster = ((SatelliteVisualizationViewer) vv).getMaster(); + int amount = e.getWheelRotation(); + if (amount > 0) { + scaler.scale(vvMaster, in, vvMaster.getCenter()); + + } else if (amount < 0) { + scaler.scale(vvMaster, out, vvMaster.getCenter()); + } + e.consume(); + vv.repaint(); + } + } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteShearingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteShearingGraphMousePlugin.java index a1dd66ed..e41d87f5 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteShearingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteShearingGraphMousePlugin.java @@ -10,73 +10,75 @@ package edu.uci.ics.jung.visualization.control; -import java.awt.Dimension; -import java.awt.event.MouseEvent; -import java.awt.geom.Point2D; - import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.VisualizationViewer; import edu.uci.ics.jung.visualization.transform.MutableTransformer; +import java.awt.Dimension; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; /** - * Overrides ShearingGraphMousePlugin so that mouse events in the - * satellite view cause shearing of the main view - * - * @see ShearingGraphMousePlugin - * @author Tom Nelson + * Overrides ShearingGraphMousePlugin so that mouse events in the satellite view cause shearing of + * the main view * + * @see ShearingGraphMousePlugin + * @author Tom Nelson */ public class SatelliteShearingGraphMousePlugin extends ShearingGraphMousePlugin { - public SatelliteShearingGraphMousePlugin() { - super(); - } + public SatelliteShearingGraphMousePlugin() { + super(); + } - public SatelliteShearingGraphMousePlugin(int modifiers) { - super(modifiers); - } - - /** - * overridden to shear the main view - */ - public void mouseDragged(MouseEvent e) { - if(down == null) return; - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - boolean accepted = checkModifiers(e); - if(accepted) { - if(vv instanceof SatelliteVisualizationViewer) { - VisualizationViewer vvMaster = - ((SatelliteVisualizationViewer)vv).getMaster(); - - MutableTransformer modelTransformerMaster = - vvMaster.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + public SatelliteShearingGraphMousePlugin(int modifiers) { + super(modifiers); + } - vv.setCursor(cursor); - Point2D q = down; - Point2D p = e.getPoint(); - float dx = (float) (p.getX()-q.getX()); - float dy = (float) (p.getY()-q.getY()); + /** overridden to shear the main view */ + public void mouseDragged(MouseEvent e) { + if (down == null) return; + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + boolean accepted = checkModifiers(e); + if (accepted) { + if (vv instanceof SatelliteVisualizationViewer) { + VisualizationViewer vvMaster = ((SatelliteVisualizationViewer) vv).getMaster(); - Dimension d = vv.getSize(); - float shx = 2.f*dx/d.height; - float shy = 2.f*dy/d.width; - // I want to compute shear based on the view coordinates of the - // lens center in the satellite view. - // translate the master view center to layout coords, then translate - // that point to the satellite view's view coordinate system.... - Point2D center = vv.getRenderContext().getMultiLayerTransformer().transform(vvMaster.getRenderContext().getMultiLayerTransformer().inverseTransform(vvMaster.getCenter())); - if(p.getX() < center.getX()) { - shy = -shy; - } - if(p.getY() < center.getY()) { - shx = -shx; - } - modelTransformerMaster.shear(-shx, -shy, vvMaster.getCenter()); + MutableTransformer modelTransformerMaster = + vvMaster.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - down.x = e.getX(); - down.y = e.getY(); - } - e.consume(); + vv.setCursor(cursor); + Point2D q = down; + Point2D p = e.getPoint(); + float dx = (float) (p.getX() - q.getX()); + float dy = (float) (p.getY() - q.getY()); + + Dimension d = vv.getSize(); + float shx = 2.f * dx / d.height; + float shy = 2.f * dy / d.width; + // I want to compute shear based on the view coordinates of the + // lens center in the satellite view. + // translate the master view center to layout coords, then translate + // that point to the satellite view's view coordinate system.... + Point2D center = + vv.getRenderContext() + .getMultiLayerTransformer() + .transform( + vvMaster + .getRenderContext() + .getMultiLayerTransformer() + .inverseTransform(vvMaster.getCenter())); + if (p.getX() < center.getX()) { + shy = -shy; + } + if (p.getY() < center.getY()) { + shx = -shx; } + modelTransformerMaster.shear(-shx, -shy, vvMaster.getCenter()); + + down.x = e.getX(); + down.y = e.getY(); + } + e.consume(); } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteTranslatingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteTranslatingGraphMousePlugin.java index 70beeeb0..ab448ed8 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteTranslatingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteTranslatingGraphMousePlugin.java @@ -10,66 +10,62 @@ package edu.uci.ics.jung.visualization.control; -import java.awt.Cursor; -import java.awt.event.MouseEvent; -import java.awt.geom.Point2D; - import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.VisualizationViewer; import edu.uci.ics.jung.visualization.transform.MutableTransformer; +import java.awt.Cursor; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; /** - * Overrides TranslatingGraphMousePlugin so that mouse events in - * the satellite view cause translating of the main view - * - * @see TranslatingGraphMousePlugin - * @author Tom Nelson + * Overrides TranslatingGraphMousePlugin so that mouse events in the satellite view cause + * translating of the main view * + * @see TranslatingGraphMousePlugin + * @author Tom Nelson */ -public class SatelliteTranslatingGraphMousePlugin extends - TranslatingGraphMousePlugin { +public class SatelliteTranslatingGraphMousePlugin extends TranslatingGraphMousePlugin { - public SatelliteTranslatingGraphMousePlugin() { - super(); - } + public SatelliteTranslatingGraphMousePlugin() { + super(); + } - public SatelliteTranslatingGraphMousePlugin(int modifiers) { - super(modifiers); - } - - /** - * Check the modifiers. If accepted, translate the main view according - * to the dragging of the mouse pointer in the satellite view - * @param e the event - */ - public void mouseDragged(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - boolean accepted = checkModifiers(e); - if(accepted) { - if(vv instanceof SatelliteVisualizationViewer) { - VisualizationViewer vvMaster = - ((SatelliteVisualizationViewer)vv).getMaster(); - - MutableTransformer modelTransformerMaster = - vvMaster.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - vv.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); - try { - Point2D q = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(down); - Point2D p = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(e.getPoint()); - float dx = (float) (p.getX()-q.getX()); - float dy = (float) (p.getY()-q.getY()); - - modelTransformerMaster.translate(-dx, -dy); - down.x = e.getX(); - down.y = e.getY(); - } catch(RuntimeException ex) { - System.err.println("down = "+down+", e = "+e); - throw ex; - } - } - e.consume(); - } - } + public SatelliteTranslatingGraphMousePlugin(int modifiers) { + super(modifiers); + } + + /** + * Check the modifiers. If accepted, translate the main view according to the dragging of the + * mouse pointer in the satellite view + * + * @param e the event + */ + public void mouseDragged(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + boolean accepted = checkModifiers(e); + if (accepted) { + if (vv instanceof SatelliteVisualizationViewer) { + VisualizationViewer vvMaster = ((SatelliteVisualizationViewer) vv).getMaster(); + MutableTransformer modelTransformerMaster = + vvMaster.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + vv.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + try { + Point2D q = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(down); + Point2D p = + vv.getRenderContext().getMultiLayerTransformer().inverseTransform(e.getPoint()); + float dx = (float) (p.getX() - q.getX()); + float dy = (float) (p.getY() - q.getY()); + modelTransformerMaster.translate(-dx, -dy); + down.x = e.getX(); + down.y = e.getY(); + } catch (RuntimeException ex) { + System.err.println("down = " + down + ", e = " + e); + throw ex; + } + } + e.consume(); + } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteVisualizationViewer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteVisualizationViewer.java index 2b018385..f8200c97 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteVisualizationViewer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SatelliteVisualizationViewer.java @@ -10,6 +10,10 @@ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.transform.MutableAffineTransformer; +import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; @@ -17,113 +21,106 @@ import java.awt.Shape; import java.awt.geom.AffineTransform; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.transform.MutableAffineTransformer; -import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer; - /** - * A VisualizationViewer that can act as a satellite view for another - * (master) VisualizationViewer. In this view, the full graph is always visible - * and all mouse actions affect the graph in the master view. - * - * A rectangular shape in the satellite view shows the visible bounds of - * the master view. - * - * @author Tom Nelson + * A VisualizationViewer that can act as a satellite view for another (master) VisualizationViewer. + * In this view, the full graph is always visible and all mouse actions affect the graph in the + * master view. + * + *

            A rectangular shape in the satellite view shows the visible bounds of the master view. * - * + * @author Tom Nelson */ @SuppressWarnings("serial") -public class SatelliteVisualizationViewer - extends VisualizationViewer { - - /** - * the master VisualizationViewer that this is a satellite view for - */ - protected VisualizationViewer master; - - /** - * @param master the master VisualizationViewer for which this is a satellite view - * @param preferredSize the specified size of the component - */ - public SatelliteVisualizationViewer(VisualizationViewer master, - Dimension preferredSize) { - super(master.getModel(), preferredSize); - this.master = master; - - // create a graph mouse with custom plugins to affect the master view - ModalGraphMouse gm = new ModalSatelliteGraphMouse(); - setGraphMouse(gm); - - // this adds the Lens to the satellite view - addPreRenderPaintable(new ViewLens(this, master)); - - // get a copy of the current layout transform - // it may have been scaled to fit the graph - AffineTransform modelLayoutTransform = - new AffineTransform(master.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getTransform()); - - // I want no layout transformations in the satellite view - // this resets the auto-scaling that occurs in the super constructor - getRenderContext().getMultiLayerTransformer().setTransformer(Layer.LAYOUT, new MutableAffineTransformer(modelLayoutTransform)); - - // make sure the satellite listens for changes in the master - master.addChangeListener(this); - - // share the picked state of the master - setPickedVertexState(master.getPickedVertexState()); - setPickedEdgeState(master.getPickedEdgeState()); - } +public class SatelliteVisualizationViewer extends VisualizationViewer { + + /** the master VisualizationViewer that this is a satellite view for */ + protected VisualizationViewer master; + + /** + * @param master the master VisualizationViewer for which this is a satellite view + * @param preferredSize the specified size of the component + */ + public SatelliteVisualizationViewer(VisualizationViewer master, Dimension preferredSize) { + super(master.getModel(), preferredSize); + this.master = master; - /** - * @return Returns the master. - */ - public VisualizationViewer getMaster() { - return master; + // create a graph mouse with custom plugins to affect the master view + ModalGraphMouse gm = new ModalSatelliteGraphMouse(); + setGraphMouse(gm); + + // this adds the Lens to the satellite view + addPreRenderPaintable(new ViewLens(this, master)); + + // get a copy of the current layout transform + // it may have been scaled to fit the graph + AffineTransform modelLayoutTransform = + new AffineTransform( + master + .getRenderContext() + .getMultiLayerTransformer() + .getTransformer(Layer.LAYOUT) + .getTransform()); + + // I want no layout transformations in the satellite view + // this resets the auto-scaling that occurs in the super constructor + getRenderContext() + .getMultiLayerTransformer() + .setTransformer(Layer.LAYOUT, new MutableAffineTransformer(modelLayoutTransform)); + + // make sure the satellite listens for changes in the master + master.addChangeListener(this); + + // share the picked state of the master + setPickedVertexState(master.getPickedVertexState()); + setPickedEdgeState(master.getPickedEdgeState()); + } + + /** @return Returns the master. */ + public VisualizationViewer getMaster() { + return master; + } + + /** + * A four-sided shape that represents the visible part of the master view and is drawn in the + * satellite view + * + * @author Tom Nelson + */ + static class ViewLens implements Paintable { + + VisualizationViewer master; + VisualizationViewer vv; + + public ViewLens(VisualizationViewer vv, VisualizationViewer master) { + this.vv = vv; + this.master = master; } - - /** - * A four-sided shape that represents the visible part of the - * master view and is drawn in the satellite view - * - * @author Tom Nelson - * - * - */ - static class ViewLens implements Paintable { - - VisualizationViewer master; - VisualizationViewer vv; - - public ViewLens(VisualizationViewer vv, VisualizationViewer master) { - this.vv = vv; - this.master = master; - } - public void paint(Graphics g) { - ShapeTransformer masterViewTransformer = - master.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); - ShapeTransformer masterLayoutTransformer = master.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - ShapeTransformer vvLayoutTransformer = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - - Shape lens = master.getBounds(); - lens = masterViewTransformer.inverseTransform(lens); - lens = masterLayoutTransformer.inverseTransform(lens); - lens = vvLayoutTransformer.transform(lens); - Graphics2D g2d = (Graphics2D)g; - Color old = g.getColor(); - Color lensColor = master.getBackground(); - vv.setBackground(lensColor.darker()); - g.setColor(lensColor); - g2d.fill(lens); - g.setColor(Color.gray); - g2d.draw(lens); - g.setColor(old); - } - - public boolean useTransform() { - return true; - } + + public void paint(Graphics g) { + ShapeTransformer masterViewTransformer = + master.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); + ShapeTransformer masterLayoutTransformer = + master.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + ShapeTransformer vvLayoutTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + + Shape lens = master.getBounds(); + lens = masterViewTransformer.inverseTransform(lens); + lens = masterLayoutTransformer.inverseTransform(lens); + lens = vvLayoutTransformer.transform(lens); + Graphics2D g2d = (Graphics2D) g; + Color old = g.getColor(); + Color lensColor = master.getBackground(); + vv.setBackground(lensColor.darker()); + g.setColor(lensColor); + g2d.fill(lens); + g.setColor(Color.gray); + g2d.draw(lens); + g.setColor(old); } + public boolean useTransform() { + return true; + } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ScalingControl.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ScalingControl.java index ca6e2236..e8496939 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ScalingControl.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ScalingControl.java @@ -1,25 +1,24 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.visualization.control; -import java.awt.geom.Point2D; - import edu.uci.ics.jung.visualization.VisualizationServer; +import java.awt.geom.Point2D; public interface ScalingControl { - /** - * zoom the display in or out - * @param vv the VisualizationViewer - * @param amount how much to adjust scale by - * @param at where to adjust scale from - */ - void scale(VisualizationServer vv, float amount, Point2D at); - -} \ No newline at end of file + /** + * zoom the display in or out + * + * @param vv the VisualizationViewer + * @param amount how much to adjust scale by + * @param at where to adjust scale from + */ + void scale(VisualizationServer vv, float amount, Point2D at); +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ScalingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ScalingGraphMousePlugin.java index 6b25697d..b1f643dc 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ScalingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ScalingGraphMousePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,121 +11,99 @@ */ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.visualization.VisualizationViewer; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.geom.Point2D; -import edu.uci.ics.jung.visualization.VisualizationViewer; - -/** - * ScalingGraphMouse applies a scaling transformation to the graph layout. - * The Vertices get closer or farther apart, but do not themselves change - * size. ScalingGraphMouse uses MouseWheelEvents to apply the scaling. - * +/** + * ScalingGraphMouse applies a scaling transformation to the graph layout. The Vertices get closer + * or farther apart, but do not themselves change size. ScalingGraphMouse uses MouseWheelEvents to + * apply the scaling. + * * @author Tom Nelson */ public class ScalingGraphMousePlugin extends AbstractGraphMousePlugin implements MouseWheelListener { - /** - * the amount to zoom in by - */ - protected float in = 1.1f; - /** - * the amount to zoom out by - */ - protected float out = 1/1.1f; - - /** - * whether to center the zoom at the current mouse position - */ - protected boolean zoomAtMouse = true; - - /** - * controls scaling operations - */ - protected ScalingControl scaler; - - public ScalingGraphMousePlugin(ScalingControl scaler, int modifiers) { - this(scaler, modifiers, 1.1f, 1/1.1f); - } - - public ScalingGraphMousePlugin(ScalingControl scaler, int modifiers, float in, float out) { - super(modifiers); - this.scaler = scaler; - this.in = in; - this.out = out; - } - /** - * @param zoomAtMouse The zoomAtMouse to set. - */ - public void setZoomAtMouse(boolean zoomAtMouse) { - this.zoomAtMouse = zoomAtMouse; - } - - public boolean checkModifiers(MouseEvent e) { - return e.getModifiers() == modifiers || (e.getModifiers() & modifiers) != 0; - } + /** the amount to zoom in by */ + protected float in = 1.1f; + /** the amount to zoom out by */ + protected float out = 1 / 1.1f; + + /** whether to center the zoom at the current mouse position */ + protected boolean zoomAtMouse = true; + + /** controls scaling operations */ + protected ScalingControl scaler; - /** - * zoom the display in or out, depending on the direction of the - * mouse wheel motion. - */ - public void mouseWheelMoved(MouseWheelEvent e) { - boolean accepted = checkModifiers(e); - if(accepted == true) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - Point2D mouse = e.getPoint(); - Point2D center = vv.getCenter(); - int amount = e.getWheelRotation(); - if(zoomAtMouse) { - if(amount > 0) { - scaler.scale(vv, in, mouse); - } else if(amount < 0) { - scaler.scale(vv, out, mouse); - } - } else { - if(amount > 0) { - scaler.scale(vv, in, center); - } else if(amount < 0) { - scaler.scale(vv, out, center); - } - } - e.consume(); - vv.repaint(); + public ScalingGraphMousePlugin(ScalingControl scaler, int modifiers) { + this(scaler, modifiers, 1.1f, 1 / 1.1f); + } + + public ScalingGraphMousePlugin(ScalingControl scaler, int modifiers, float in, float out) { + super(modifiers); + this.scaler = scaler; + this.in = in; + this.out = out; + } + /** @param zoomAtMouse The zoomAtMouse to set. */ + public void setZoomAtMouse(boolean zoomAtMouse) { + this.zoomAtMouse = zoomAtMouse; + } + + public boolean checkModifiers(MouseEvent e) { + return e.getModifiers() == modifiers || (e.getModifiers() & modifiers) != 0; + } + + /** zoom the display in or out, depending on the direction of the mouse wheel motion. */ + public void mouseWheelMoved(MouseWheelEvent e) { + boolean accepted = checkModifiers(e); + if (accepted == true) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + Point2D mouse = e.getPoint(); + Point2D center = vv.getCenter(); + int amount = e.getWheelRotation(); + if (zoomAtMouse) { + if (amount > 0) { + scaler.scale(vv, in, mouse); + } else if (amount < 0) { + scaler.scale(vv, out, mouse); } - } - /** - * @return Returns the zoom in value. - */ - public float getIn() { - return in; - } - /** - * @param in The zoom in value to set. - */ - public void setIn(float in) { - this.in = in; - } - /** - * @return Returns the zoom out value. - */ - public float getOut() { - return out; - } - /** - * @param out The zoom out value to set. - */ - public void setOut(float out) { - this.out = out; + } else { + if (amount > 0) { + scaler.scale(vv, in, center); + } else if (amount < 0) { + scaler.scale(vv, out, center); + } + } + e.consume(); + vv.repaint(); } + } + /** @return Returns the zoom in value. */ + public float getIn() { + return in; + } + /** @param in The zoom in value to set. */ + public void setIn(float in) { + this.in = in; + } + /** @return Returns the zoom out value. */ + public float getOut() { + return out; + } + /** @param out The zoom out value to set. */ + public void setOut(float out) { + this.out = out; + } - public ScalingControl getScaler() { - return scaler; - } + public ScalingControl getScaler() { + return scaler; + } - public void setScaler(ScalingControl scaler) { - this.scaler = scaler; - } + public void setScaler(ScalingControl scaler) { + this.scaler = scaler; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ShearingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ShearingGraphMousePlugin.java index e2d0b9ab..400b875b 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ShearingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ShearingGraphMousePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,6 +11,9 @@ */ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.transform.MutableTransformer; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Cursor; @@ -27,140 +30,135 @@ import java.awt.image.BufferedImage; import java.util.Collections; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.transform.MutableTransformer; - -/** - * ShearingGraphMousePlugin allows the user to drag with the mouse - * to shear the transform either in the horizontal or vertical direction. - * By default, the control or meta key must be depressed to activate - * shearing. - * - * +/** + * ShearingGraphMousePlugin allows the user to drag with the mouse to shear the transform either in + * the horizontal or vertical direction. By default, the control or meta key must be depressed to + * activate shearing. + * * @author Tom Nelson */ public class ShearingGraphMousePlugin extends AbstractGraphMousePlugin implements MouseListener, MouseMotionListener { - private static int mask = MouseEvent.CTRL_MASK; - - static { - if(System.getProperty("os.name").startsWith("Mac")) { - mask = MouseEvent.META_MASK; - } - } - /** - * create an instance with default modifier values - */ - public ShearingGraphMousePlugin() { - this(MouseEvent.BUTTON1_MASK | mask); - } - - /** - * create an instance with passed modifier values - * @param modifiers the mouse modifiers to use - */ - public ShearingGraphMousePlugin(int modifiers) { - super(modifiers); - Dimension cd = Toolkit.getDefaultToolkit().getBestCursorSize(16,16); - BufferedImage cursorImage = - new BufferedImage(cd.width,cd.height,BufferedImage.TYPE_INT_ARGB); - Graphics g = cursorImage.createGraphics(); - Graphics2D g2 = (Graphics2D)g; - g2.addRenderingHints(Collections.singletonMap(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); - g.setColor(new Color(0,0,0,0)); - g.fillRect(0,0,16,16); - - int left = 0; - int top = 0; - int right = 15; - int bottom = 15; - - g.setColor(Color.white); - g2.setStroke(new BasicStroke(3)); - g.drawLine(left+2,top+5,right-2,top+5); - g.drawLine(left+2,bottom-5,right-2,bottom-5); - g.drawLine(left+2,top+5,left+4,top+3); - g.drawLine(left+2,top+5,left+4,top+7); - g.drawLine(right-2,bottom-5,right-4,bottom-3); - g.drawLine(right-2,bottom-5,right-4,bottom-7); - - g.setColor(Color.black); - g2.setStroke(new BasicStroke(1)); - g.drawLine(left+2,top+5,right-2,top+5); - g.drawLine(left+2,bottom-5,right-2,bottom-5); - g.drawLine(left+2,top+5,left+4,top+3); - g.drawLine(left+2,top+5,left+4,top+7); - g.drawLine(right-2,bottom-5,right-4,bottom-3); - g.drawLine(right-2,bottom-5,right-4,bottom-7); - g.dispose(); - cursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(), "RotateCursor"); - } - - public void mousePressed(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - boolean accepted = checkModifiers(e); - down = e.getPoint(); - if(accepted) { - vv.setCursor(cursor); - } - } - - public void mouseReleased(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - down = null; - vv.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - - public void mouseDragged(MouseEvent e) { - if(down == null) return; - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - boolean accepted = checkModifiers(e); - if(accepted) { - MutableTransformer modelTransformer = - vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - vv.setCursor(cursor); - Point2D q = down; - Point2D p = e.getPoint(); - float dx = (float) (p.getX()-q.getX()); - float dy = (float) (p.getY()-q.getY()); - - Dimension d = vv.getSize(); - float shx = 2.f*dx/d.height; - float shy = 2.f*dy/d.width; - Point2D center = vv.getCenter(); - if(p.getX() < center.getX()) { - shy = -shy; - } - if(p.getY() < center.getY()) { - shx = -shx; - } - modelTransformer.shear(shx, shy, center); - down.x = e.getX(); - down.y = e.getY(); - - e.consume(); - } - } + private static int mask = MouseEvent.CTRL_MASK; - public void mouseClicked(MouseEvent e) { - // TODO Auto-generated method stub - + static { + if (System.getProperty("os.name").startsWith("Mac")) { + mask = MouseEvent.META_MASK; } + } + /** create an instance with default modifier values */ + public ShearingGraphMousePlugin() { + this(MouseEvent.BUTTON1_MASK | mask); + } - public void mouseEntered(MouseEvent e) { - // TODO Auto-generated method stub - - } + /** + * create an instance with passed modifier values + * + * @param modifiers the mouse modifiers to use + */ + public ShearingGraphMousePlugin(int modifiers) { + super(modifiers); + Dimension cd = Toolkit.getDefaultToolkit().getBestCursorSize(16, 16); + BufferedImage cursorImage = new BufferedImage(cd.width, cd.height, BufferedImage.TYPE_INT_ARGB); + Graphics g = cursorImage.createGraphics(); + Graphics2D g2 = (Graphics2D) g; + g2.addRenderingHints( + Collections.singletonMap( + RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); + g.setColor(new Color(0, 0, 0, 0)); + g.fillRect(0, 0, 16, 16); + + int left = 0; + int top = 0; + int right = 15; + int bottom = 15; - public void mouseExited(MouseEvent e) { - // TODO Auto-generated method stub - + g.setColor(Color.white); + g2.setStroke(new BasicStroke(3)); + g.drawLine(left + 2, top + 5, right - 2, top + 5); + g.drawLine(left + 2, bottom - 5, right - 2, bottom - 5); + g.drawLine(left + 2, top + 5, left + 4, top + 3); + g.drawLine(left + 2, top + 5, left + 4, top + 7); + g.drawLine(right - 2, bottom - 5, right - 4, bottom - 3); + g.drawLine(right - 2, bottom - 5, right - 4, bottom - 7); + + g.setColor(Color.black); + g2.setStroke(new BasicStroke(1)); + g.drawLine(left + 2, top + 5, right - 2, top + 5); + g.drawLine(left + 2, bottom - 5, right - 2, bottom - 5); + g.drawLine(left + 2, top + 5, left + 4, top + 3); + g.drawLine(left + 2, top + 5, left + 4, top + 7); + g.drawLine(right - 2, bottom - 5, right - 4, bottom - 3); + g.drawLine(right - 2, bottom - 5, right - 4, bottom - 7); + g.dispose(); + cursor = + Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(), "RotateCursor"); + } + + public void mousePressed(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + boolean accepted = checkModifiers(e); + down = e.getPoint(); + if (accepted) { + vv.setCursor(cursor); } + } + + public void mouseReleased(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + down = null; + vv.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + + public void mouseDragged(MouseEvent e) { + if (down == null) return; + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + boolean accepted = checkModifiers(e); + if (accepted) { + MutableTransformer modelTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + vv.setCursor(cursor); + Point2D q = down; + Point2D p = e.getPoint(); + float dx = (float) (p.getX() - q.getX()); + float dy = (float) (p.getY() - q.getY()); - public void mouseMoved(MouseEvent e) { - // TODO Auto-generated method stub - + Dimension d = vv.getSize(); + float shx = 2.f * dx / d.height; + float shy = 2.f * dy / d.width; + Point2D center = vv.getCenter(); + if (p.getX() < center.getX()) { + shy = -shy; + } + if (p.getY() < center.getY()) { + shx = -shx; + } + modelTransformer.shear(shx, shy, center); + down.x = e.getX(); + down.y = e.getY(); + + e.consume(); } + } + + public void mouseClicked(MouseEvent e) { + // TODO Auto-generated method stub + + } + + public void mouseEntered(MouseEvent e) { + // TODO Auto-generated method stub + + } + + public void mouseExited(MouseEvent e) { + // TODO Auto-generated method stub + + } + + public void mouseMoved(MouseEvent e) { + // TODO Auto-generated method stub + + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SimpleVertexSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SimpleVertexSupport.java index bd9c994e..2643bee7 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SimpleVertexSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/SimpleVertexSupport.java @@ -1,59 +1,54 @@ package edu.uci.ics.jung.visualization.control; -import java.awt.geom.Point2D; - import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.graph.MutableNetwork; - import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.visualization.BasicVisualizationServer; +import java.awt.geom.Point2D; -/** +/** * sample implementation showing how to use the VertexSupport interface member of the - * EditingGraphMousePlugin. - * override midVertexCreate and endVertexCreate for more elaborate implementations - * @author tanelso + * EditingGraphMousePlugin. override midVertexCreate and endVertexCreate for more elaborate + * implementations * + * @author tanelso * @param the vertex type * @param the edge type */ -public class SimpleVertexSupport implements VertexSupport { - - protected Supplier vertexFactory; - - public SimpleVertexSupport(Supplier vertexFactory) { - this.vertexFactory = vertexFactory; - } - - public void startVertexCreate(BasicVisualizationServer vv, - Point2D point) { - Preconditions.checkState(vv.getModel().getNetwork() instanceof MutableNetwork, - "graph must be mutable"); - V newVertex = vertexFactory.get(); - Layout layout = vv.getGraphLayout(); - MutableNetwork graph = (MutableNetwork) vv.getModel().getNetwork(); - graph.addNode(newVertex); - layout.setLocation(newVertex, vv.getRenderContext().getMultiLayerTransformer().inverseTransform(point)); - vv.repaint(); - } - - public void midVertexCreate(BasicVisualizationServer vv, - Point2D point) { - // noop - } - - public void endVertexCreate(BasicVisualizationServer vv, - Point2D point) { - //noop - } - - public Supplier getVertexFactory() { - return vertexFactory; - } - - public void setVertexFactory(Supplier vertexFactory) { - this.vertexFactory = vertexFactory; - } - +public class SimpleVertexSupport implements VertexSupport { + + protected Supplier vertexFactory; + + public SimpleVertexSupport(Supplier vertexFactory) { + this.vertexFactory = vertexFactory; + } + + public void startVertexCreate(BasicVisualizationServer vv, Point2D point) { + Preconditions.checkState( + vv.getModel().getNetwork() instanceof MutableNetwork, "graph must be mutable"); + V newVertex = vertexFactory.get(); + Layout layout = vv.getGraphLayout(); + MutableNetwork graph = (MutableNetwork) vv.getModel().getNetwork(); + graph.addNode(newVertex); + layout.setLocation( + newVertex, vv.getRenderContext().getMultiLayerTransformer().inverseTransform(point)); + vv.repaint(); + } + + public void midVertexCreate(BasicVisualizationServer vv, Point2D point) { + // noop + } + + public void endVertexCreate(BasicVisualizationServer vv, Point2D point) { + //noop + } + + public Supplier getVertexFactory() { + return vertexFactory; + } + + public void setVertexFactory(Supplier vertexFactory) { + this.vertexFactory = vertexFactory; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/TranslatingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/TranslatingGraphMousePlugin.java index 8f6e0823..d42f404d 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/TranslatingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/TranslatingGraphMousePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,115 +11,111 @@ */ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.transform.MutableTransformer; import java.awt.Cursor; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.Point2D; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.transform.MutableTransformer; - -/** - * TranslatingGraphMousePlugin uses a MouseButtonOne press and - * drag gesture to translate the graph display in the x and y - * direction. The default MouseButtonOne modifier can be overridden - * to cause a different mouse gesture to translate the display. - * - * +/** + * TranslatingGraphMousePlugin uses a MouseButtonOne press and drag gesture to translate the graph + * display in the x and y direction. The default MouseButtonOne modifier can be overridden to cause + * a different mouse gesture to translate the display. + * * @author Tom Nelson */ public class TranslatingGraphMousePlugin extends AbstractGraphMousePlugin implements MouseListener, MouseMotionListener { - /** - */ - public TranslatingGraphMousePlugin() { - this(MouseEvent.BUTTON1_MASK); - } - - /** - * create an instance with passed modifer value - * @param modifiers the mouse event modifier to activate this function - */ - public TranslatingGraphMousePlugin(int modifiers) { - super(modifiers); - this.cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); - } - - /** - * Check the event modifiers. Set the 'down' point for later - * use. If this event satisfies the modifiers, change the cursor - * to the system 'move cursor' - * @param e the event - */ - public void mousePressed(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - boolean accepted = checkModifiers(e); - down = e.getPoint(); - if(accepted) { - vv.setCursor(cursor); - } - } - - /** - * unset the 'down' point and change the cursoe back to the system - * default cursor - */ - public void mouseReleased(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - down = null; - vv.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - - /** - * chack the modifiers. If accepted, translate the graph according - * to the dragging of the mouse pointer - * @param e the event - */ - public void mouseDragged(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - boolean accepted = checkModifiers(e); - if(accepted) { - MutableTransformer modelTransformer = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - vv.setCursor(cursor); - try { - Point2D q = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(down); - Point2D p = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(e.getPoint()); - float dx = (float) (p.getX()-q.getX()); - float dy = (float) (p.getY()-q.getY()); - - modelTransformer.translate(dx, dy); - down.x = e.getX(); - down.y = e.getY(); - } catch(RuntimeException ex) { - System.err.println("down = "+down+", e = "+e); - throw ex; - } - - e.consume(); - vv.repaint(); - } - } + /** */ + public TranslatingGraphMousePlugin() { + this(MouseEvent.BUTTON1_MASK); + } - public void mouseClicked(MouseEvent e) { - // TODO Auto-generated method stub - - } + /** + * create an instance with passed modifer value + * + * @param modifiers the mouse event modifier to activate this function + */ + public TranslatingGraphMousePlugin(int modifiers) { + super(modifiers); + this.cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); + } - public void mouseEntered(MouseEvent e) { - // TODO Auto-generated method stub - + /** + * Check the event modifiers. Set the 'down' point for later use. If this event satisfies the + * modifiers, change the cursor to the system 'move cursor' + * + * @param e the event + */ + public void mousePressed(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + boolean accepted = checkModifiers(e); + down = e.getPoint(); + if (accepted) { + vv.setCursor(cursor); } + } - public void mouseExited(MouseEvent e) { - // TODO Auto-generated method stub - - } + /** unset the 'down' point and change the cursoe back to the system default cursor */ + public void mouseReleased(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + down = null; + vv.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + + /** + * chack the modifiers. If accepted, translate the graph according to the dragging of the mouse + * pointer + * + * @param e the event + */ + public void mouseDragged(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + boolean accepted = checkModifiers(e); + if (accepted) { + MutableTransformer modelTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + vv.setCursor(cursor); + try { + Point2D q = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(down); + Point2D p = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(e.getPoint()); + float dx = (float) (p.getX() - q.getX()); + float dy = (float) (p.getY() - q.getY()); + + modelTransformer.translate(dx, dy); + down.x = e.getX(); + down.y = e.getY(); + } catch (RuntimeException ex) { + System.err.println("down = " + down + ", e = " + e); + throw ex; + } - public void mouseMoved(MouseEvent e) { - // TODO Auto-generated method stub - + e.consume(); + vv.repaint(); } + } + + public void mouseClicked(MouseEvent e) { + // TODO Auto-generated method stub + + } + + public void mouseEntered(MouseEvent e) { + // TODO Auto-generated method stub + + } + + public void mouseExited(MouseEvent e) { + // TODO Auto-generated method stub + + } + + public void mouseMoved(MouseEvent e) { + // TODO Auto-generated method stub + + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/VertexSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/VertexSupport.java index 296921f0..3b5a5c3b 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/VertexSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/VertexSupport.java @@ -1,22 +1,20 @@ package edu.uci.ics.jung.visualization.control; -import java.awt.geom.Point2D; - import edu.uci.ics.jung.visualization.BasicVisualizationServer; +import java.awt.geom.Point2D; /** * interface to support the creation of new vertices by the EditingGraphMousePlugin. * SimpleVertexSupport is a sample implementation. - * @author tanelso * + * @author tanelso * @param the vertex type */ -public interface VertexSupport { - - void startVertexCreate(BasicVisualizationServer vv, Point2D point); - - void midVertexCreate(BasicVisualizationServer vv, Point2D point); - - void endVertexCreate(BasicVisualizationServer vv, Point2D point); +public interface VertexSupport { + + void startVertexCreate(BasicVisualizationServer vv, Point2D point); + + void midVertexCreate(BasicVisualizationServer vv, Point2D point); + void endVertexCreate(BasicVisualizationServer vv, Point2D point); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ViewScalingControl.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ViewScalingControl.java index 3b651a26..fe5330cd 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ViewScalingControl.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ViewScalingControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,30 +11,26 @@ */ package edu.uci.ics.jung.visualization.control; -import java.awt.geom.Point2D; - import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.VisualizationServer; import edu.uci.ics.jung.visualization.transform.MutableTransformer; +import java.awt.geom.Point2D; -/** - * ViewScalingGraphMouse applies a scaling transform to the View - * of the graph. This causes all elements of the graph to grow - * larger or smaller. ViewScalingGraphMouse, by default, is activated - * by the MouseWheel when the control key is pressed. The control - * key modifier can be overridden in the contstructor. - * +/** + * ViewScalingGraphMouse applies a scaling transform to the View of the graph. This causes all + * elements of the graph to grow larger or smaller. ViewScalingGraphMouse, by default, is activated + * by the MouseWheel when the control key is pressed. The control key modifier can be overridden in + * the contstructor. + * * @author Tom Nelson */ public class ViewScalingControl implements ScalingControl { - /** - * zoom the display in or out, depending on the direction of the - * mouse wheel motion. - */ - public void scale(VisualizationServer vv, float amount, Point2D from) { - MutableTransformer viewTransformer = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); - viewTransformer.scale(amount, amount, from); - vv.repaint(); - } + /** zoom the display in or out, depending on the direction of the mouse wheel motion. */ + public void scale(VisualizationServer vv, float amount, Point2D from) { + MutableTransformer viewTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); + viewTransformer.scale(amount, amount, from); + vv.repaint(); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ViewTranslatingGraphMousePlugin.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ViewTranslatingGraphMousePlugin.java index 2b2c4d44..95a562ad 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ViewTranslatingGraphMousePlugin.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/control/ViewTranslatingGraphMousePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,107 +11,99 @@ */ package edu.uci.ics.jung.visualization.control; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.transform.MutableTransformer; import java.awt.Cursor; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.Point2D; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.transform.MutableTransformer; - -/** - * ViewTranslatingGraphMousePlugin uses a MouseButtonOne press and - * drag gesture to translate the graph display in the x and y - * direction by changing the AffineTransform applied to the Graphics2D. - * The default MouseButtonOne modifier can be overridden - * to cause a different mouse gesture to translate the display. - * - * +/** + * ViewTranslatingGraphMousePlugin uses a MouseButtonOne press and drag gesture to translate the + * graph display in the x and y direction by changing the AffineTransform applied to the Graphics2D. + * The default MouseButtonOne modifier can be overridden to cause a different mouse gesture to + * translate the display. + * * @author Tom Nelson */ public class ViewTranslatingGraphMousePlugin extends AbstractGraphMousePlugin implements MouseListener, MouseMotionListener { - /** - */ - public ViewTranslatingGraphMousePlugin() { - this(MouseEvent.BUTTON1_MASK); - } + /** */ + public ViewTranslatingGraphMousePlugin() { + this(MouseEvent.BUTTON1_MASK); + } - /** - * create an instance with passed modifer value - * @param modifiers the mouse event modifier to activate this function - */ - public ViewTranslatingGraphMousePlugin(int modifiers) { - super(modifiers); - this.cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); - } + /** + * create an instance with passed modifer value + * + * @param modifiers the mouse event modifier to activate this function + */ + public ViewTranslatingGraphMousePlugin(int modifiers) { + super(modifiers); + this.cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); + } - /** - * Check the event modifiers. Set the 'down' point for later - * use. If this event satisfies the modifiers, change the cursor - * to the system 'move cursor' - * @param e the event - */ - public void mousePressed(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - boolean accepted = checkModifiers(e); - down = e.getPoint(); - if(accepted) { - vv.setCursor(cursor); - } - } - - /** - * unset the 'down' point and change the cursoe back to the system - * default cursor - */ - public void mouseReleased(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - down = null; - vv.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - - /** - * chack the modifiers. If accepted, translate the graph according - * to the dragging of the mouse pointer - * @param e the event - */ - public void mouseDragged(MouseEvent e) { - VisualizationViewer vv = (VisualizationViewer)e.getSource(); - boolean accepted = checkModifiers(e); - if(accepted) { - MutableTransformer viewTransformer = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); - vv.setCursor(cursor); - try { - Point2D q = viewTransformer.inverseTransform(down); - Point2D p = viewTransformer.inverseTransform(e.getPoint()); - float dx = (float) (p.getX()-q.getX()); - float dy = (float) (p.getY()-q.getY()); - - viewTransformer.translate(dx, dy); - down.x = e.getX(); - down.y = e.getY(); - } catch(RuntimeException ex) { - System.err.println("down = "+down+", e = "+e); - throw ex; - } - - e.consume(); - } + /** + * Check the event modifiers. Set the 'down' point for later use. If this event satisfies the + * modifiers, change the cursor to the system 'move cursor' + * + * @param e the event + */ + public void mousePressed(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + boolean accepted = checkModifiers(e); + down = e.getPoint(); + if (accepted) { + vv.setCursor(cursor); } + } - public void mouseClicked(MouseEvent e) { - } + /** unset the 'down' point and change the cursoe back to the system default cursor */ + public void mouseReleased(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + down = null; + vv.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } - public void mouseEntered(MouseEvent e) { - } + /** + * chack the modifiers. If accepted, translate the graph according to the dragging of the mouse + * pointer + * + * @param e the event + */ + public void mouseDragged(MouseEvent e) { + VisualizationViewer vv = (VisualizationViewer) e.getSource(); + boolean accepted = checkModifiers(e); + if (accepted) { + MutableTransformer viewTransformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW); + vv.setCursor(cursor); + try { + Point2D q = viewTransformer.inverseTransform(down); + Point2D p = viewTransformer.inverseTransform(e.getPoint()); + float dx = (float) (p.getX() - q.getX()); + float dy = (float) (p.getY() - q.getY()); - public void mouseExited(MouseEvent e) { - } + viewTransformer.translate(dx, dy); + down.x = e.getX(); + down.y = e.getY(); + } catch (RuntimeException ex) { + System.err.println("down = " + down + ", e = " + e); + throw ex; + } - public void mouseMoved(MouseEvent e) { + e.consume(); } + } + + public void mouseClicked(MouseEvent e) {} + + public void mouseEntered(MouseEvent e) {} + + public void mouseExited(MouseEvent e) {} + + public void mouseMoved(MouseEvent e) {} } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/AbstractVertexShapeTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/AbstractVertexShapeTransformer.java index d74a7f27..58f35d6e 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/AbstractVertexShapeTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/AbstractVertexShapeTransformer.java @@ -1,7 +1,7 @@ /* * Created on Jul 16, 2004 * - * Copyright (c) 2004, The JUNG Authors + * Copyright (c) 2004, The JUNG Authors * * All rights reserved. * @@ -13,45 +13,35 @@ import com.google.common.base.Function; import com.google.common.base.Functions; - import edu.uci.ics.jung.visualization.util.VertexShapeFactory; +/** @author Joshua O'Madadhain */ +public abstract class AbstractVertexShapeTransformer + implements SettableVertexShapeTransformer { + protected Function vsf; + protected Function varf; + protected VertexShapeFactory factory; + public static final int DEFAULT_SIZE = 8; + public static final float DEFAULT_ASPECT_RATIO = 1.0f; + public AbstractVertexShapeTransformer( + Function vsf, Function varf) { + this.vsf = vsf; + this.varf = varf; + factory = new VertexShapeFactory(vsf, varf); + } -/** - * - * @author Joshua O'Madadhain - */ -public abstract class AbstractVertexShapeTransformer implements SettableVertexShapeTransformer -{ - protected Function vsf; - protected Function varf; - protected VertexShapeFactory factory; - public final static int DEFAULT_SIZE = 8; - public final static float DEFAULT_ASPECT_RATIO = 1.0f; - - public AbstractVertexShapeTransformer(Function vsf, Function varf) - { - this.vsf = vsf; - this.varf = varf; - factory = new VertexShapeFactory(vsf, varf); - } + public AbstractVertexShapeTransformer() { + this(Functions.constant(DEFAULT_SIZE), Functions.constant(DEFAULT_ASPECT_RATIO)); + } + + public void setSizeTransformer(Function vsf) { + this.vsf = vsf; + factory = new VertexShapeFactory(vsf, varf); + } - public AbstractVertexShapeTransformer() - { - this(Functions.constant(DEFAULT_SIZE), - Functions.constant(DEFAULT_ASPECT_RATIO)); - } - - public void setSizeTransformer(Function vsf) - { - this.vsf = vsf; - factory = new VertexShapeFactory(vsf, varf); - } - - public void setAspectRatioTransformer(Function varf) - { - this.varf = varf; - factory = new VertexShapeFactory(vsf, varf); - } + public void setAspectRatioTransformer(Function varf) { + this.varf = varf; + factory = new VertexShapeFactory(vsf, varf); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/EllipseVertexShapeTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/EllipseVertexShapeTransformer.java index e5b64455..6224b573 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/EllipseVertexShapeTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/EllipseVertexShapeTransformer.java @@ -1,7 +1,7 @@ /* * Created on Jul 16, 2004 * - * Copyright (c) 2004, The JUNG Authors + * Copyright (c) 2004, The JUNG Authors * * All rights reserved. * @@ -11,27 +11,19 @@ */ package edu.uci.ics.jung.visualization.decorators; -import java.awt.Shape; - import com.google.common.base.Function; +import java.awt.Shape; -/** - * - * @author Joshua O'Madadhain - */ +/** @author Joshua O'Madadhain */ public class EllipseVertexShapeTransformer extends AbstractVertexShapeTransformer - implements Function -{ - public EllipseVertexShapeTransformer() - { - } - public EllipseVertexShapeTransformer(Function vsf, Function varf) - { - super(vsf, varf); - } - - public Shape apply(V v) - { - return factory.getEllipse(v); - } + implements Function { + public EllipseVertexShapeTransformer() {} + + public EllipseVertexShapeTransformer(Function vsf, Function varf) { + super(vsf, varf); + } + + public Shape apply(V v) { + return factory.getEllipse(v); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/GradientEdgePaintTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/GradientEdgePaintTransformer.java index 192e4d12..b04fab13 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/GradientEdgePaintTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/GradientEdgePaintTransformer.java @@ -1,7 +1,7 @@ /* * Created on Apr 8, 2005 * - * Copyright (c) 2004, The JUNG Authors + * Copyright (c) 2004, The JUNG Authors * * All rights reserved. * @@ -13,92 +13,83 @@ import static edu.uci.ics.jung.graph.util.Graphs.isSelfLoop; -import java.awt.Color; -import java.awt.GradientPaint; -import java.awt.Paint; -import java.awt.geom.Point2D; - import com.google.common.base.Function; import com.google.common.graph.EndpointPair; import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.VisualizationViewer; import edu.uci.ics.jung.visualization.transform.BidirectionalTransformer; +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Paint; +import java.awt.geom.Point2D; /** - * Creates GradientPaint instances which can be used - * to paint an Edge. For DirectedEdges, - * the color will blend from c1 (source) to - * c2 (destination); for UndirectedEdges, - * the color will be c1 at each end and c2 - * in the middle. - * + * Creates GradientPaint instances which can be used to paint an Edge. For + * DirectedEdges, the color will blend from c1 (source) to c2 + * (destination); for UndirectedEdges, the color will be c1 at each end + * and c2 in the middle. + * * @author Joshua O'Madadhain */ -public class GradientEdgePaintTransformer implements Function -{ - protected Color c1; - protected Color c2; - protected Network graph; - protected Layout layout; - protected BidirectionalTransformer transformer; +public class GradientEdgePaintTransformer implements Function { + protected Color c1; + protected Color c2; + protected Network graph; + protected Layout layout; + protected BidirectionalTransformer transformer; - public GradientEdgePaintTransformer(Color c1, Color c2, - VisualizationViewer vv) - { - this.c1 = c1; - this.c2 = c2; - this.graph = vv.getModel().getNetwork(); - this.layout = vv.getGraphLayout(); - this.transformer = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); - } - - public Paint apply(E e) - { - EndpointPair endpoints = graph.incidentNodes(e); - N b = endpoints.nodeU(); - N f = endpoints.nodeV(); - Point2D pb = transformer.transform(layout.apply(b)); - Point2D pf = transformer.transform(layout.apply(f)); - float xB = (float) pb.getX(); - float yB = (float) pb.getY(); - float xF = (float) pf.getX(); - float yF = (float) pf.getY(); - if (!graph.isDirected()) { - xF = (xF + xB) / 2; - yF = (yF + yB) / 2; - } - if (isSelfLoop(endpoints)) { - yF += 50; - xF += 50; - } + public GradientEdgePaintTransformer(Color c1, Color c2, VisualizationViewer vv) { + this.c1 = c1; + this.c2 = c2; + this.graph = vv.getModel().getNetwork(); + this.layout = vv.getGraphLayout(); + this.transformer = + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT); + } - return new GradientPaint(xB, yB, getColor1(e), xF, yF, getColor2(e), true); + public Paint apply(E e) { + EndpointPair endpoints = graph.incidentNodes(e); + N b = endpoints.nodeU(); + N f = endpoints.nodeV(); + Point2D pb = transformer.transform(layout.apply(b)); + Point2D pf = transformer.transform(layout.apply(f)); + float xB = (float) pb.getX(); + float yB = (float) pb.getY(); + float xF = (float) pf.getX(); + float yF = (float) pf.getY(); + if (!graph.isDirected()) { + xF = (xF + xB) / 2; + yF = (yF + yB) / 2; } - - /** - * Returns c1. Subclasses may override - * this method to enable more complex behavior (e.g., for - * picked edges). - * @param e the edge for which a color is to be retrieved - * @return the constructor-supplied color {@code c1} - */ - protected Color getColor1(E e) - { - return c1; + if (isSelfLoop(endpoints)) { + yF += 50; + xF += 50; } - /** - * Returns c2. Subclasses may override - * this method to enable more complex behavior (e.g., for - * picked edges). - * @param e the edge for which a color is to be retrieved - * @return the constructor-supplied color {@code c2} - */ - protected Color getColor2(E e) - { - return c2; - } + return new GradientPaint(xB, yB, getColor1(e), xF, yF, getColor2(e), true); + } + + /** + * Returns c1. Subclasses may override this method to enable more complex behavior + * (e.g., for picked edges). + * + * @param e the edge for which a color is to be retrieved + * @return the constructor-supplied color {@code c1} + */ + protected Color getColor1(E e) { + return c1; + } + + /** + * Returns c2. Subclasses may override this method to enable more complex behavior + * (e.g., for picked edges). + * + * @param e the edge for which a color is to be retrieved + * @return the constructor-supplied color {@code c2} + */ + protected Color getColor2(E e) { + return c2; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/InterpolatingVertexSizeTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/InterpolatingVertexSizeTransformer.java index c6c92e58..b7c1572e 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/InterpolatingVertexSizeTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/InterpolatingVertexSizeTransformer.java @@ -1,7 +1,7 @@ /* * Created on Nov 3, 2005 * - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -13,60 +13,50 @@ import com.google.common.base.Function; - /** - * Provides vertex sizes that are spaced proportionally between - * min_size and max_size depending on - * + * Provides vertex sizes that are spaced proportionally between min_size and max_size depending on + * * @author Joshua O'Madadhain */ -public class InterpolatingVertexSizeTransformer implements Function -{ - protected double min; - protected double max; - protected Function values; - protected int min_size; - protected int size_diff; - - public InterpolatingVertexSizeTransformer(Function values, - int min_size, int max_size) - { - super(); - if (min_size < 0 || max_size < 0) - throw new IllegalArgumentException("sizes must be non-negative"); - if (min_size > max_size) - throw new IllegalArgumentException("min_size must be <= max_size"); - this.min = 0; - this.max = 0; - this.values = values; - setMinSize(min_size); - setMaxSize(max_size); - } +public class InterpolatingVertexSizeTransformer implements Function { + protected double min; + protected double max; + protected Function values; + protected int min_size; + protected int size_diff; + + public InterpolatingVertexSizeTransformer( + Function values, int min_size, int max_size) { + super(); + if (min_size < 0 || max_size < 0) + throw new IllegalArgumentException("sizes must be non-negative"); + if (min_size > max_size) throw new IllegalArgumentException("min_size must be <= max_size"); + this.min = 0; + this.max = 0; + this.values = values; + setMinSize(min_size); + setMaxSize(max_size); + } + + public Integer apply(V v) { + Number n = values.apply(v); + double value = min; + if (n != null) value = n.doubleValue(); + min = Math.min(this.min, value); + max = Math.max(this.max, value); + + if (min == max) return min_size; + + // interpolate between min and max sizes based on how big value is + // with respect to min and max values + return min_size + (int) (((value - min) / (max - min)) * size_diff); + } - public Integer apply(V v) - { - Number n = values.apply(v); - double value = min; - if (n != null) - value = n.doubleValue(); - min = Math.min(this.min, value); - max = Math.max(this.max, value); - - if (min == max) - return min_size; - - // interpolate between min and max sizes based on how big value is - // with respect to min and max values - return min_size + (int)(((value - min) / (max - min)) * size_diff); - } - - public void setMinSize(int min_size) - { - this.min_size = min_size; - } + public void setMinSize(int min_size) { + this.min_size = min_size; + } - public void setMaxSize(int max_size) - { - this.size_diff = max_size - this.min_size; - } + public void setMaxSize(int max_size) { + this.size_diff = max_size - this.min_size; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/NumberFormattingTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/NumberFormattingTransformer.java index 8dad0b87..4decfe76 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/NumberFormattingTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/NumberFormattingTransformer.java @@ -1,7 +1,7 @@ /* * Created on Feb 16, 2009 * - * Copyright (c) 2009, The JUNG Authors + * Copyright (c) 2009, The JUNG Authors * * All rights reserved. * @@ -11,32 +11,25 @@ */ package edu.uci.ics.jung.visualization.decorators; -import java.text.NumberFormat; - import com.google.common.base.Function; +import java.text.NumberFormat; /** - * Transforms inputs to String representations by chaining an input - * {@code Number}-generating {@code Function} with an internal - * {@code NumberFormat} instance. + * Transforms inputs to String representations by chaining an input {@code Number}-generating {@code + * Function} with an internal {@code NumberFormat} instance. + * * @author Joshua O'Madadhain */ -public class NumberFormattingTransformer implements Function -{ - private Function values; - private NumberFormat formatter = NumberFormat.getInstance(); - - public NumberFormattingTransformer(Function values) - { - this.values = values; - } +public class NumberFormattingTransformer implements Function { + private Function values; + private NumberFormat formatter = NumberFormat.getInstance(); - /** - * Returns a formatted string for the input. - */ - public String apply(T input) - { - return formatter.format(values.apply(input)); - } + public NumberFormattingTransformer(Function values) { + this.values = values; + } + /** Returns a formatted string for the input. */ + public String apply(T input) { + return formatter.format(values.apply(input)); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/PickableEdgePaintTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/PickableEdgePaintTransformer.java index 9998ae85..8d98fd98 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/PickableEdgePaintTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/PickableEdgePaintTransformer.java @@ -1,7 +1,7 @@ /* * Created on Mar 10, 2005 * - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,49 +11,40 @@ */ package edu.uci.ics.jung.visualization.decorators; -import java.awt.Paint; - import com.google.common.base.Function; - import edu.uci.ics.jung.visualization.picking.PickedInfo; +import java.awt.Paint; /** - * Paints each edge according to the Paint - * parameters given in the constructor, so that picked and - * non-picked edges can be made to look different. - * - * @author Tom Nelson + * Paints each edge according to the Paint parameters given in the constructor, so that + * picked and non-picked edges can be made to look different. + * + * @author Tom Nelson * @author Joshua O'Madadhain - * */ -public class PickableEdgePaintTransformer implements Function { - protected PickedInfo pi; - protected Paint draw_paint; - protected Paint picked_paint; +public class PickableEdgePaintTransformer implements Function { + protected PickedInfo pi; + protected Paint draw_paint; + protected Paint picked_paint; - /** - * - * @param pi specifies which vertices report as "picked" - * @param draw_paint Paint used to draw edge shapes - * @param picked_paint Paint used to draw picked edge shapes - */ - public PickableEdgePaintTransformer(PickedInfo pi, Paint draw_paint, Paint picked_paint) { - if (pi == null) - throw new IllegalArgumentException("PickedInfo instance must be non-null"); - this.pi = pi; - this.draw_paint = draw_paint; - this.picked_paint = picked_paint; - } - - /** - * - */ - public Paint apply(E e) { - if (pi.isPicked(e)) { - return picked_paint; - } - else { - return draw_paint; - } + /** + * @param pi specifies which vertices report as "picked" + * @param draw_paint Paint used to draw edge shapes + * @param picked_paint Paint used to draw picked edge shapes + */ + public PickableEdgePaintTransformer(PickedInfo pi, Paint draw_paint, Paint picked_paint) { + if (pi == null) throw new IllegalArgumentException("PickedInfo instance must be non-null"); + this.pi = pi; + this.draw_paint = draw_paint; + this.picked_paint = picked_paint; + } + + /** */ + public Paint apply(E e) { + if (pi.isPicked(e)) { + return picked_paint; + } else { + return draw_paint; } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/PickableVertexIconTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/PickableVertexIconTransformer.java index 9c1291c2..4444a2fc 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/PickableVertexIconTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/PickableVertexIconTransformer.java @@ -1,55 +1,45 @@ /* -* Created on Mar 10, 2005 -* -* Copyright (c) 2005, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Created on Mar 10, 2005 + * + * Copyright (c) 2005, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.visualization.decorators; -import javax.swing.Icon; - import com.google.common.base.Function; - import edu.uci.ics.jung.visualization.picking.PickedInfo; +import javax.swing.Icon; /** - * Supplies an Icon for each vertex according to the Icon - * parameters given in the constructor, so that picked and - * non-picked vertices can be made to look different. + * Supplies an Icon for each vertex according to the Icon parameters given in the + * constructor, so that picked and non-picked vertices can be made to look different. */ -public class PickableVertexIconTransformer implements Function { +public class PickableVertexIconTransformer implements Function { + + protected Icon icon; + protected Icon picked_icon; + protected PickedInfo pi; - protected Icon icon; - protected Icon picked_icon; - protected PickedInfo pi; - - /** - * - * @param pi specifies which vertices report as "picked" - * @param icon Icon used to represent vertices - * @param picked_icon Icon used to represent picked vertices - */ - public PickableVertexIconTransformer(PickedInfo pi, Icon icon, Icon picked_icon) - { - if (pi == null) - throw new IllegalArgumentException("PickedInfo instance must be non-null"); - this.pi = pi; - this.icon = icon; - this.picked_icon = picked_icon; - } + /** + * @param pi specifies which vertices report as "picked" + * @param icon Icon used to represent vertices + * @param picked_icon Icon used to represent picked vertices + */ + public PickableVertexIconTransformer(PickedInfo pi, Icon icon, Icon picked_icon) { + if (pi == null) throw new IllegalArgumentException("PickedInfo instance must be non-null"); + this.pi = pi; + this.icon = icon; + this.picked_icon = picked_icon; + } - /** - * Returns the appropriate Icon, depending on picked state. - */ - public Icon apply(V v) { - if (pi.isPicked(v)) - return picked_icon; - else - return icon; - } + /** Returns the appropriate Icon, depending on picked state. */ + public Icon apply(V v) { + if (pi.isPicked(v)) return picked_icon; + else return icon; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/PickableVertexPaintTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/PickableVertexPaintTransformer.java index 14d4b6d2..70f8ccb6 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/PickableVertexPaintTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/PickableVertexPaintTransformer.java @@ -1,55 +1,44 @@ /* -* Created on Mar 10, 2005 -* -* Copyright (c) 2005, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Created on Mar 10, 2005 + * + * Copyright (c) 2005, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.visualization.decorators; -import java.awt.Paint; - import com.google.common.base.Function; - import edu.uci.ics.jung.visualization.picking.PickedInfo; +import java.awt.Paint; /** - * Paints each vertex according to the Paint - * parameters given in the constructor, so that picked and - * non-picked vertices can be made to look different. + * Paints each vertex according to the Paint parameters given in the constructor, so + * that picked and non-picked vertices can be made to look different. */ -public class PickableVertexPaintTransformer implements Function { +public class PickableVertexPaintTransformer implements Function { - protected Paint fill_paint; - protected Paint picked_paint; - protected PickedInfo pi; - - /** - * - * @param pi specifies which vertices report as "picked" - * @param fill_paint Paint used to fill vertex shapes - * @param picked_paint Paint used to fill picked vertex shapes - */ - public PickableVertexPaintTransformer(PickedInfo pi, - Paint fill_paint, Paint picked_paint) - { - if (pi == null) - throw new IllegalArgumentException("PickedInfo instance must be non-null"); - this.pi = pi; - this.fill_paint = fill_paint; - this.picked_paint = picked_paint; - } + protected Paint fill_paint; + protected Paint picked_paint; + protected PickedInfo pi; - public Paint apply(V v) - { - if (pi.isPicked(v)) - return picked_paint; - else - return fill_paint; - } + /** + * @param pi specifies which vertices report as "picked" + * @param fill_paint Paint used to fill vertex shapes + * @param picked_paint Paint used to fill picked vertex shapes + */ + public PickableVertexPaintTransformer(PickedInfo pi, Paint fill_paint, Paint picked_paint) { + if (pi == null) throw new IllegalArgumentException("PickedInfo instance must be non-null"); + this.pi = pi; + this.fill_paint = fill_paint; + this.picked_paint = picked_paint; + } + public Paint apply(V v) { + if (pi.isPicked(v)) return picked_paint; + else return fill_paint; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/SettableVertexShapeTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/SettableVertexShapeTransformer.java index 5a1d9af7..4df6c539 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/SettableVertexShapeTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/SettableVertexShapeTransformer.java @@ -1,7 +1,7 @@ /* * Created on Jul 18, 2004 * - * Copyright (c) 2004, The JUNG Authors + * Copyright (c) 2004, The JUNG Authors * * All rights reserved. * @@ -11,19 +11,12 @@ */ package edu.uci.ics.jung.visualization.decorators; -import java.awt.Shape; - import com.google.common.base.Function; +import java.awt.Shape; +/** @author Joshua O'Madadhain */ +public interface SettableVertexShapeTransformer extends Function { + public abstract void setSizeTransformer(Function vsf); - -/** - * - * @author Joshua O'Madadhain - */ -public interface SettableVertexShapeTransformer extends Function -{ - public abstract void setSizeTransformer(Function vsf); - - public abstract void setAspectRatioTransformer(Function varf); -} \ No newline at end of file + public abstract void setAspectRatioTransformer(Function varf); +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/ToStringLabeller.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/ToStringLabeller.java index 9dac59be..0578b0b4 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/ToStringLabeller.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/ToStringLabeller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, The JUNG Authors + * Copyright (c) 2003, The JUNG Authors * * All rights reserved. * @@ -14,22 +14,16 @@ import com.google.common.base.Function; - - /** - * Labels vertices by their toString. This class functions as a drop-in - * replacement for the default StringLabeller method. This class does not - * guarantee unique labels; or even consistent ones. - * + * Labels vertices by their toString. This class functions as a drop-in replacement for the default + * StringLabeller method. This class does not guarantee unique labels; or even consistent ones. + * * @author danyelf */ public class ToStringLabeller implements Function { - /** - * @return o.toString() - */ - public String apply(Object o) { - return o.toString(); - } - - } \ No newline at end of file + /** @return o.toString() */ + public String apply(Object o) { + return o.toString(); + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/VertexIconShapeTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/VertexIconShapeTransformer.java index 56a3cddd..efaa7622 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/VertexIconShapeTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/decorators/VertexIconShapeTransformer.java @@ -10,107 +10,90 @@ package edu.uci.ics.jung.visualization.decorators; +import com.google.common.base.Function; +import edu.uci.ics.jung.visualization.util.ImageShapeUtils; import java.awt.Image; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.util.HashMap; import java.util.Map; - import javax.swing.Icon; import javax.swing.ImageIcon; -import com.google.common.base.Function; - -import edu.uci.ics.jung.visualization.util.ImageShapeUtils; - /** - * A default implementation that stores images in a Map keyed on the - * vertex. Also applies a shaping function to images to extract the - * shape of the opaque part of a transparent image. - * - * @author Tom Nelson + * A default implementation that stores images in a Map keyed on the vertex. Also applies a shaping + * function to images to extract the shape of the opaque part of a transparent image. + * + * @author Tom Nelson */ -public class VertexIconShapeTransformer implements Function { - protected Map shapeMap = new HashMap(); - protected Map iconMap; - protected Function delegate; - - /** - * Creates an instance with the specified delegate. - * @param delegate the vertex-to-shape function to use if no image is present for the vertex - */ - public VertexIconShapeTransformer(Function delegate) { - this.delegate = delegate; - } +public class VertexIconShapeTransformer implements Function { + protected Map shapeMap = new HashMap(); + protected Map iconMap; + protected Function delegate; - /** - * @return Returns the delegate. - */ - public Function getDelegate() { - return delegate; - } + /** + * Creates an instance with the specified delegate. + * + * @param delegate the vertex-to-shape function to use if no image is present for the vertex + */ + public VertexIconShapeTransformer(Function delegate) { + this.delegate = delegate; + } - /** - * @param delegate The delegate to set. - */ - public void setDelegate(Function delegate) { - this.delegate = delegate; - } + /** @return Returns the delegate. */ + public Function getDelegate() { + return delegate; + } + + /** @param delegate The delegate to set. */ + public void setDelegate(Function delegate) { + this.delegate = delegate; + } - /** - * get the shape from the image. If not available, get - * the shape from the delegate VertexShapeFunction - */ - public Shape apply(V v) { - Icon icon = iconMap.get(v); - if (icon != null && icon instanceof ImageIcon) { - Image image = ((ImageIcon) icon).getImage(); - Shape shape = (Shape) shapeMap.get(image); - if (shape == null) { - shape = ImageShapeUtils.getShape(image, 30); - if(shape.getBounds().getWidth() > 0 && - shape.getBounds().getHeight() > 0) { - // don't cache a zero-sized shape, wait for the image - // to be ready - int width = image.getWidth(null); - int height = image.getHeight(null); - AffineTransform transform = AffineTransform - .getTranslateInstance(-width / 2, -height / 2); - shape = transform.createTransformedShape(shape); - shapeMap.put(image, shape); - } - } - return shape; - } else { - return delegate.apply(v); - } - } + /** + * get the shape from the image. If not available, get the shape from the delegate + * VertexShapeFunction + */ + public Shape apply(V v) { + Icon icon = iconMap.get(v); + if (icon != null && icon instanceof ImageIcon) { + Image image = ((ImageIcon) icon).getImage(); + Shape shape = (Shape) shapeMap.get(image); + if (shape == null) { + shape = ImageShapeUtils.getShape(image, 30); + if (shape.getBounds().getWidth() > 0 && shape.getBounds().getHeight() > 0) { + // don't cache a zero-sized shape, wait for the image + // to be ready + int width = image.getWidth(null); + int height = image.getHeight(null); + AffineTransform transform = AffineTransform.getTranslateInstance(-width / 2, -height / 2); + shape = transform.createTransformedShape(shape); + shapeMap.put(image, shape); + } + } + return shape; + } else { + return delegate.apply(v); + } + } - /** - * @return the iconMap - */ - public Map getIconMap() { - return iconMap; - } + /** @return the iconMap */ + public Map getIconMap() { + return iconMap; + } - /** - * @param iconMap the iconMap to set - */ - public void setIconMap(Map iconMap) { - this.iconMap = iconMap; - } + /** @param iconMap the iconMap to set */ + public void setIconMap(Map iconMap) { + this.iconMap = iconMap; + } - /** - * @return the shapeMap - */ - public Map getShapeMap() { - return shapeMap; - } + /** @return the shapeMap */ + public Map getShapeMap() { + return shapeMap; + } - /** - * @param shapeMap the shapeMap to set - */ - public void setShapeMap(Map shapeMap) { - this.shapeMap = shapeMap; - } + /** @param shapeMap the shapeMap to set */ + public void setShapeMap(Map shapeMap) { + this.shapeMap = shapeMap; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/BoundingRectangleCollector.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/BoundingRectangleCollector.java index 1fe25c31..7ccbfc8e 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/BoundingRectangleCollector.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/BoundingRectangleCollector.java @@ -1,5 +1,9 @@ package edu.uci.ics.jung.visualization.layout; +import com.google.common.graph.EndpointPair; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.RenderContext; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; @@ -7,81 +11,73 @@ import java.util.ArrayList; import java.util.List; -import com.google.common.graph.EndpointPair; -import com.google.common.graph.Network; +public class BoundingRectangleCollector { -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.RenderContext; + protected RenderContext rc; + protected Network graph; + protected Layout layout; + protected List rectangles = new ArrayList(); + + public BoundingRectangleCollector(RenderContext rc, Layout layout) { + this.rc = rc; + this.layout = layout; + this.graph = rc.getNetwork(); + compute(); + } + + /** @return the rectangles */ + public List getRectangles() { + return rectangles; + } -public class BoundingRectangleCollector { + public void compute() { + rectangles.clear(); + // Graphics2D g2d = (Graphics2D)g; + // g.setColor(Color.cyan); - protected RenderContext rc; - protected Network graph; - protected Layout layout; - protected List rectangles = new ArrayList(); - - public BoundingRectangleCollector(RenderContext rc, Layout layout) { - this.rc = rc; - this.layout = layout; - this.graph = rc.getNetwork(); - compute(); - } + for (E e : graph.edges()) { + EndpointPair endpoints = graph.incidentNodes(e); + V v1 = endpoints.nodeU(); + V v2 = endpoints.nodeV(); + Point2D p1 = layout.apply(v1); + Point2D p2 = layout.apply(v2); + float x1 = (float) p1.getX(); + float y1 = (float) p1.getY(); + float x2 = (float) p2.getX(); + float y2 = (float) p2.getY(); - /** - * @return the rectangles - */ - public List getRectangles() { - return rectangles; - } + boolean isLoop = v1.equals(v2); + Shape s2 = rc.getVertexShapeTransformer().apply(v2); + Shape edgeShape = rc.getEdgeShapeTransformer().apply(e); - public void compute() { - rectangles.clear(); -// Graphics2D g2d = (Graphics2D)g; -// g.setColor(Color.cyan); + AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); - for(E e : graph.edges()) { - EndpointPair endpoints = graph.incidentNodes(e); - V v1 = endpoints.nodeU(); - V v2 = endpoints.nodeV(); - Point2D p1 = layout.apply(v1); - Point2D p2 = layout.apply(v2); - float x1 = (float)p1.getX(); - float y1 = (float)p1.getY(); - float x2 = (float)p2.getX(); - float y2 = (float)p2.getY(); - - boolean isLoop = v1.equals(v2); - Shape s2 = rc.getVertexShapeTransformer().apply(v2); - Shape edgeShape = rc.getEdgeShapeTransformer().apply(e); + if (isLoop) { + Rectangle2D s2Bounds = s2.getBounds2D(); + xform.scale(s2Bounds.getWidth(), s2Bounds.getHeight()); + xform.translate(0, -edgeShape.getBounds2D().getWidth() / 2); + } else { + float dx = x2 - x1; + float dy = y2 - y1; + float theta = (float) Math.atan2(dy, dx); + xform.rotate(theta); + float dist = (float) p1.distance(p2); + xform.scale(dist, 1.0); + } + edgeShape = xform.createTransformedShape(edgeShape); + // edgeShape = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, edgeShape); + rectangles.add(edgeShape.getBounds2D()); + } - AffineTransform xform = AffineTransform.getTranslateInstance(x1,y1); - - if(isLoop) { - Rectangle2D s2Bounds = s2.getBounds2D(); - xform.scale(s2Bounds.getWidth(), s2Bounds.getHeight()); - xform.translate(0, -edgeShape.getBounds2D().getWidth()/2); - } else { - float dx = x2-x1; - float dy = y2-y1; - float theta = (float)Math.atan2(dy,dx); - xform.rotate(theta); - float dist = (float)p1.distance(p2); - xform.scale(dist, 1.0); - } - edgeShape = xform.createTransformedShape(edgeShape); -// edgeShape = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, edgeShape); - rectangles.add(edgeShape.getBounds2D()); - } - - for(V v : graph.nodes()) { - Shape shape = rc.getVertexShapeTransformer().apply(v); - Point2D p = layout.apply(v); -// p = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p); - float x = (float)p.getX(); - float y = (float)p.getY(); - AffineTransform xform = AffineTransform.getTranslateInstance(x, y); - shape = xform.createTransformedShape(shape); - rectangles.add(shape.getBounds2D()); - } - } + for (V v : graph.nodes()) { + Shape shape = rc.getVertexShapeTransformer().apply(v); + Point2D p = layout.apply(v); + // p = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p); + float x = (float) p.getX(); + float y = (float) p.getY(); + AffineTransform xform = AffineTransform.getTranslateInstance(x, y); + shape = xform.createTransformedShape(shape); + rectangles.add(shape.getBounds2D()); + } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/BoundingRectanglePaintable.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/BoundingRectanglePaintable.java index 1c870f9a..c5b16319 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/BoundingRectanglePaintable.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/BoundingRectanglePaintable.java @@ -1,57 +1,56 @@ package edu.uci.ics.jung.visualization.layout; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.VisualizationServer; +import edu.uci.ics.jung.visualization.util.ChangeEventSupport; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.util.List; - import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import com.google.common.graph.Network; - -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.VisualizationServer; -import edu.uci.ics.jung.visualization.util.ChangeEventSupport; - -public class BoundingRectanglePaintable implements VisualizationServer.Paintable { - - protected RenderContext rc; - protected Network graph; - protected Layout layout; - protected List rectangles; - - public BoundingRectanglePaintable(RenderContext rc, Layout layout) { - super(); - this.rc = rc; - this.layout = layout; - this.graph = rc.getNetwork(); - final BoundingRectangleCollector brc = new BoundingRectangleCollector(rc, layout); - this.rectangles = brc.getRectangles(); - if(layout instanceof ChangeEventSupport) { - ((ChangeEventSupport)layout).addChangeListener(new ChangeListener() { - - public void stateChanged(ChangeEvent e) { - brc.compute(); - rectangles = brc.getRectangles(); - }}); - } - } - - public void paint(Graphics g) { - Graphics2D g2d = (Graphics2D)g; - g.setColor(Color.cyan); - - for(Rectangle2D r : rectangles) { - g2d.draw(rc.getMultiLayerTransformer().transform(Layer.LAYOUT, r)); - } - } - - public boolean useTransform() { - return true; - } - +public class BoundingRectanglePaintable implements VisualizationServer.Paintable { + + protected RenderContext rc; + protected Network graph; + protected Layout layout; + protected List rectangles; + + public BoundingRectanglePaintable(RenderContext rc, Layout layout) { + super(); + this.rc = rc; + this.layout = layout; + this.graph = rc.getNetwork(); + final BoundingRectangleCollector brc = new BoundingRectangleCollector(rc, layout); + this.rectangles = brc.getRectangles(); + if (layout instanceof ChangeEventSupport) { + ((ChangeEventSupport) layout) + .addChangeListener( + new ChangeListener() { + + public void stateChanged(ChangeEvent e) { + brc.compute(); + rectangles = brc.getRectangles(); + } + }); + } + } + + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + g.setColor(Color.cyan); + + for (Rectangle2D r : rectangles) { + g2d.draw(rc.getMultiLayerTransformer().transform(Layer.LAYOUT, r)); + } + } + + public boolean useTransform() { + return true; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/CachingLayout.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/CachingLayout.java index 5f739601..d1597066 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/CachingLayout.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/CachingLayout.java @@ -10,60 +10,61 @@ package edu.uci.ics.jung.visualization.layout; -import java.awt.geom.Point2D; -import java.util.Set; - import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; - import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.algorithms.layout.LayoutDecorator; import edu.uci.ics.jung.visualization.util.Caching; +import java.awt.geom.Point2D; +import java.util.Set; /** - * A LayoutDecorator that caches locations in a clearable Map. This can be used to ensure that - * edge endpoints are always the same as vertex locations when they are drawn in the render loop - * during the time that the layout's relaxer thread is changing the locations. - * - * @see LayoutDecorator - * @author Tom Nelson + * A LayoutDecorator that caches locations in a clearable Map. This can be used to ensure that edge + * endpoints are always the same as vertex locations when they are drawn in the render loop during + * the time that the layout's relaxer thread is changing the locations. * + * @see LayoutDecorator + * @author Tom Nelson */ public class CachingLayout extends LayoutDecorator implements Caching { - - protected LoadingCache locations; - public CachingLayout(Layout delegate) { - super(delegate); - Function chain = Functions.compose( - new Function() { - public Point2D apply(Point2D p) { - return (Point2D)p.clone(); - }}, - delegate); - this.locations = CacheBuilder.newBuilder().build(CacheLoader.from(chain)); - } - - public void clear() { - this.locations = CacheBuilder.newBuilder().build(new CacheLoader() { - public Point2D load(V vertex) { - return new Point2D.Double(); - } - }); - } + protected LoadingCache locations; + + public CachingLayout(Layout delegate) { + super(delegate); + Function chain = + Functions.compose( + new Function() { + public Point2D apply(Point2D p) { + return (Point2D) p.clone(); + } + }, + delegate); + this.locations = CacheBuilder.newBuilder().build(CacheLoader.from(chain)); + } + + public void clear() { + this.locations = + CacheBuilder.newBuilder() + .build( + new CacheLoader() { + public Point2D load(V vertex) { + return new Point2D.Double(); + } + }); + } - public void init() { - } + public void init() {} - public Point2D apply(V v) { - return locations.getUnchecked(v); - } + public Point2D apply(V v) { + return locations.getUnchecked(v); + } - @Override - public Set nodes() { - return delegate.nodes(); - } + @Override + public Set nodes() { + return delegate.nodes(); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutChangeListener.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutChangeListener.java index 097b5d4c..9d3fcb2b 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutChangeListener.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutChangeListener.java @@ -1,7 +1,6 @@ package edu.uci.ics.jung.visualization.layout; public interface LayoutChangeListener { - - void layoutChanged(LayoutEvent evt); + void layoutChanged(LayoutEvent evt); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutEvent.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutEvent.java index cc7463a5..12f249f1 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutEvent.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutEvent.java @@ -3,24 +3,28 @@ import com.google.common.graph.Graph; public class LayoutEvent { - - V vertex; - Graph graph; - - public LayoutEvent(V vertex, Graph graph) { - this.vertex = vertex; - this.graph = graph; - } - public V getVertex() { - return vertex; - } - public void setVertex(V vertex) { - this.vertex = vertex; - } - public Graph getGraph() { - return graph; - } - public void setGraph(Graph graph) { - this.graph = graph; - } + + V vertex; + Graph graph; + + public LayoutEvent(V vertex, Graph graph) { + this.vertex = vertex; + this.graph = graph; + } + + public V getVertex() { + return vertex; + } + + public void setVertex(V vertex) { + this.vertex = vertex; + } + + public Graph getGraph() { + return graph; + } + + public void setGraph(Graph graph) { + this.graph = graph; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutEventSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutEventSupport.java index f1f55537..f5782973 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutEventSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutEventSupport.java @@ -1,9 +1,8 @@ package edu.uci.ics.jung.visualization.layout; public interface LayoutEventSupport { - - void addLayoutChangeListener(LayoutChangeListener listener); - - void removeLayoutChangeListener(LayoutChangeListener listener); + void addLayoutChangeListener(LayoutChangeListener listener); + + void removeLayoutChangeListener(LayoutChangeListener listener); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutTransition.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutTransition.java index 318a718b..4cdf3939 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutTransition.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/LayoutTransition.java @@ -1,55 +1,53 @@ package edu.uci.ics.jung.visualization.layout; -import java.awt.geom.Point2D; - import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.algorithms.layout.StaticLayout; import edu.uci.ics.jung.algorithms.layout.util.Relaxer; import edu.uci.ics.jung.algorithms.layout.util.VisRunner; import edu.uci.ics.jung.algorithms.util.IterativeContext; import edu.uci.ics.jung.visualization.VisualizationViewer; +import java.awt.geom.Point2D; + +public class LayoutTransition implements IterativeContext { -public class LayoutTransition implements IterativeContext { - - protected Layout startLayout; - protected Layout endLayout; - protected Layout transitionLayout; - protected boolean done = false; - protected int count = 20; - protected int counter = 0; - protected VisualizationViewer vv; + protected Layout startLayout; + protected Layout endLayout; + protected Layout transitionLayout; + protected boolean done = false; + protected int count = 20; + protected int counter = 0; + protected VisualizationViewer vv; - public LayoutTransition(VisualizationViewer vv, Layout startLayout, Layout endLayout) { - this.vv = vv; - this.startLayout = startLayout; - this.endLayout = endLayout; - if(endLayout instanceof IterativeContext) { - Relaxer relaxer = new VisRunner((IterativeContext)endLayout); - relaxer.prerelax(); - } - this.transitionLayout = - new StaticLayout(vv.getModel().getNetwork().asGraph(), startLayout); - vv.setGraphLayout(transitionLayout); - } + public LayoutTransition( + VisualizationViewer vv, Layout startLayout, Layout endLayout) { + this.vv = vv; + this.startLayout = startLayout; + this.endLayout = endLayout; + if (endLayout instanceof IterativeContext) { + Relaxer relaxer = new VisRunner((IterativeContext) endLayout); + relaxer.prerelax(); + } + this.transitionLayout = new StaticLayout(vv.getModel().getNetwork().asGraph(), startLayout); + vv.setGraphLayout(transitionLayout); + } - public boolean done() { - return done; - } + public boolean done() { + return done; + } - public void step() { - for(V v : vv.getModel().getNetwork().nodes()) { - Point2D tp = transitionLayout.apply(v); - Point2D fp = endLayout.apply(v); - double dx = (fp.getX()-tp.getX())/(count-counter); - double dy = (fp.getY()-tp.getY())/(count-counter); - transitionLayout.setLocation(v, - new Point2D.Double(tp.getX()+dx,tp.getY()+dy)); - } - counter++; - if(counter >= count) { - done = true; - vv.setGraphLayout(endLayout); - } - vv.repaint(); - } + public void step() { + for (V v : vv.getModel().getNetwork().nodes()) { + Point2D tp = transitionLayout.apply(v); + Point2D fp = endLayout.apply(v); + double dx = (fp.getX() - tp.getX()) / (count - counter); + double dy = (fp.getY() - tp.getY()) / (count - counter); + transitionLayout.setLocation(v, new Point2D.Double(tp.getX() + dx, tp.getY() + dy)); + } + counter++; + if (counter >= count) { + done = true; + vv.setGraphLayout(endLayout); + } + vv.repaint(); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/ObservableCachingLayout.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/ObservableCachingLayout.java index eb692b69..a9c1d9b0 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/ObservableCachingLayout.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/layout/ObservableCachingLayout.java @@ -10,125 +10,118 @@ package edu.uci.ics.jung.visualization.layout; -import java.awt.geom.Point2D; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import javax.swing.event.ChangeListener; - import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.graph.Graph; - import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.algorithms.layout.LayoutDecorator; import edu.uci.ics.jung.algorithms.util.IterativeContext; import edu.uci.ics.jung.visualization.util.Caching; import edu.uci.ics.jung.visualization.util.ChangeEventSupport; import edu.uci.ics.jung.visualization.util.DefaultChangeEventSupport; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import javax.swing.event.ChangeListener; /** - * A LayoutDecorator that fires ChangeEvents when certain methods - * are called. Used to wrap a Layout so that the visualization - * components can be notified of changes. - * - * @see LayoutDecorator - * @author Tom Nelson + * A LayoutDecorator that fires ChangeEvents when certain methods are called. Used to wrap a Layout + * so that the visualization components can be notified of changes. * + * @see LayoutDecorator + * @author Tom Nelson */ -public class ObservableCachingLayout extends LayoutDecorator - implements ChangeEventSupport, Caching, LayoutEventSupport { - - protected ChangeEventSupport changeSupport = new DefaultChangeEventSupport(this); - protected Graph graph; - - protected LoadingCache locations; - - private List> layoutChangeListeners = - new ArrayList>(); - - public ObservableCachingLayout(Graph graph, Layout delegate) { - super(delegate); - this.graph = graph; - Function chain = Functions. compose( - p -> (Point2D)p.clone(), - delegate); - this.locations = CacheBuilder.newBuilder().build(CacheLoader.from(chain)); - } - - @Override - public void step() { - super.step(); - fireStateChanged(); - } - - @Override - public void initialize() { - super.initialize(); - fireStateChanged(); - } - - @Override - public boolean done() { - if(delegate instanceof IterativeContext) { - return ((IterativeContext)delegate).done(); - } - return true; +public class ObservableCachingLayout extends LayoutDecorator + implements ChangeEventSupport, Caching, LayoutEventSupport { + + protected ChangeEventSupport changeSupport = new DefaultChangeEventSupport(this); + protected Graph graph; + + protected LoadingCache locations; + + private List> layoutChangeListeners = + new ArrayList>(); + + public ObservableCachingLayout(Graph graph, Layout delegate) { + super(delegate); + this.graph = graph; + Function chain = + Functions.compose(p -> (Point2D) p.clone(), delegate); + this.locations = CacheBuilder.newBuilder().build(CacheLoader.from(chain)); + } + + @Override + public void step() { + super.step(); + fireStateChanged(); + } + + @Override + public void initialize() { + super.initialize(); + fireStateChanged(); + } + + @Override + public boolean done() { + if (delegate instanceof IterativeContext) { + return ((IterativeContext) delegate).done(); } - - - @Override - public void setLocation(V v, Point2D location) { - super.setLocation(v, location); - fireStateChanged(); - fireLayoutChanged(v); - } - - public void addChangeListener(ChangeListener l) { - changeSupport.addChangeListener(l); + return true; + } + + @Override + public void setLocation(V v, Point2D location) { + super.setLocation(v, location); + fireStateChanged(); + fireLayoutChanged(v); + } + + public void addChangeListener(ChangeListener l) { + changeSupport.addChangeListener(l); + } + + public void removeChangeListener(ChangeListener l) { + changeSupport.removeChangeListener(l); + } + + public ChangeListener[] getChangeListeners() { + return changeSupport.getChangeListeners(); + } + + public void fireStateChanged() { + changeSupport.fireStateChanged(); + } + + public void clear() { + this.locations.invalidateAll(); + } + + public Point2D apply(V v) { + return locations.getUnchecked(v); + } + + private void fireLayoutChanged(V v) { + LayoutEvent evt = new LayoutEvent(v, graph); + for (LayoutChangeListener listener : layoutChangeListeners) { + listener.layoutChanged(evt); } + } - public void removeChangeListener(ChangeListener l) { - changeSupport.removeChangeListener(l); - } + public void addLayoutChangeListener(LayoutChangeListener listener) { + layoutChangeListeners.add(listener); + } - public ChangeListener[] getChangeListeners() { - return changeSupport.getChangeListeners(); - } + public void removeLayoutChangeListener(LayoutChangeListener listener) { + layoutChangeListeners.remove(listener); + } - public void fireStateChanged() { - changeSupport.fireStateChanged(); - } - - public void clear() { - this.locations.invalidateAll(); - } - - public Point2D apply(V v) { - return locations.getUnchecked(v); - } - - private void fireLayoutChanged(V v) { - LayoutEvent evt = new LayoutEvent(v, graph); - for(LayoutChangeListener listener : layoutChangeListeners) { - listener.layoutChanged(evt); - } - } - - public void addLayoutChangeListener(LayoutChangeListener listener) { - layoutChangeListeners.add(listener); - } - - public void removeLayoutChangeListener(LayoutChangeListener listener) { - layoutChangeListeners.remove(listener); - } - - @Override - public Set nodes() { - return graph.nodes(); - } + @Override + public Set nodes() { + return graph.nodes(); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/AbstractPickedState.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/AbstractPickedState.java index 713c1c63..2dbab738 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/AbstractPickedState.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/AbstractPickedState.java @@ -1,10 +1,10 @@ /* * Copyright (c) 2005, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * * * Created on Apr 2, 2005 */ @@ -12,33 +12,31 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; - import javax.swing.event.EventListenerList; /** * An abstract class to support ItemEvents for PickedState - * + * * @author Tom Nelson */ public abstract class AbstractPickedState implements PickedState { - - protected EventListenerList listenerList = new EventListenerList(); - public void addItemListener(ItemListener l) { - listenerList.add(ItemListener.class, l); - - } + protected EventListenerList listenerList = new EventListenerList(); + + public void addItemListener(ItemListener l) { + listenerList.add(ItemListener.class, l); + } + + public void removeItemListener(ItemListener l) { + listenerList.remove(ItemListener.class, l); + } - public void removeItemListener(ItemListener l) { - listenerList.remove(ItemListener.class, l); + protected void fireItemStateChanged(ItemEvent e) { + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ItemListener.class) { + ((ItemListener) listeners[i + 1]).itemStateChanged(e); + } } - - protected void fireItemStateChanged(ItemEvent e) { - Object[] listeners = listenerList.getListenerList(); - for ( int i = listeners.length-2; i>=0; i-=2 ) { - if ( listeners[i]==ItemListener.class ) { - ((ItemListener)listeners[i+1]).itemStateChanged(e); - } - } - } -} \ No newline at end of file + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/ClosestShapePickSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/ClosestShapePickSupport.java index 50064919..f5000f6c 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/ClosestShapePickSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/ClosestShapePickSupport.java @@ -1,144 +1,123 @@ /** - * Copyright (c) 2008, The JUNG Authors + * Copyright (c) 2008, The JUNG Authors * - * All rights reserved. + *

            All rights reserved. * - * This software is open-source under the BSD license; see either - * "license.txt" or - * https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * Created on Apr 24, 2008 - * + *

            This software is open-source under the BSD license; see either "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. Created on Apr 24, 2008 */ package edu.uci.ics.jung.visualization.picking; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationServer; import java.awt.Shape; import java.awt.geom.Point2D; import java.util.Collection; import java.util.ConcurrentModificationException; -import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationServer; - /** - * A NetworkElementAccessor that finds the closest element to - * the pick point, and returns it if it is within the element's shape. - * This is best suited to elements with convex shapes that do not overlap. - * It differs from ShapePickSupport in that it only checks - * the closest element to see whether it contains the pick point. - * Possible unexpected odd behaviors: + * A NetworkElementAccessor that finds the closest element to the pick point, and + * returns it if it is within the element's shape. This is best suited to elements with convex + * shapes that do not overlap. It differs from ShapePickSupport in that it only checks + * the closest element to see whether it contains the pick point. Possible unexpected odd behaviors: + * *

              - *
            • If the elements overlap, this mechanism may pick another element than the one that's - * "on top" (rendered last) if the pick point is closer to the center of an obscured vertex. - *
            • If element shapes are not convex, then this mechanism may return null - * even if the pick point is inside some element's shape, if the pick point is closer - * to the center of another element. + *
            • If the elements overlap, this mechanism may pick another element than the one that's "on + * top" (rendered last) if the pick point is closer to the center of an obscured vertex. + *
            • If element shapes are not convex, then this mechanism may return null even if + * the pick point is inside some element's shape, if the pick point is closer to the center of + * another element. *
            - * Users who want to avoid either of these should use ShapePickSupport - * instead, which is slower but more flexible. If neither of the above conditions - * (overlapping elements or non-convex shapes) is true, then ShapePickSupport - * and this class should have the same behavior. + * + * Users who want to avoid either of these should use ShapePickSupport instead, which + * is slower but more flexible. If neither of the above conditions (overlapping elements or + * non-convex shapes) is true, then ShapePickSupport and this class should have the + * same behavior. */ -public class ClosestShapePickSupport implements NetworkElementAccessor { - - protected VisualizationServer vv; - protected float pickSize; +public class ClosestShapePickSupport implements NetworkElementAccessor { + + protected VisualizationServer vv; + protected float pickSize; + + /** + * Creates a ShapePickSupport for the vv VisualizationServer, with the + * specified pick footprint. The VisualizationServer is used to fetch the current + * Layout. + * + * @param vv source of the current Layout. + * @param pickSize the size of the pick footprint for line edges + */ + public ClosestShapePickSupport(VisualizationServer vv, float pickSize) { + this.vv = vv; + this.pickSize = pickSize; + } + + /** + * Create a ShapePickSupport with the vv VisualizationServer and default + * pick footprint. The footprint defaults to 2. + * + * @param vv source of the current Layout. + */ + public ClosestShapePickSupport(VisualizationServer vv) { + this.vv = vv; + } + + /** @see edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor#getEdge(double, double) */ + public E getEdge(double x, double y) { + return null; + } + + /** @see edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor#getNode(double, double) */ + @Override + public V getNode(double x, double y) { + Layout layout = vv.getGraphLayout(); + // first, find the closest vertex to (x,y) + double minDistance = Double.MAX_VALUE; + V closest = null; + while (true) { + try { + for (V v : vv.getModel().getNetwork().nodes()) { + Point2D p = layout.apply(v); + double dx = p.getX() - x; + double dy = p.getY() - y; + double dist = dx * dx + dy * dy; + if (dist < minDistance) { + minDistance = dist; + closest = v; + } + } + break; + } catch (ConcurrentModificationException cme) { + } + } - /** - * Creates a ShapePickSupport for the vv - * VisualizationServer, with the specified pick footprint. - * The VisualizationServer is used to fetch the current - * Layout. - * @param vv source of the current Layout. - * @param pickSize the size of the pick footprint for line edges - */ - public ClosestShapePickSupport(VisualizationServer vv, float pickSize) - { - this.vv = vv; - this.pickSize = pickSize; - } - - /** - * Create a ShapePickSupport with the vv - * VisualizationServer and default pick footprint. - * The footprint defaults to 2. - * @param vv source of the current Layout. - */ - public ClosestShapePickSupport(VisualizationServer vv) - { - this.vv = vv; - } + // now check to see whether (x,y) is in the shape for this vertex. - /** - * @see edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor#getEdge(double, double) - */ - public E getEdge(double x, double y) - { - return null; - } + // get the vertex shape + Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(closest); + // get the vertex location + Point2D p = layout.apply(closest); + // transform the vertex location to screen coords + p = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p); - /** - * @see edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor#getNode(double, double) - */ - @Override - public V getNode(double x, double y) - { - Layout layout = vv.getGraphLayout(); - // first, find the closest vertex to (x,y) - double minDistance = Double.MAX_VALUE; - V closest = null; - while(true) - { - try - { - for(V v : vv.getModel().getNetwork().nodes()) - { - Point2D p = layout.apply(v); - double dx = p.getX() - x; - double dy = p.getY() - y; - double dist = dx * dx + dy * dy; - if (dist < minDistance) - { - minDistance = dist; - closest = v; - } - } - break; - } - catch(ConcurrentModificationException cme) {} - } - - // now check to see whether (x,y) is in the shape for this vertex. - - // get the vertex shape - Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(closest); - // get the vertex location - Point2D p = layout.apply(closest); - // transform the vertex location to screen coords - p = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p); - - double ox = x - p.getX(); - double oy = y - p.getY(); + double ox = x - p.getX(); + double oy = y - p.getY(); - if (shape.contains(ox, oy)) - return closest; - else - return null; - } + if (shape.contains(ox, oy)) return closest; + else return null; + } - /** - * @see edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor#getNodes(java.awt.Shape) - */ - @Override - public Collection getNodes(Shape rectangle) - { - // FIXME: RadiusPickSupport and ShapePickSupport are not using the same mechanism! - // talk to Tom and make sure I understand which should be used. - // in particular, there are some transformations that the latter uses; the latter is also - // doing a couple of kinds of filtering. (well, only one--just predicate-based.) - // looks to me like the VV could (should) be doing this filtering. (maybe.) - // - return null; - } + /** @see edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor#getNodes(java.awt.Shape) */ + @Override + public Collection getNodes(Shape rectangle) { + // FIXME: RadiusPickSupport and ShapePickSupport are not using the same mechanism! + // talk to Tom and make sure I understand which should be used. + // in particular, there are some transformations that the latter uses; the latter is also + // doing a couple of kinds of filtering. (well, only one--just predicate-based.) + // looks to me like the VV could (should) be doing this filtering. (maybe.) + // + return null; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/LayoutLensShapePickSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/LayoutLensShapePickSupport.java index e8e86111..201acedc 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/LayoutLensShapePickSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/LayoutLensShapePickSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,6 +11,11 @@ */ package edu.uci.ics.jung.visualization.picking; +import com.google.common.graph.EndpointPair; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationServer; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; @@ -22,184 +27,187 @@ import java.util.HashSet; import java.util.Set; -import com.google.common.graph.EndpointPair; -import com.google.common.graph.Network; - -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationServer; - /** - * ShapePickSupport provides access to Vertices and EdgeType based on - * their actual shapes. - * - * @author Tom Nelson + * ShapePickSupport provides access to Vertices and EdgeType based on their actual shapes. * + * @author Tom Nelson */ -public class LayoutLensShapePickSupport extends ShapePickSupport { - - public LayoutLensShapePickSupport(VisualizationServer vv, float pickSize) { - super(vv,pickSize); - } - - public LayoutLensShapePickSupport(VisualizationServer vv) { - this(vv,2); - } - - @Override - public V getNode(double x, double y) { - - V closest = null; - double minDistance = Double.MAX_VALUE; - Layout layout = vv.getGraphLayout(); - - while(true) { - try { - for(V v : getFilteredVertices()) { - - Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(v); - // get the vertex location - Point2D p = layout.apply(v); - if(p == null) continue; - // transform the vertex location to screen coords - p = vv.getRenderContext().getMultiLayerTransformer().transform(p); - AffineTransform xform = - AffineTransform.getTranslateInstance(p.getX(), p.getY()); - shape = xform.createTransformedShape(shape); - - // see if this vertex center is closest to the pick point - // among any other containing vertices - if(shape.contains(x, y)) { - - if(style == Style.LOWEST) { - // return the first match - return v; - } else if(style == Style.HIGHEST) { - // will return the last match - closest = v; - } else { - Rectangle2D bounds = shape.getBounds2D(); - double dx = bounds.getCenterX() - x; - double dy = bounds.getCenterY() - y; - double dist = dx * dx + dy * dy; - if (dist < minDistance) { - minDistance = dist; - closest = v; - } - } - } - } - break; - } catch(ConcurrentModificationException cme) {} +public class LayoutLensShapePickSupport extends ShapePickSupport { + + public LayoutLensShapePickSupport(VisualizationServer vv, float pickSize) { + super(vv, pickSize); + } + + public LayoutLensShapePickSupport(VisualizationServer vv) { + this(vv, 2); + } + + @Override + public V getNode(double x, double y) { + + V closest = null; + double minDistance = Double.MAX_VALUE; + Layout layout = vv.getGraphLayout(); + + while (true) { + try { + for (V v : getFilteredVertices()) { + + Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(v); + // get the vertex location + Point2D p = layout.apply(v); + if (p == null) continue; + // transform the vertex location to screen coords + p = vv.getRenderContext().getMultiLayerTransformer().transform(p); + AffineTransform xform = AffineTransform.getTranslateInstance(p.getX(), p.getY()); + shape = xform.createTransformedShape(shape); + + // see if this vertex center is closest to the pick point + // among any other containing vertices + if (shape.contains(x, y)) { + + if (style == Style.LOWEST) { + // return the first match + return v; + } else if (style == Style.HIGHEST) { + // will return the last match + closest = v; + } else { + Rectangle2D bounds = shape.getBounds2D(); + double dx = bounds.getCenterX() - x; + double dy = bounds.getCenterY() - y; + double dist = dx * dx + dy * dy; + if (dist < minDistance) { + minDistance = dist; + closest = v; + } + } + } } - return closest; + break; + } catch (ConcurrentModificationException cme) { + } } - - public Collection nodes(Layout layout, Shape rectangle) { - Set pickedVertices = new HashSet(); - - while(true) { - try { - for(V v : getFilteredVertices()) { - Point2D p = layout.apply(v); - if(p == null) continue; - - p = vv.getRenderContext().getMultiLayerTransformer().transform(p); - if(rectangle.contains(p)) { - pickedVertices.add(v); - } - } - break; - } catch(ConcurrentModificationException cme) {} + return closest; + } + + public Collection nodes(Layout layout, Shape rectangle) { + Set pickedVertices = new HashSet(); + + while (true) { + try { + for (V v : getFilteredVertices()) { + Point2D p = layout.apply(v); + if (p == null) continue; + + p = vv.getRenderContext().getMultiLayerTransformer().transform(p); + if (rectangle.contains(p)) { + pickedVertices.add(v); + } } - return pickedVertices; + break; + } catch (ConcurrentModificationException cme) { + } } - - public E getEdge(double x, double y) { - - Layout layout = vv.getGraphLayout(); - Network network = vv.getModel().getNetwork(); - Point2D ip = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, new Point2D.Double(x,y)); - x = ip.getX(); - y = ip.getY(); - - // as a Line has no area, we can't always use edgeshape.contains(point) so we - // make a small rectangular pickArea around the point and check if the - // edgeshape.intersects(pickArea) - Rectangle2D pickArea = - new Rectangle2D.Float((float)x-pickSize/2,(float)y-pickSize/2,pickSize,pickSize); - E closest = null; - double minDistance = Double.MAX_VALUE; - while(true) { - try { - for(E e : getFilteredEdges()) { - EndpointPair endpoints = network.incidentNodes(e); - V v1 = endpoints.nodeU(); - V v2 = endpoints.nodeV(); - boolean isLoop = v1.equals(v2); - Point2D p1 = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, layout.apply(v1)); - Point2D p2 = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, layout.apply(v2)); - if(p1 == null || p2 == null) continue; - float x1 = (float) p1.getX(); - float y1 = (float) p1.getY(); - float x2 = (float) p2.getX(); - float y2 = (float) p2.getY(); - - // translate the edge to the starting vertex - AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); - - Shape edgeShape = vv.getRenderContext().getEdgeShapeTransformer().apply(e); - if(isLoop) { - // make the loops proportional to the size of the vertex - Shape s2 = vv.getRenderContext().getVertexShapeTransformer().apply(v2); - Rectangle2D s2Bounds = s2.getBounds2D(); - xform.scale(s2Bounds.getWidth(),s2Bounds.getHeight()); - // move the loop so that the nadir is centered in the vertex - xform.translate(0, -edgeShape.getBounds2D().getHeight()/2); - } else { - float dx = x2 - x1; - float dy = y2 - y1; - // rotate the edge to the angle between the vertices - double theta = Math.atan2(dy,dx); - xform.rotate(theta); - // stretch the edge to span the distance between the vertices - float dist = (float) Math.sqrt(dx*dx + dy*dy); - xform.scale(dist, 1.0f); - } - - // transform the edge to its location and dimensions - edgeShape = xform.createTransformedShape(edgeShape); - - // because of the transform, the edgeShape is now a GeneralPath - // see if this edge is the closest of any that intersect - if(edgeShape.intersects(pickArea)) { - float cx=0; - float cy=0; - float[] f = new float[6]; - PathIterator pi = new GeneralPath(edgeShape).getPathIterator(null); - if(pi.isDone()==false) { - pi.next(); - pi.currentSegment(f); - cx = f[0]; - cy = f[1]; - if(pi.isDone()==false) { - pi.currentSegment(f); - cx = f[0]; - cy = f[1]; - } - } - float dx = (float) (cx - x); - float dy = (float) (cy - y); - float dist = dx * dx + dy * dy; - if (dist < minDistance) { - minDistance = dist; - closest = e; - } - } - } - break; - } catch(ConcurrentModificationException cme) {} - } - return closest; + return pickedVertices; + } + + public E getEdge(double x, double y) { + + Layout layout = vv.getGraphLayout(); + Network network = vv.getModel().getNetwork(); + Point2D ip = + vv.getRenderContext() + .getMultiLayerTransformer() + .inverseTransform(Layer.VIEW, new Point2D.Double(x, y)); + x = ip.getX(); + y = ip.getY(); + + // as a Line has no area, we can't always use edgeshape.contains(point) so we + // make a small rectangular pickArea around the point and check if the + // edgeshape.intersects(pickArea) + Rectangle2D pickArea = + new Rectangle2D.Float( + (float) x - pickSize / 2, (float) y - pickSize / 2, pickSize, pickSize); + E closest = null; + double minDistance = Double.MAX_VALUE; + while (true) { + try { + for (E e : getFilteredEdges()) { + EndpointPair endpoints = network.incidentNodes(e); + V v1 = endpoints.nodeU(); + V v2 = endpoints.nodeV(); + boolean isLoop = v1.equals(v2); + Point2D p1 = + vv.getRenderContext() + .getMultiLayerTransformer() + .transform(Layer.LAYOUT, layout.apply(v1)); + Point2D p2 = + vv.getRenderContext() + .getMultiLayerTransformer() + .transform(Layer.LAYOUT, layout.apply(v2)); + if (p1 == null || p2 == null) continue; + float x1 = (float) p1.getX(); + float y1 = (float) p1.getY(); + float x2 = (float) p2.getX(); + float y2 = (float) p2.getY(); + + // translate the edge to the starting vertex + AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); + + Shape edgeShape = vv.getRenderContext().getEdgeShapeTransformer().apply(e); + if (isLoop) { + // make the loops proportional to the size of the vertex + Shape s2 = vv.getRenderContext().getVertexShapeTransformer().apply(v2); + Rectangle2D s2Bounds = s2.getBounds2D(); + xform.scale(s2Bounds.getWidth(), s2Bounds.getHeight()); + // move the loop so that the nadir is centered in the vertex + xform.translate(0, -edgeShape.getBounds2D().getHeight() / 2); + } else { + float dx = x2 - x1; + float dy = y2 - y1; + // rotate the edge to the angle between the vertices + double theta = Math.atan2(dy, dx); + xform.rotate(theta); + // stretch the edge to span the distance between the vertices + float dist = (float) Math.sqrt(dx * dx + dy * dy); + xform.scale(dist, 1.0f); + } + + // transform the edge to its location and dimensions + edgeShape = xform.createTransformedShape(edgeShape); + + // because of the transform, the edgeShape is now a GeneralPath + // see if this edge is the closest of any that intersect + if (edgeShape.intersects(pickArea)) { + float cx = 0; + float cy = 0; + float[] f = new float[6]; + PathIterator pi = new GeneralPath(edgeShape).getPathIterator(null); + if (pi.isDone() == false) { + pi.next(); + pi.currentSegment(f); + cx = f[0]; + cy = f[1]; + if (pi.isDone() == false) { + pi.currentSegment(f); + cx = f[0]; + cy = f[1]; + } + } + float dx = (float) (cx - x); + float dy = (float) (cy - y); + float dist = dx * dx + dy * dy; + if (dist < minDistance) { + minDistance = dist; + closest = e; + } + } + } + break; + } catch (ConcurrentModificationException cme) { + } } + return closest; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/MultiPickedState.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/MultiPickedState.java index 865602c9..dc73a920 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/MultiPickedState.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/MultiPickedState.java @@ -1,10 +1,10 @@ /* * Copyright (c) 2005, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * * * Created on Mar 28, 2005 */ @@ -19,64 +19,55 @@ import java.util.Set; /** - * Maintains the state of what has been 'picked' in the graph. - * The Sets are constructed so that their iterators - * will traverse them in the order in which they are picked. - * - * @author Tom Nelson + * Maintains the state of what has been 'picked' in the graph. The Sets are constructed + * so that their iterators will traverse them in the order in which they are picked. + * + * @author Tom Nelson * @author Joshua O'Madadhain - * */ public class MultiPickedState extends AbstractPickedState implements PickedState { - /** - * the 'picked' vertices - */ - protected Set picked = new LinkedHashSet(); - - public boolean pick(T v, boolean state) { - boolean prior_state = this.picked.contains(v); - if (state) { - picked.add(v); - if(prior_state == false) { - fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, - v, ItemEvent.SELECTED)); - } + /** the 'picked' vertices */ + protected Set picked = new LinkedHashSet(); - } else { - picked.remove(v); - if(prior_state == true) { - fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, - v, ItemEvent.DESELECTED)); - } + public boolean pick(T v, boolean state) { + boolean prior_state = this.picked.contains(v); + if (state) { + picked.add(v); + if (prior_state == false) { + fireItemStateChanged( + new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, v, ItemEvent.SELECTED)); + } - } - return prior_state; + } else { + picked.remove(v); + if (prior_state == true) { + fireItemStateChanged( + new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, v, ItemEvent.DESELECTED)); + } } + return prior_state; + } - public void clear() { - Collection unpicks = new ArrayList(picked); - for(T v : unpicks) { - pick(v, false); - } - picked.clear(); - + public void clear() { + Collection unpicks = new ArrayList(picked); + for (T v : unpicks) { + pick(v, false); } + picked.clear(); + } - public Set getPicked() { - return Collections.unmodifiableSet(picked); - } - - public boolean isPicked(T e) { - return picked.contains(e); - } + public Set getPicked() { + return Collections.unmodifiableSet(picked); + } - /** - * for the ItemSelectable interface contract - */ - @SuppressWarnings("unchecked") - public T[] getSelectedObjects() { - List list = new ArrayList(picked); - return (T[])list.toArray(); - } - + public boolean isPicked(T e) { + return picked.contains(e); + } + + /** for the ItemSelectable interface contract */ + @SuppressWarnings("unchecked") + public T[] getSelectedObjects() { + List list = new ArrayList(picked); + return (T[]) list.toArray(); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/PickedInfo.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/PickedInfo.java index ce6ce2cd..a492703c 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/PickedInfo.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/PickedInfo.java @@ -1,23 +1,21 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.visualization.picking; - - /** - * An interface for classes that return information regarding whether a - * given graph element (vertex or edge) has been selected. - * + * An interface for classes that return information regarding whether a given graph element (vertex + * or edge) has been selected. + * * @author danyelf */ public interface PickedInfo { - public boolean isPicked(T t); + public boolean isPicked(T t); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/PickedState.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/PickedState.java index 6e3a4495..cc801d6f 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/PickedState.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/PickedState.java @@ -1,10 +1,10 @@ /* * Copyright (c) 2005, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * * * Created on Apr 2, 2005 */ @@ -14,35 +14,28 @@ import java.util.Set; /** - * An interface for classes that keep track of the "picked" state - * of edges or vertices. - * + * An interface for classes that keep track of the "picked" state of edges or vertices. + * * @author Tom Nelson * @author Joshua O'Madadhain */ public interface PickedState extends PickedInfo, ItemSelectable { - /** - * Marks v as "picked" if b == true, - * and unmarks v as picked if b == false. - * @param v the element to be picked/unpicked - * @param b true if {@code v} is to be marked as picked, false if to be marked as unpicked - * @return the "picked" state of v prior to this call - */ - boolean pick(T v, boolean b); - - /** - * Clears the "picked" state from all elements. - */ - void clear(); - - /** - * @return all "picked" elements. - */ - Set getPicked(); - - /** - * @return true if v is currently "picked". - */ - boolean isPicked(T v); + /** + * Marks v as "picked" if b == true, and unmarks v as + * picked if b == false. + * + * @param v the element to be picked/unpicked + * @param b true if {@code v} is to be marked as picked, false if to be marked as unpicked + * @return the "picked" state of v prior to this call + */ + boolean pick(T v, boolean b); + + /** Clears the "picked" state from all elements. */ + void clear(); + + /** @return all "picked" elements. */ + Set getPicked(); -} \ No newline at end of file + /** @return true if v is currently "picked". */ + boolean isPicked(T v); +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/ShapePickSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/ShapePickSupport.java index d08019af..a9af7485 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/ShapePickSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/ShapePickSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,6 +11,15 @@ */ package edu.uci.ics.jung.visualization.picking; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Sets; +import com.google.common.graph.EndpointPair; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationServer; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; @@ -22,435 +31,419 @@ import java.util.HashSet; import java.util.Set; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.Sets; -import com.google.common.graph.EndpointPair; -import com.google.common.graph.Network; - -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationServer; - /** - * A NetworkElementAccessor that returns elements whose Shape - * contains the specified pick point or region. - * - * @author Tom Nelson + * A NetworkElementAccessor that returns elements whose Shape contains the + * specified pick point or region. * + * @author Tom Nelson */ -public class ShapePickSupport implements NetworkElementAccessor { - - /** - * The available picking heuristics: - *
              - *
            • Style.CENTERED: returns the element whose - * center is closest to the pick point. - *
            • Style.LOWEST: returns the first such element - * encountered. (If the element collection has a consistent - * ordering, this will also be the element "on the bottom", - * that is, the one which is rendered first.) - *
            • Style.HIGHEST: returns the last such element - * encountered. (If the element collection has a consistent - * ordering, this will also be the element "on the top", - * that is, the one which is rendered last.) - *
            - * - */ - public static enum Style { LOWEST, CENTERED, HIGHEST }; - - protected float pickSize; - - /** - * The VisualizationServer in which the - * this instance is being used for picking. Used to - * retrieve properties such as the layout, renderer, - * vertex and edge shapes, and coordinate transformations. - */ - protected VisualizationServer vv; - - /** - * The current picking heuristic for this instance. Defaults - * to CENTERED. - */ - protected Style style = Style.CENTERED; - - /** - * Creates a ShapePickSupport for the vv - * VisualizationServer, with the specified pick footprint and - * the default pick style. - * The VisualizationServer is used to access - * properties of the current visualization (layout, renderer, - * coordinate transformations, vertex/edge shapes, etc.). - * @param vv source of the current Layout. - * @param pickSize the size of the pick footprint for line edges - */ - public ShapePickSupport(VisualizationServer vv, float pickSize) { - this.vv = vv; - this.pickSize = pickSize; - } - - /** - * Create a ShapePickSupport for the specified - * VisualizationServer with a default pick footprint. - * of size 2. - * @param vv the visualization server used for rendering - */ - public ShapePickSupport(VisualizationServer vv) { - this.vv = vv; - this.pickSize = 2; +public class ShapePickSupport implements NetworkElementAccessor { + + /** + * The available picking heuristics: + * + *
              + *
            • Style.CENTERED: returns the element whose center is closest to the pick + * point. + *
            • Style.LOWEST: returns the first such element encountered. (If the element + * collection has a consistent ordering, this will also be the element "on the bottom", that + * is, the one which is rendered first.) + *
            • Style.HIGHEST: returns the last such element encountered. (If the element + * collection has a consistent ordering, this will also be the element "on the top", that + * is, the one which is rendered last.) + *
            + */ + public static enum Style { + LOWEST, + CENTERED, + HIGHEST + }; + + protected float pickSize; + + /** + * The VisualizationServer in which the this instance is being used for picking. Used + * to retrieve properties such as the layout, renderer, vertex and edge shapes, and coordinate + * transformations. + */ + protected VisualizationServer vv; + + /** The current picking heuristic for this instance. Defaults to CENTERED. */ + protected Style style = Style.CENTERED; + + /** + * Creates a ShapePickSupport for the vv VisualizationServer, with the + * specified pick footprint and the default pick style. The VisualizationServer is + * used to access properties of the current visualization (layout, renderer, coordinate + * transformations, vertex/edge shapes, etc.). + * + * @param vv source of the current Layout. + * @param pickSize the size of the pick footprint for line edges + */ + public ShapePickSupport(VisualizationServer vv, float pickSize) { + this.vv = vv; + this.pickSize = pickSize; + } + + /** + * Create a ShapePickSupport for the specified VisualizationServer with + * a default pick footprint. of size 2. + * + * @param vv the visualization server used for rendering + */ + public ShapePickSupport(VisualizationServer vv) { + this.vv = vv; + this.pickSize = 2; + } + + /** + * Returns the style of picking used by this instance. This specifies which of the elements, among + * those whose shapes contain the pick point, is returned. The available styles are: + * + *
              + *
            • Style.CENTERED: returns the element whose center is closest to the pick + * point. + *
            • Style.LOWEST: returns the first such element encountered. (If the element + * collection has a consistent ordering, this will also be the element "on the bottom", that + * is, the one which is rendered first.) + *
            • Style.HIGHEST: returns the last such element encountered. (If the element + * collection has a consistent ordering, this will also be the element "on the top", that + * is, the one which is rendered last.) + *
            + * + * @return the style of picking used by this instance + */ + public Style getStyle() { + return style; + } + + /** + * Specifies the style of picking to be used by this instance. This specifies which of the + * elements, among those whose shapes contain the pick point, will be returned. The available + * styles are: + * + *
              + *
            • Style.CENTERED: returns the element whose center is closest to the pick + * point. + *
            • Style.LOWEST: returns the first such element encountered. (If the element + * collection has a consistent ordering, this will also be the element "on the bottom", that + * is, the one which is rendered first.) + *
            • Style.HIGHEST: returns the last such element encountered. (If the element + * collection has a consistent ordering, this will also be the element "on the top", that + * is, the one which is rendered last.) + *
            + * + * @param style the style to set + */ + public void setStyle(Style style) { + this.style = style; + } + + /** + * Returns the vertex, if any, whose shape contains (x, y). If (x,y) is contained in more than one + * vertex's shape, returns the vertex whose center is closest to the pick point. + * + * @param x the x coordinate of the pick point + * @param y the y coordinate of the pick point + * @return the vertex whose shape contains (x,y), and whose center is closest to the pick point + */ + @Override + public V getNode(double x, double y) { + + V closest = null; + double minDistance = Double.MAX_VALUE; + Point2D ip = + vv.getRenderContext() + .getMultiLayerTransformer() + .inverseTransform(Layer.VIEW, new Point2D.Double(x, y)); + x = ip.getX(); + y = ip.getY(); + Layout layout = vv.getGraphLayout(); + + while (true) { + try { + for (V v : getFilteredVertices()) { + + Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(v); + // get the vertex location + Point2D p = layout.apply(v); + if (p == null) continue; + // transform the vertex location to screen coords + p = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p); + + double ox = x - p.getX(); + double oy = y - p.getY(); + + if (shape.contains(ox, oy)) { + + if (style == Style.LOWEST) { + // return the first match + return v; + } else if (style == Style.HIGHEST) { + // will return the last match + closest = v; + } else { + + // return the vertex closest to the + // center of a vertex shape + Rectangle2D bounds = shape.getBounds2D(); + double dx = bounds.getCenterX() - ox; + double dy = bounds.getCenterY() - oy; + double dist = dx * dx + dy * dy; + if (dist < minDistance) { + minDistance = dist; + closest = v; + } + } + } + } + break; + } catch (ConcurrentModificationException cme) { + } } - - /** - * Returns the style of picking used by this instance. - * This specifies which of the elements, among those - * whose shapes contain the pick point, is returned. - * The available styles are: - *
              - *
            • Style.CENTERED: returns the element whose - * center is closest to the pick point. - *
            • Style.LOWEST: returns the first such element - * encountered. (If the element collection has a consistent - * ordering, this will also be the element "on the bottom", - * that is, the one which is rendered first.) - *
            • Style.HIGHEST: returns the last such element - * encountered. (If the element collection has a consistent - * ordering, this will also be the element "on the top", - * that is, the one which is rendered last.) - *
            - * - * @return the style of picking used by this instance - */ - public Style getStyle() { - return style; - } - - /** - * Specifies the style of picking to be used by this instance. - * This specifies which of the elements, among those - * whose shapes contain the pick point, will be returned. - * The available styles are: - *
              - *
            • Style.CENTERED: returns the element whose - * center is closest to the pick point. - *
            • Style.LOWEST: returns the first such element - * encountered. (If the element collection has a consistent - * ordering, this will also be the element "on the bottom", - * that is, the one which is rendered first.) - *
            • Style.HIGHEST: returns the last such element - * encountered. (If the element collection has a consistent - * ordering, this will also be the element "on the top", - * that is, the one which is rendered last.) - *
            - * @param style the style to set - */ - public void setStyle(Style style) { - this.style = style; - } - - /** - * Returns the vertex, if any, whose shape contains (x, y). - * If (x,y) is contained in more than one vertex's shape, returns - * the vertex whose center is closest to the pick point. - * @param x the x coordinate of the pick point - * @param y the y coordinate of the pick point - * - * @return the vertex whose shape contains (x,y), and whose center is closest to the pick point - */ - @Override - public V getNode(double x, double y) { - - V closest = null; - double minDistance = Double.MAX_VALUE; - Point2D ip = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, - new Point2D.Double(x,y)); - x = ip.getX(); - y = ip.getY(); + return closest; + } + + /** + * Returns the vertices whose layout coordinates are contained in Shape. The shape is + * in screen coordinates, and the graph vertices are transformed to screen coordinates before they + * are tested for inclusion. + * + * @return the Collection of vertices whose layout coordinates are + * contained in shape. + */ + @Override + public Collection getNodes(Shape shape) { + Set pickedVertices = new HashSet(); + + // remove the view transform from the rectangle + shape = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, shape); + + while (true) { + try { Layout layout = vv.getGraphLayout(); + for (V v : getFilteredVertices()) { + Point2D p = layout.apply(v); + if (p == null) continue; - while(true) { - try { - for(V v : getFilteredVertices()) { - - Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(v); - // get the vertex location - Point2D p = layout.apply(v); - if(p == null) continue; - // transform the vertex location to screen coords - p = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p); - - double ox = x - p.getX(); - double oy = y - p.getY(); - - if(shape.contains(ox, oy)) { - - if(style == Style.LOWEST) { - // return the first match - return v; - } else if(style == Style.HIGHEST) { - // will return the last match - closest = v; - } else { - - // return the vertex closest to the - // center of a vertex shape - Rectangle2D bounds = shape.getBounds2D(); - double dx = bounds.getCenterX() - ox; - double dy = bounds.getCenterY() - oy; - double dist = dx * dx + dy * dy; - if (dist < minDistance) { - minDistance = dist; - closest = v; - } - } - } - } - break; - } catch(ConcurrentModificationException cme) {} + p = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p); + if (shape.contains(p)) { + pickedVertices.add(v); + } } - return closest; + break; + } catch (ConcurrentModificationException cme) { + } } + return pickedVertices; + } + + /** + * Returns an edge whose shape intersects the 'pickArea' footprint of the passed x,y, coordinates. + * + * @param x the x coordinate of the location + * @param y the y coordinate of the location + * @return an edge whose shape intersects the pick area centered on the location {@code (x,y)} + */ + @Override + public E getEdge(double x, double y) { + + Point2D ip = + vv.getRenderContext() + .getMultiLayerTransformer() + .inverseTransform(Layer.VIEW, new Point2D.Double(x, y)); + x = ip.getX(); + y = ip.getY(); - /** - * Returns the vertices whose layout coordinates are contained in - * Shape. - * The shape is in screen coordinates, and the graph vertices - * are transformed to screen coordinates before they are tested - * for inclusion. - * @return the Collection of vertices whose layout - * coordinates are contained in shape. - */ - @Override - public Collection getNodes(Shape shape) { - Set pickedVertices = new HashSet(); - - // remove the view transform from the rectangle - shape = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, shape); - - while(true) { - try { - Layout layout = vv.getGraphLayout(); - for(V v : getFilteredVertices()) { - Point2D p = layout.apply(v); - if(p == null) continue; - - p = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p); - if(shape.contains(p)) { - pickedVertices.add(v); - } - } - break; - } catch(ConcurrentModificationException cme) {} + // as a Line has no area, we can't always use edgeshape.contains(point) so we + // make a small rectangular pickArea around the point and check if the + // edgeshape.intersects(pickArea) + Rectangle2D pickArea = + new Rectangle2D.Float( + (float) x - pickSize / 2, (float) y - pickSize / 2, pickSize, pickSize); + E closest = null; + double minDistance = Double.MAX_VALUE; + while (true) { + try { + for (E e : getFilteredEdges()) { + + Shape edgeShape = getTransformedEdgeShape(e); + if (edgeShape == null) continue; + + // because of the transform, the edgeShape is now a GeneralPath + // see if this edge is the closest of any that intersect + if (edgeShape.intersects(pickArea)) { + float cx = 0; + float cy = 0; + float[] f = new float[6]; + PathIterator pi = new GeneralPath(edgeShape).getPathIterator(null); + if (pi.isDone() == false) { + pi.next(); + pi.currentSegment(f); + cx = f[0]; + cy = f[1]; + if (pi.isDone() == false) { + pi.currentSegment(f); + cx = f[0]; + cy = f[1]; + } + } + float dx = (float) (cx - x); + float dy = (float) (cy - y); + float dist = dx * dx + dy * dy; + if (dist < minDistance) { + minDistance = dist; + closest = e; + } + } } - return pickedVertices; - } - - /** - * Returns an edge whose shape intersects the 'pickArea' footprint of the passed - * x,y, coordinates. - * @param x the x coordinate of the location - * @param y the y coordinate of the location - * - * @return an edge whose shape intersects the pick area centered on the location {@code (x,y)} - */ - @Override - public E getEdge(double x, double y) { - - Point2D ip = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, new Point2D.Double(x,y)); - x = ip.getX(); - y = ip.getY(); - - // as a Line has no area, we can't always use edgeshape.contains(point) so we - // make a small rectangular pickArea around the point and check if the - // edgeshape.intersects(pickArea) - Rectangle2D pickArea = - new Rectangle2D.Float((float)x-pickSize/2,(float)y-pickSize/2,pickSize,pickSize); - E closest = null; - double minDistance = Double.MAX_VALUE; - while(true) { - try { - for(E e : getFilteredEdges()) { - - Shape edgeShape = getTransformedEdgeShape(e); - if (edgeShape == null) - continue; - - // because of the transform, the edgeShape is now a GeneralPath - // see if this edge is the closest of any that intersect - if(edgeShape.intersects(pickArea)) { - float cx=0; - float cy=0; - float[] f = new float[6]; - PathIterator pi = new GeneralPath(edgeShape).getPathIterator(null); - if(pi.isDone()==false) { - pi.next(); - pi.currentSegment(f); - cx = f[0]; - cy = f[1]; - if(pi.isDone()==false) { - pi.currentSegment(f); - cx = f[0]; - cy = f[1]; - } - } - float dx = (float) (cx - x); - float dy = (float) (cy - y); - float dist = dx * dx + dy * dy; - if (dist < minDistance) { - minDistance = dist; - closest = e; - } - } - } - break; - } catch(ConcurrentModificationException cme) {} - } - return closest; + break; + } catch (ConcurrentModificationException cme) { + } } + return closest; + } - /** - * Retrieves the shape template for e and - * transforms it according to the positions of its endpoints - * in layout. - * @param layout the Layout which specifies - * e's endpoints' positions - * @param e the edge whose shape is to be returned - * @return the transformed shape - */ - private Shape getTransformedEdgeShape(E e) { - EndpointPair endpoints = vv.getModel().getNetwork().incidentNodes(e); - V v1 = endpoints.nodeU(); - V v2 = endpoints.nodeV(); - boolean isLoop = v1.equals(v2); - Layout layout = vv.getGraphLayout(); - Point2D p1 = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, layout.apply(v1)); - Point2D p2 = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, layout.apply(v2)); - if(p1 == null || p2 == null) - return null; - float x1 = (float) p1.getX(); - float y1 = (float) p1.getY(); - float x2 = (float) p2.getX(); - float y2 = (float) p2.getY(); - - // translate the edge to the starting vertex - AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); - - Shape edgeShape = vv.getRenderContext().getEdgeShapeTransformer().apply(e); - if(isLoop) { - // make the loops proportional to the size of the vertex - Shape s2 = vv.getRenderContext().getVertexShapeTransformer().apply(v2); - Rectangle2D s2Bounds = s2.getBounds2D(); - xform.scale(s2Bounds.getWidth(),s2Bounds.getHeight()); - // move the loop so that the nadir is centered in the vertex - xform.translate(0, -edgeShape.getBounds2D().getHeight()/2); - } else { - float dx = x2 - x1; - float dy = y2 - y1; - // rotate the edge to the angle between the vertices - double theta = Math.atan2(dy,dx); - xform.rotate(theta); - // stretch the edge to span the distance between the vertices - float dist = (float) Math.sqrt(dx*dx + dy*dy); - xform.scale(dist, 1.0f); - } - - // transform the edge to its location and dimensions - edgeShape = xform.createTransformedShape(edgeShape); - return edgeShape; - } - - protected Collection getFilteredVertices() { - Set nodes = vv.getModel().getNetwork().nodes(); - return verticesAreFiltered() - ? Sets.filter(nodes, vv.getRenderContext().getVertexIncludePredicate()) - : nodes; - } + /** + * Retrieves the shape template for e and transforms it according to the positions of + * its endpoints in layout. + * + * @param layout the Layout which specifies e's endpoints' positions + * @param e the edge whose shape is to be returned + * @return the transformed shape + */ + private Shape getTransformedEdgeShape(E e) { + EndpointPair endpoints = vv.getModel().getNetwork().incidentNodes(e); + V v1 = endpoints.nodeU(); + V v2 = endpoints.nodeV(); + boolean isLoop = v1.equals(v2); + Layout layout = vv.getGraphLayout(); + Point2D p1 = + vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, layout.apply(v1)); + Point2D p2 = + vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, layout.apply(v2)); + if (p1 == null || p2 == null) return null; + float x1 = (float) p1.getX(); + float y1 = (float) p1.getY(); + float x2 = (float) p2.getX(); + float y2 = (float) p2.getY(); - protected Collection getFilteredEdges() { - Set edges = vv.getModel().getNetwork().edges(); - return edgesAreFiltered() - ? Sets.filter(edges, vv.getRenderContext().getEdgeIncludePredicate()) - : edges; - } - - /** - * Quick test to allow optimization of getFilteredVertices(). - * @return true if there is an active vertex filtering - * mechanism for this visualization, false otherwise - */ - protected boolean verticesAreFiltered() { - Predicate vertexIncludePredicate = - vv.getRenderContext().getVertexIncludePredicate(); - return vertexIncludePredicate != null && - vertexIncludePredicate.equals(Predicates.alwaysTrue()) == false; + // translate the edge to the starting vertex + AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); + + Shape edgeShape = vv.getRenderContext().getEdgeShapeTransformer().apply(e); + if (isLoop) { + // make the loops proportional to the size of the vertex + Shape s2 = vv.getRenderContext().getVertexShapeTransformer().apply(v2); + Rectangle2D s2Bounds = s2.getBounds2D(); + xform.scale(s2Bounds.getWidth(), s2Bounds.getHeight()); + // move the loop so that the nadir is centered in the vertex + xform.translate(0, -edgeShape.getBounds2D().getHeight() / 2); + } else { + float dx = x2 - x1; + float dy = y2 - y1; + // rotate the edge to the angle between the vertices + double theta = Math.atan2(dy, dx); + xform.rotate(theta); + // stretch the edge to span the distance between the vertices + float dist = (float) Math.sqrt(dx * dx + dy * dy); + xform.scale(dist, 1.0f); } - - /** - * Quick test to allow optimization of getFilteredEdges(). - * @return true if there is an active edge filtering - * mechanism for this visualization, false otherwise - */ - protected boolean edgesAreFiltered() { - Predicate edgeIncludePredicate = vv.getRenderContext().getEdgeIncludePredicate(); - return edgeIncludePredicate != null && - edgeIncludePredicate.equals(Predicates.alwaysTrue()) == false; + + // transform the edge to its location and dimensions + edgeShape = xform.createTransformedShape(edgeShape); + return edgeShape; + } + + protected Collection getFilteredVertices() { + Set nodes = vv.getModel().getNetwork().nodes(); + return verticesAreFiltered() + ? Sets.filter(nodes, vv.getRenderContext().getVertexIncludePredicate()) + : nodes; + } + + protected Collection getFilteredEdges() { + Set edges = vv.getModel().getNetwork().edges(); + return edgesAreFiltered() + ? Sets.filter(edges, vv.getRenderContext().getEdgeIncludePredicate()) + : edges; + } + + /** + * Quick test to allow optimization of getFilteredVertices(). + * + * @return true if there is an active vertex filtering mechanism for this + * visualization, false otherwise + */ + protected boolean verticesAreFiltered() { + Predicate vertexIncludePredicate = vv.getRenderContext().getVertexIncludePredicate(); + return vertexIncludePredicate != null + && vertexIncludePredicate.equals(Predicates.alwaysTrue()) == false; + } + + /** + * Quick test to allow optimization of getFilteredEdges(). + * + * @return true if there is an active edge filtering mechanism for this + * visualization, false otherwise + */ + protected boolean edgesAreFiltered() { + Predicate edgeIncludePredicate = vv.getRenderContext().getEdgeIncludePredicate(); + return edgeIncludePredicate != null + && edgeIncludePredicate.equals(Predicates.alwaysTrue()) == false; + } + + /** + * Returns true if this vertex in this graph is included in the collections of + * elements to be rendered, and false otherwise. + * + * @param context the vertex and graph to be queried + * @return true if this vertex is included in the collections of elements to be + * rendered, false otherwise. + */ + protected boolean isVertexRendered(V vertex) { + Predicate vertexIncludePredicate = vv.getRenderContext().getVertexIncludePredicate(); + return vertexIncludePredicate == null || vertexIncludePredicate.apply(vertex); + } + + /** + * Returns true if this edge and its endpoints in this graph are all included in the + * collections of elements to be rendered, and false otherwise. + * + * @param context the edge and graph to be queried + * @return true if this edge and its endpoints are all included in the collections of + * elements to be rendered, false otherwise. + */ + protected boolean isEdgeRendered(E edge) { + Predicate vertexIncludePredicate = vv.getRenderContext().getVertexIncludePredicate(); + Predicate edgeIncludePredicate = vv.getRenderContext().getEdgeIncludePredicate(); + Network g = vv.getModel().getNetwork(); + if (edgeIncludePredicate != null && !edgeIncludePredicate.apply(edge)) { + return false; } - - /** - * Returns true if this vertex in this graph is included - * in the collections of elements to be rendered, and false otherwise. - * @param context the vertex and graph to be queried - * @return true if this vertex is - * included in the collections of elements to be rendered, false - * otherwise. - */ - protected boolean isVertexRendered(V vertex) { - Predicate vertexIncludePredicate = vv.getRenderContext().getVertexIncludePredicate(); - return vertexIncludePredicate == null || vertexIncludePredicate.apply(vertex); - } - - /** - * Returns true if this edge and its endpoints - * in this graph are all included in the collections of - * elements to be rendered, and false otherwise. - * @param context the edge and graph to be queried - * @return true if this edge and its endpoints are all - * included in the collections of elements to be rendered, false - * otherwise. - */ - protected boolean isEdgeRendered(E edge) { - Predicate vertexIncludePredicate = - vv.getRenderContext().getVertexIncludePredicate(); - Predicate edgeIncludePredicate = - vv.getRenderContext().getEdgeIncludePredicate(); - Network g = vv.getModel().getNetwork(); - if (edgeIncludePredicate != null && !edgeIncludePredicate.apply(edge)) { - return false; - } - EndpointPair endpoints = g.incidentNodes(edge); - V v1 = endpoints.nodeU(); - V v2 = endpoints.nodeV(); - return vertexIncludePredicate == null || - (vertexIncludePredicate.apply(v1) && vertexIncludePredicate.apply(v2)); - } - - /** - * Returns the size of the edge picking area. - * The picking area is square; the size is specified as the length of one - * side, in view coordinates. - * @return the size of the edge picking area - */ - public float getPickSize() { - return pickSize; - } - - /** - * Sets the size of the edge picking area. - * @param pickSize the length of one side of the (square) picking area, in view coordinates - */ - public void setPickSize(float pickSize) { - this.pickSize = pickSize; - } + EndpointPair endpoints = g.incidentNodes(edge); + V v1 = endpoints.nodeU(); + V v2 = endpoints.nodeV(); + return vertexIncludePredicate == null + || (vertexIncludePredicate.apply(v1) && vertexIncludePredicate.apply(v2)); + } + + /** + * Returns the size of the edge picking area. The picking area is square; the size is specified as + * the length of one side, in view coordinates. + * + * @return the size of the edge picking area + */ + public float getPickSize() { + return pickSize; + } + /** + * Sets the size of the edge picking area. + * + * @param pickSize the length of one side of the (square) picking area, in view coordinates + */ + public void setPickSize(float pickSize) { + this.pickSize = pickSize; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/ViewLensShapePickSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/ViewLensShapePickSupport.java index e37b7530..eb299d9f 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/ViewLensShapePickSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/picking/ViewLensShapePickSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, The JUNG Authors + * Copyright (c) 2005, The JUNG Authors * * All rights reserved. * @@ -11,6 +11,12 @@ */ package edu.uci.ics.jung.visualization.picking; +import com.google.common.graph.EndpointPair; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.VisualizationServer; +import edu.uci.ics.jung.visualization.transform.MutableTransformerDecorator; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; @@ -22,217 +28,221 @@ import java.util.HashSet; import java.util.Set; -import com.google.common.graph.EndpointPair; -import com.google.common.graph.Network; - -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.VisualizationServer; -import edu.uci.ics.jung.visualization.transform.MutableTransformerDecorator; - /** - * ShapePickSupport provides access to Vertices and EdgeType based on - * their actual shapes. - * + * ShapePickSupport provides access to Vertices and EdgeType based on their actual shapes. + * * @param the vertex type * @param the edge type - * * @author Tom Nelson */ -public class ViewLensShapePickSupport extends ShapePickSupport { - - public ViewLensShapePickSupport(VisualizationServer vv, float pickSize) { - super(vv, pickSize); - } - - public ViewLensShapePickSupport(VisualizationServer vv) { - this(vv, 2); - } - - @Override - public V getNode(double x, double y) { - - V closest = null; - double minDistance = Double.MAX_VALUE; - Point2D ip = ((MutableTransformerDecorator)vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)).getDelegate().inverseTransform(new Point2D.Double(x,y)); - x = ip.getX(); - y = ip.getY(); - - while(true) { - try { - Layout layout = vv.getGraphLayout(); - for(V v : getFilteredVertices()) { - // get the shape - Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(v); - // transform the vertex location to screen coords - Point2D p = layout.apply(v); - if(p == null) continue; - AffineTransform xform = - AffineTransform.getTranslateInstance(p.getX(), p.getY()); - shape = xform.createTransformedShape(shape); - - // use the LAYOUT transform to move the shape center without - // modifying the actual shape - Point2D lp = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p); - AffineTransform xlate = AffineTransform.getTranslateInstance( - lp.getX()-p.getX(),lp.getY()-p.getY()); - shape = xlate.createTransformedShape(shape); - // now use the VIEW transform to modify the actual shape - - shape = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.VIEW, shape); - //vv.getRenderContext().getMultiLayerTransformer().transform(shape); - - // see if this vertex center is closest to the pick point - // among any other containing vertices - if(shape.contains(x, y)) { - - if(style == Style.LOWEST) { - // return the first match - return v; - } else if(style == Style.HIGHEST) { - // will return the last match - closest = v; - } else { - Rectangle2D bounds = shape.getBounds2D(); - double dx = bounds.getCenterX() - x; - double dy = bounds.getCenterY() - y; - double dist = dx * dx + dy * dy; - if (dist < minDistance) { - minDistance = dist; - closest = v; - } - } - } - } - break; - } catch(ConcurrentModificationException cme) {} +public class ViewLensShapePickSupport extends ShapePickSupport { + + public ViewLensShapePickSupport(VisualizationServer vv, float pickSize) { + super(vv, pickSize); + } + + public ViewLensShapePickSupport(VisualizationServer vv) { + this(vv, 2); + } + + @Override + public V getNode(double x, double y) { + + V closest = null; + double minDistance = Double.MAX_VALUE; + Point2D ip = + ((MutableTransformerDecorator) + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)) + .getDelegate() + .inverseTransform(new Point2D.Double(x, y)); + x = ip.getX(); + y = ip.getY(); + + while (true) { + try { + Layout layout = vv.getGraphLayout(); + for (V v : getFilteredVertices()) { + // get the shape + Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(v); + // transform the vertex location to screen coords + Point2D p = layout.apply(v); + if (p == null) continue; + AffineTransform xform = AffineTransform.getTranslateInstance(p.getX(), p.getY()); + shape = xform.createTransformedShape(shape); + + // use the LAYOUT transform to move the shape center without + // modifying the actual shape + Point2D lp = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p); + AffineTransform xlate = + AffineTransform.getTranslateInstance(lp.getX() - p.getX(), lp.getY() - p.getY()); + shape = xlate.createTransformedShape(shape); + // now use the VIEW transform to modify the actual shape + + shape = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.VIEW, shape); + //vv.getRenderContext().getMultiLayerTransformer().transform(shape); + + // see if this vertex center is closest to the pick point + // among any other containing vertices + if (shape.contains(x, y)) { + + if (style == Style.LOWEST) { + // return the first match + return v; + } else if (style == Style.HIGHEST) { + // will return the last match + closest = v; + } else { + Rectangle2D bounds = shape.getBounds2D(); + double dx = bounds.getCenterX() - x; + double dy = bounds.getCenterY() - y; + double dist = dx * dx + dy * dy; + if (dist < minDistance) { + minDistance = dist; + closest = v; + } + } + } } - return closest; + break; + } catch (ConcurrentModificationException cme) { + } } - - @Override - public Collection getNodes(Shape rectangle) { - Set pickedVertices = new HashSet(); - -// remove the view transform from the rectangle - rectangle = ((MutableTransformerDecorator)vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)).getDelegate().inverseTransform(rectangle); - - while(true) { - try { - Layout layout = vv.getGraphLayout(); - for(V v : getFilteredVertices()) { - Point2D p = layout.apply(v); - if(p == null) continue; - // get the shape - Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(v); - - AffineTransform xform = - AffineTransform.getTranslateInstance(p.getX(), p.getY()); - shape = xform.createTransformedShape(shape); - - shape = vv.getRenderContext().getMultiLayerTransformer().transform(shape); - Rectangle2D bounds = shape.getBounds2D(); - p.setLocation(bounds.getCenterX(),bounds.getCenterY()); - - if(rectangle.contains(p)) { - pickedVertices.add(v); - } - } - break; - } catch(ConcurrentModificationException cme) {} + return closest; + } + + @Override + public Collection getNodes(Shape rectangle) { + Set pickedVertices = new HashSet(); + + // remove the view transform from the rectangle + rectangle = + ((MutableTransformerDecorator) + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)) + .getDelegate() + .inverseTransform(rectangle); + + while (true) { + try { + Layout layout = vv.getGraphLayout(); + for (V v : getFilteredVertices()) { + Point2D p = layout.apply(v); + if (p == null) continue; + // get the shape + Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(v); + + AffineTransform xform = AffineTransform.getTranslateInstance(p.getX(), p.getY()); + shape = xform.createTransformedShape(shape); + + shape = vv.getRenderContext().getMultiLayerTransformer().transform(shape); + Rectangle2D bounds = shape.getBounds2D(); + p.setLocation(bounds.getCenterX(), bounds.getCenterY()); + + if (rectangle.contains(p)) { + pickedVertices.add(v); + } } - return pickedVertices; + break; + } catch (ConcurrentModificationException cme) { + } } - - @Override - public E getEdge(double x, double y) { - Point2D ip = ((MutableTransformerDecorator)vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)).getDelegate().inverseTransform(new Point2D.Double(x,y)); - x = ip.getX(); - y = ip.getY(); - - // as a Line has no area, we can't always use edgeshape.contains(point) so we - // make a small rectangular pickArea around the point and check if the - // edgeshape.intersects(pickArea) - Rectangle2D pickArea = - new Rectangle2D.Float((float)x-pickSize/2,(float)y-pickSize/2,pickSize,pickSize); - E closest = null; - double minDistance = Double.MAX_VALUE; - while(true) { - try { - Layout layout = vv.getGraphLayout(); - Network network = vv.getModel().getNetwork(); - for(E e : getFilteredEdges()) { - EndpointPair endpoints = network.incidentNodes(e); - V v1 = endpoints.nodeU(); - V v2 = endpoints.nodeV(); - boolean isLoop = v1.equals(v2); - Point2D p1 = layout.apply(v1); - //vv.getRenderContext().getBasicTransformer().transform(layout.transform(v1)); - Point2D p2 = layout.apply(v2); - //vv.getRenderContext().getBasicTransformer().transform(layout.transform(v2)); - if(p1 == null || p2 == null) continue; - float x1 = (float) p1.getX(); - float y1 = (float) p1.getY(); - float x2 = (float) p2.getX(); - float y2 = (float) p2.getY(); - - // translate the edge to the starting vertex - AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); - - Shape edgeShape = vv.getRenderContext().getEdgeShapeTransformer().apply(e); - if(isLoop) { - // make the loops proportional to the size of the vertex - Shape s2 = vv.getRenderContext().getVertexShapeTransformer().apply(v2); - Rectangle2D s2Bounds = s2.getBounds2D(); - xform.scale(s2Bounds.getWidth(),s2Bounds.getHeight()); - // move the loop so that the nadir is centered in the vertex - xform.translate(0, -edgeShape.getBounds2D().getHeight()/2); - } else { - float dx = x2 - x1; - float dy = y2 - y1; - // rotate the edge to the angle between the vertices - double theta = Math.atan2(dy,dx); - xform.rotate(theta); - // stretch the edge to span the distance between the vertices - float dist = (float) Math.sqrt(dx*dx + dy*dy); - xform.scale(dist, 1.0f); - } - - // transform the edge to its location and dimensions - edgeShape = xform.createTransformedShape(edgeShape); - - edgeShape = vv.getRenderContext().getMultiLayerTransformer().transform(edgeShape); - - // because of the transform, the edgeShape is now a GeneralPath - // see if this edge is the closest of any that intersect - if(edgeShape.intersects(pickArea)) { - float cx=0; - float cy=0; - float[] f = new float[6]; - PathIterator pi = new GeneralPath(edgeShape).getPathIterator(null); - if(pi.isDone()==false) { - pi.next(); - pi.currentSegment(f); - cx = f[0]; - cy = f[1]; - if(pi.isDone()==false) { - pi.currentSegment(f); - cx = f[0]; - cy = f[1]; - } - } - float dx = (float) (cx - x); - float dy = (float) (cy - y); - float dist = dx * dx + dy * dy; - if (dist < minDistance) { - minDistance = dist; - closest = e; - } - } - } - break; - } catch(ConcurrentModificationException cme) {} - } - return closest; + return pickedVertices; + } + + @Override + public E getEdge(double x, double y) { + Point2D ip = + ((MutableTransformerDecorator) + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)) + .getDelegate() + .inverseTransform(new Point2D.Double(x, y)); + x = ip.getX(); + y = ip.getY(); + + // as a Line has no area, we can't always use edgeshape.contains(point) so we + // make a small rectangular pickArea around the point and check if the + // edgeshape.intersects(pickArea) + Rectangle2D pickArea = + new Rectangle2D.Float( + (float) x - pickSize / 2, (float) y - pickSize / 2, pickSize, pickSize); + E closest = null; + double minDistance = Double.MAX_VALUE; + while (true) { + try { + Layout layout = vv.getGraphLayout(); + Network network = vv.getModel().getNetwork(); + for (E e : getFilteredEdges()) { + EndpointPair endpoints = network.incidentNodes(e); + V v1 = endpoints.nodeU(); + V v2 = endpoints.nodeV(); + boolean isLoop = v1.equals(v2); + Point2D p1 = layout.apply(v1); + //vv.getRenderContext().getBasicTransformer().transform(layout.transform(v1)); + Point2D p2 = layout.apply(v2); + //vv.getRenderContext().getBasicTransformer().transform(layout.transform(v2)); + if (p1 == null || p2 == null) continue; + float x1 = (float) p1.getX(); + float y1 = (float) p1.getY(); + float x2 = (float) p2.getX(); + float y2 = (float) p2.getY(); + + // translate the edge to the starting vertex + AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); + + Shape edgeShape = vv.getRenderContext().getEdgeShapeTransformer().apply(e); + if (isLoop) { + // make the loops proportional to the size of the vertex + Shape s2 = vv.getRenderContext().getVertexShapeTransformer().apply(v2); + Rectangle2D s2Bounds = s2.getBounds2D(); + xform.scale(s2Bounds.getWidth(), s2Bounds.getHeight()); + // move the loop so that the nadir is centered in the vertex + xform.translate(0, -edgeShape.getBounds2D().getHeight() / 2); + } else { + float dx = x2 - x1; + float dy = y2 - y1; + // rotate the edge to the angle between the vertices + double theta = Math.atan2(dy, dx); + xform.rotate(theta); + // stretch the edge to span the distance between the vertices + float dist = (float) Math.sqrt(dx * dx + dy * dy); + xform.scale(dist, 1.0f); + } + + // transform the edge to its location and dimensions + edgeShape = xform.createTransformedShape(edgeShape); + + edgeShape = vv.getRenderContext().getMultiLayerTransformer().transform(edgeShape); + + // because of the transform, the edgeShape is now a GeneralPath + // see if this edge is the closest of any that intersect + if (edgeShape.intersects(pickArea)) { + float cx = 0; + float cy = 0; + float[] f = new float[6]; + PathIterator pi = new GeneralPath(edgeShape).getPathIterator(null); + if (pi.isDone() == false) { + pi.next(); + pi.currentSegment(f); + cx = f[0]; + cy = f[1]; + if (pi.isDone() == false) { + pi.currentSegment(f); + cx = f[0]; + cy = f[1]; + } + } + float dx = (float) (cx - x); + float dy = (float) (cy - y); + float dist = dx * dx + dy * dy; + if (dist < minDistance) { + minDistance = dist; + closest = e; + } + } + } + break; + } catch (ConcurrentModificationException cme) { + } } + return closest; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicEdgeArrowRenderingSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicEdgeArrowRenderingSupport.java index 4fb4ad9f..4078f291 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicEdgeArrowRenderingSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicEdgeArrowRenderingSupport.java @@ -9,6 +9,7 @@ */ package edu.uci.ics.jung.visualization.renderers; +import edu.uci.ics.jung.visualization.RenderContext; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; @@ -16,188 +17,189 @@ import java.awt.geom.PathIterator; import java.awt.geom.Point2D; -import edu.uci.ics.jung.visualization.RenderContext; - -public class BasicEdgeArrowRenderingSupport implements EdgeArrowRenderingSupport { +public class BasicEdgeArrowRenderingSupport implements EdgeArrowRenderingSupport { - public AffineTransform getArrowTransform(RenderContext rc, Shape edgeShape, Shape vertexShape) { - GeneralPath path = new GeneralPath(edgeShape); - float[] seg = new float[6]; - Point2D p1=null; - Point2D p2=null; - AffineTransform at = new AffineTransform(); - // when the PathIterator is done, switch to the line-subdivide - // method to get the arrowhead closer. - for(PathIterator i=path.getPathIterator(null,1); !i.isDone(); i.next()) { - int ret = i.currentSegment(seg); - if(ret == PathIterator.SEG_MOVETO) { - p2 = new Point2D.Float(seg[0],seg[1]); - } else if(ret == PathIterator.SEG_LINETO) { - p1 = p2; - p2 = new Point2D.Float(seg[0],seg[1]); - if(vertexShape.contains(p2)) { - at = getArrowTransform(rc, new Line2D.Float(p1,p2),vertexShape); - break; - } - } + public AffineTransform getArrowTransform( + RenderContext rc, Shape edgeShape, Shape vertexShape) { + GeneralPath path = new GeneralPath(edgeShape); + float[] seg = new float[6]; + Point2D p1 = null; + Point2D p2 = null; + AffineTransform at = new AffineTransform(); + // when the PathIterator is done, switch to the line-subdivide + // method to get the arrowhead closer. + for (PathIterator i = path.getPathIterator(null, 1); !i.isDone(); i.next()) { + int ret = i.currentSegment(seg); + if (ret == PathIterator.SEG_MOVETO) { + p2 = new Point2D.Float(seg[0], seg[1]); + } else if (ret == PathIterator.SEG_LINETO) { + p1 = p2; + p2 = new Point2D.Float(seg[0], seg[1]); + if (vertexShape.contains(p2)) { + at = getArrowTransform(rc, new Line2D.Float(p1, p2), vertexShape); + break; } - return at; + } } + return at; + } - public AffineTransform getReverseArrowTransform(RenderContext rc, Shape edgeShape, Shape vertexShape) { - return getReverseArrowTransform(rc, edgeShape, vertexShape, true); - } - - public AffineTransform getReverseArrowTransform(RenderContext rc, Shape edgeShape, Shape vertexShape, - boolean passedGo) { - GeneralPath path = new GeneralPath(edgeShape); - float[] seg = new float[6]; - Point2D p1=null; - Point2D p2=null; + public AffineTransform getReverseArrowTransform( + RenderContext rc, Shape edgeShape, Shape vertexShape) { + return getReverseArrowTransform(rc, edgeShape, vertexShape, true); + } - AffineTransform at = new AffineTransform(); - for(PathIterator i=path.getPathIterator(null,1); !i.isDone(); i.next()) { - int ret = i.currentSegment(seg); - if(ret == PathIterator.SEG_MOVETO) { - p2 = new Point2D.Float(seg[0],seg[1]); - } else if(ret == PathIterator.SEG_LINETO) { - p1 = p2; - p2 = new Point2D.Float(seg[0],seg[1]); - if(passedGo == false && vertexShape.contains(p2)) { - passedGo = true; - } else if(passedGo==true && - vertexShape.contains(p2)==false) { - at = getReverseArrowTransform(rc, new Line2D.Float(p1,p2),vertexShape); - break; - } - } - } - return at; - } + public AffineTransform getReverseArrowTransform( + RenderContext rc, Shape edgeShape, Shape vertexShape, boolean passedGo) { + GeneralPath path = new GeneralPath(edgeShape); + float[] seg = new float[6]; + Point2D p1 = null; + Point2D p2 = null; - public AffineTransform getArrowTransform(RenderContext rc, Line2D edgeShape, Shape vertexShape) { - float dx = (float) (edgeShape.getX1()-edgeShape.getX2()); - float dy = (float) (edgeShape.getY1()-edgeShape.getY2()); - // iterate over the line until the edge shape will place the - // arrowhead closer than 'arrowGap' to the vertex shape boundary - while((dx*dx+dy*dy) > rc.getArrowPlacementTolerance()) { - try { - edgeShape = getLastOutsideSegment(edgeShape, vertexShape); - } catch(IllegalArgumentException e) { - System.err.println(e.toString()); - return null; - } - dx = (float) (edgeShape.getX1()-edgeShape.getX2()); - dy = (float) (edgeShape.getY1()-edgeShape.getY2()); + AffineTransform at = new AffineTransform(); + for (PathIterator i = path.getPathIterator(null, 1); !i.isDone(); i.next()) { + int ret = i.currentSegment(seg); + if (ret == PathIterator.SEG_MOVETO) { + p2 = new Point2D.Float(seg[0], seg[1]); + } else if (ret == PathIterator.SEG_LINETO) { + p1 = p2; + p2 = new Point2D.Float(seg[0], seg[1]); + if (passedGo == false && vertexShape.contains(p2)) { + passedGo = true; + } else if (passedGo == true && vertexShape.contains(p2) == false) { + at = getReverseArrowTransform(rc, new Line2D.Float(p1, p2), vertexShape); + break; } - double atheta = Math.atan2(dx,dy)+Math.PI/2; - AffineTransform at = - AffineTransform.getTranslateInstance(edgeShape.getX1(), edgeShape.getY1()); - at.rotate(-atheta); - return at; + } } + return at; + } - protected AffineTransform getReverseArrowTransform(RenderContext rc, Line2D edgeShape, Shape vertexShape) { - float dx = (float) (edgeShape.getX1()-edgeShape.getX2()); - float dy = (float) (edgeShape.getY1()-edgeShape.getY2()); - // iterate over the line until the edge shape will place the - // arrowhead closer than 'arrowGap' to the vertex shape boundary - while((dx*dx+dy*dy) > rc.getArrowPlacementTolerance()) { - try { - edgeShape = getFirstOutsideSegment(edgeShape, vertexShape); - } catch(IllegalArgumentException e) { - System.err.println(e.toString()); - return null; - } - dx = (float) (edgeShape.getX1()-edgeShape.getX2()); - dy = (float) (edgeShape.getY1()-edgeShape.getY2()); - } - // calculate the angle for the arrowhead - double atheta = Math.atan2(dx,dy)-Math.PI/2; - AffineTransform at = AffineTransform.getTranslateInstance(edgeShape.getX1(),edgeShape.getY1()); - at.rotate(-atheta); - return at; + public AffineTransform getArrowTransform( + RenderContext rc, Line2D edgeShape, Shape vertexShape) { + float dx = (float) (edgeShape.getX1() - edgeShape.getX2()); + float dy = (float) (edgeShape.getY1() - edgeShape.getY2()); + // iterate over the line until the edge shape will place the + // arrowhead closer than 'arrowGap' to the vertex shape boundary + while ((dx * dx + dy * dy) > rc.getArrowPlacementTolerance()) { + try { + edgeShape = getLastOutsideSegment(edgeShape, vertexShape); + } catch (IllegalArgumentException e) { + System.err.println(e.toString()); + return null; + } + dx = (float) (edgeShape.getX1() - edgeShape.getX2()); + dy = (float) (edgeShape.getY1() - edgeShape.getY2()); } - - /** - * Returns a line that intersects {@code shape}'s boundary. - * - * @param line line to subdivide - * @param shape shape to compare with line - * @return a line that intersects the shape boundary - * @throws IllegalArgumentException if the passed line's point2 is not inside the shape - */ - protected Line2D getLastOutsideSegment(Line2D line, Shape shape) { - if(shape.contains(line.getP2())==false) { - String errorString = - "line end point: "+line.getP2()+" is not contained in shape: "+shape.getBounds2D(); - throw new IllegalArgumentException(errorString); - //return null; - } - Line2D left = new Line2D.Double(); - Line2D right = new Line2D.Double(); - // subdivide the line until its left segment intersects - // the shape boundary - do { - subdivide(line, left, right); - line = right; - } while(shape.contains(line.getP1())==false); - // now that right is completely inside shape, - // return left, which must be partially outside - return left; + double atheta = Math.atan2(dx, dy) + Math.PI / 2; + AffineTransform at = AffineTransform.getTranslateInstance(edgeShape.getX1(), edgeShape.getY1()); + at.rotate(-atheta); + return at; + } + + protected AffineTransform getReverseArrowTransform( + RenderContext rc, Line2D edgeShape, Shape vertexShape) { + float dx = (float) (edgeShape.getX1() - edgeShape.getX2()); + float dy = (float) (edgeShape.getY1() - edgeShape.getY2()); + // iterate over the line until the edge shape will place the + // arrowhead closer than 'arrowGap' to the vertex shape boundary + while ((dx * dx + dy * dy) > rc.getArrowPlacementTolerance()) { + try { + edgeShape = getFirstOutsideSegment(edgeShape, vertexShape); + } catch (IllegalArgumentException e) { + System.err.println(e.toString()); + return null; + } + dx = (float) (edgeShape.getX1() - edgeShape.getX2()); + dy = (float) (edgeShape.getY1() - edgeShape.getY2()); } - - /** - * Returns a line that intersects {@code shape}'s boundary. - * - * @param line line to subdivide - * @param shape shape to compare with line - * @return a line that intersects the shape boundary - * @throws IllegalArgumentException if the passed line's point1 is not inside the shape - */ - protected Line2D getFirstOutsideSegment(Line2D line, Shape shape) { - - if(shape.contains(line.getP1())==false) { - String errorString = - "line start point: "+line.getP1()+" is not contained in shape: "+shape.getBounds2D(); - throw new IllegalArgumentException(errorString); - } - Line2D left = new Line2D.Float(); - Line2D right = new Line2D.Float(); - // subdivide the line until its right side intersects the - // shape boundary - do { - subdivide(line, left, right); - line = left; - } while(shape.contains(line.getP2())==false); - // now that left is completely inside shape, - // return right, which must be partially outside - return right; + // calculate the angle for the arrowhead + double atheta = Math.atan2(dx, dy) - Math.PI / 2; + AffineTransform at = AffineTransform.getTranslateInstance(edgeShape.getX1(), edgeShape.getY1()); + at.rotate(-atheta); + return at; + } + + /** + * Returns a line that intersects {@code shape}'s boundary. + * + * @param line line to subdivide + * @param shape shape to compare with line + * @return a line that intersects the shape boundary + * @throws IllegalArgumentException if the passed line's point2 is not inside the shape + */ + protected Line2D getLastOutsideSegment(Line2D line, Shape shape) { + if (shape.contains(line.getP2()) == false) { + String errorString = + "line end point: " + line.getP2() + " is not contained in shape: " + shape.getBounds2D(); + throw new IllegalArgumentException(errorString); + //return null; } + Line2D left = new Line2D.Double(); + Line2D right = new Line2D.Double(); + // subdivide the line until its left segment intersects + // the shape boundary + do { + subdivide(line, left, right); + line = right; + } while (shape.contains(line.getP1()) == false); + // now that right is completely inside shape, + // return left, which must be partially outside + return left; + } - /** - * divide a Line2D into 2 new Line2Ds that are returned - * in the passed left and right instances, if non-null - * @param src the line to divide - * @param left the left side, or null - * @param right the right side, or null - */ - protected void subdivide(Line2D src, - Line2D left, - Line2D right) { - double x1 = src.getX1(); - double y1 = src.getY1(); - double x2 = src.getX2(); - double y2 = src.getY2(); - - double mx = x1 + (x2-x1)/2.0; - double my = y1 + (y2-y1)/2.0; - if (left != null) { - left.setLine(x1, y1, mx, my); - } - if (right != null) { - right.setLine(mx, my, x2, y2); - } + /** + * Returns a line that intersects {@code shape}'s boundary. + * + * @param line line to subdivide + * @param shape shape to compare with line + * @return a line that intersects the shape boundary + * @throws IllegalArgumentException if the passed line's point1 is not inside the shape + */ + protected Line2D getFirstOutsideSegment(Line2D line, Shape shape) { + + if (shape.contains(line.getP1()) == false) { + String errorString = + "line start point: " + + line.getP1() + + " is not contained in shape: " + + shape.getBounds2D(); + throw new IllegalArgumentException(errorString); } + Line2D left = new Line2D.Float(); + Line2D right = new Line2D.Float(); + // subdivide the line until its right side intersects the + // shape boundary + do { + subdivide(line, left, right); + line = left; + } while (shape.contains(line.getP2()) == false); + // now that left is completely inside shape, + // return right, which must be partially outside + return right; + } + + /** + * divide a Line2D into 2 new Line2Ds that are returned in the passed left and right instances, if + * non-null + * + * @param src the line to divide + * @param left the left side, or null + * @param right the right side, or null + */ + protected void subdivide(Line2D src, Line2D left, Line2D right) { + double x1 = src.getX1(); + double y1 = src.getY1(); + double x2 = src.getX2(); + double y2 = src.getY2(); + double mx = x1 + (x2 - x1) / 2.0; + double my = y1 + (y2 - y1) / 2.0; + if (left != null) { + left.setLine(x1, y1, mx, my); + } + if (right != null) { + right.setLine(mx, my, x2, y2); + } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicEdgeLabelRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicEdgeLabelRenderer.java index fb04bbdc..21ad92aa 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicEdgeLabelRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicEdgeLabelRenderer.java @@ -9,6 +9,12 @@ */ package edu.uci.ics.jung.visualization.renderers; +import com.google.common.base.Predicate; +import com.google.common.graph.EndpointPair; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; import java.awt.Component; import java.awt.Dimension; import java.awt.Shape; @@ -16,103 +22,103 @@ import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; -import com.google.common.base.Predicate; -import com.google.common.graph.EndpointPair; +public class BasicEdgeLabelRenderer implements Renderer.EdgeLabel { + private final Layout layout; + private final RenderContext renderContext; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; + public BasicEdgeLabelRenderer(Layout layout, RenderContext renderContext) { + this.layout = layout; + this.renderContext = renderContext; + } + + public Component prepareRenderer( + EdgeLabelRenderer graphLabelRenderer, Object value, boolean isSelected, E edge) { + return renderContext + .getEdgeLabelRenderer() + .getEdgeLabelRendererComponent( + renderContext.getScreenDevice(), + value, + renderContext.getEdgeFontTransformer().apply(edge), + isSelected, + edge); + } + + @Override + public void labelEdge(E e, String label) { + if (label == null || label.length() == 0) return; + + // don't draw edge if either incident vertex is not drawn + EndpointPair endpoints = renderContext.getNetwork().incidentNodes(e); + V v1 = endpoints.nodeU(); + V v2 = endpoints.nodeV(); + Predicate nodeIncludePredicate = renderContext.getVertexIncludePredicate(); + if (!nodeIncludePredicate.apply(v1) || !nodeIncludePredicate.apply(v2)) { + return; + } + + Point2D p1 = layout.apply(v1); + Point2D p2 = layout.apply(v2); + p1 = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p1); + p2 = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p2); + float x1 = (float) p1.getX(); + float y1 = (float) p1.getY(); + float x2 = (float) p2.getX(); + float y2 = (float) p2.getY(); + + GraphicsDecorator g = renderContext.getGraphicsContext(); + float distX = x2 - x1; + float distY = y2 - y1; + double totalLength = Math.sqrt(distX * distX + distY * distY); + + float closeness = renderContext.getEdgeLabelCloseness(); + + int posX = (int) (x1 + (closeness) * distX); + int posY = (int) (y1 + (closeness) * distY); + + int xDisplacement = (int) (renderContext.getLabelOffset() * (distY / totalLength)); + int yDisplacement = (int) (renderContext.getLabelOffset() * (-distX / totalLength)); + + Component component = + prepareRenderer( + renderContext.getEdgeLabelRenderer(), + label, + renderContext.getPickedEdgeState().isPicked(e), + e); + + Dimension d = component.getPreferredSize(); -public class BasicEdgeLabelRenderer implements Renderer.EdgeLabel { - private final Layout layout; - private final RenderContext renderContext; - - public BasicEdgeLabelRenderer(Layout layout, RenderContext renderContext) { - this.layout = layout; - this.renderContext = renderContext; - } - - public Component prepareRenderer(EdgeLabelRenderer graphLabelRenderer, Object value, - boolean isSelected, E edge) { - return renderContext.getEdgeLabelRenderer().getEdgeLabelRendererComponent(renderContext.getScreenDevice(), value, - renderContext.getEdgeFontTransformer().apply(edge), isSelected, edge); - } - - @Override - public void labelEdge(E e, String label) { - if(label == null || label.length() == 0) return; - - // don't draw edge if either incident vertex is not drawn - EndpointPair endpoints = renderContext.getNetwork().incidentNodes(e); - V v1 = endpoints.nodeU(); - V v2 = endpoints.nodeV(); - Predicate nodeIncludePredicate = renderContext.getVertexIncludePredicate(); - if (!nodeIncludePredicate.apply(v1) || !nodeIncludePredicate.apply(v2)) { - return; - } - - Point2D p1 = layout.apply(v1); - Point2D p2 = layout.apply(v2); - p1 = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p1); - p2 = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p2); - float x1 = (float) p1.getX(); - float y1 = (float) p1.getY(); - float x2 = (float) p2.getX(); - float y2 = (float) p2.getY(); - - GraphicsDecorator g = renderContext.getGraphicsContext(); - float distX = x2 - x1; - float distY = y2 - y1; - double totalLength = Math.sqrt(distX * distX + distY * distY); - - float closeness = renderContext.getEdgeLabelCloseness(); - - int posX = (int) (x1 + (closeness) * distX); - int posY = (int) (y1 + (closeness) * distY); - - int xDisplacement = (int) (renderContext.getLabelOffset() * (distY / totalLength)); - int yDisplacement = (int) (renderContext.getLabelOffset() * (-distX / totalLength)); - - Component component = prepareRenderer(renderContext.getEdgeLabelRenderer(), label, - renderContext.getPickedEdgeState().isPicked(e), e); - - Dimension d = component.getPreferredSize(); - - Shape edgeShape = renderContext.getEdgeShapeTransformer().apply(e); - - double parallelOffset = 1; - - parallelOffset += renderContext.getParallelEdgeIndexFunction().getIndex(e); - - parallelOffset *= d.height; - if(edgeShape instanceof Ellipse2D) { - parallelOffset += edgeShape.getBounds().getHeight(); - parallelOffset = -parallelOffset; - } - - - AffineTransform old = g.getTransform(); - AffineTransform xform = new AffineTransform(old); - xform.translate(posX+xDisplacement, posY+yDisplacement); - double dx = x2 - x1; - double dy = y2 - y1; - if(renderContext.getEdgeLabelRenderer().isRotateEdgeLabels()) { - double theta = Math.atan2(dy, dx); - if(dx < 0) { - theta += Math.PI; - } - xform.rotate(theta); - } - if(dx < 0) { - parallelOffset = -parallelOffset; - } - - xform.translate(-d.width/2, -(d.height/2-parallelOffset)); - g.setTransform(xform); - g.draw(component, renderContext.getRendererPane(), 0, 0, d.width, d.height, true); - - g.setTransform(old); + Shape edgeShape = renderContext.getEdgeShapeTransformer().apply(e); + + double parallelOffset = 1; + + parallelOffset += renderContext.getParallelEdgeIndexFunction().getIndex(e); + + parallelOffset *= d.height; + if (edgeShape instanceof Ellipse2D) { + parallelOffset += edgeShape.getBounds().getHeight(); + parallelOffset = -parallelOffset; } + AffineTransform old = g.getTransform(); + AffineTransform xform = new AffineTransform(old); + xform.translate(posX + xDisplacement, posY + yDisplacement); + double dx = x2 - x1; + double dy = y2 - y1; + if (renderContext.getEdgeLabelRenderer().isRotateEdgeLabels()) { + double theta = Math.atan2(dy, dx); + if (dx < 0) { + theta += Math.PI; + } + xform.rotate(theta); + } + if (dx < 0) { + parallelOffset = -parallelOffset; + } + + xform.translate(-d.width / 2, -(d.height / 2 - parallelOffset)); + g.setTransform(xform); + g.draw(component, renderContext.getRendererPane(), 0, 0, d.width, d.height, true); + + g.setTransform(old); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicRenderer.java index 516ad3f4..c2d8e4fc 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicRenderer.java @@ -1,138 +1,119 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. */ package edu.uci.ics.jung.visualization.renderers; -import java.util.ConcurrentModificationException; - import com.google.common.graph.Network; - import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.visualization.RenderContext; +import java.util.ConcurrentModificationException; /** - * The default implementation of the Renderer used by the - * VisualizationViewer. Default Vertex and Edge Renderers - * are supplied, or the user may set custom values. The - * Vertex and Edge renderers are used in the renderVertex - * and renderEdge methods, which are called in the render - * loop of the VisualizationViewer. - * + * The default implementation of the Renderer used by the VisualizationViewer. Default Vertex and + * Edge Renderers are supplied, or the user may set custom values. The Vertex and Edge renderers are + * used in the renderVertex and renderEdge methods, which are called in the render loop of the + * VisualizationViewer. + * * @author Tom Nelson */ -public class BasicRenderer implements Renderer { - - protected Renderer.Vertex vertexRenderer; - protected Renderer.VertexLabel vertexLabelRenderer; - protected Renderer.Edge edgeRenderer; - protected Renderer.EdgeLabel edgeLabelRenderer; - - protected final Layout layout; - protected final RenderContext renderContext; - - public BasicRenderer(Layout layout, RenderContext rc) { - this.layout = layout; - this.renderContext = rc; - this.vertexRenderer = new BasicVertexRenderer(layout, rc); - this.vertexLabelRenderer = new BasicVertexLabelRenderer(layout, rc); - this.edgeRenderer = new BasicEdgeRenderer(layout, rc); - this.edgeLabelRenderer = new BasicEdgeLabelRenderer(layout, rc); - } - - @Override - public void render() { - Network network = renderContext.getNetwork(); - // paint all the edges - try { - for(E e : network.edges()) { - renderEdge(e); - renderEdgeLabel(e); - } - } catch(ConcurrentModificationException cme) { - renderContext.getScreenDevice().repaint(); - } - - // paint all the vertices - try { - for(V v : network.nodes()) { - renderVertex(v); - renderVertexLabel(v); - } - } catch(ConcurrentModificationException cme) { - renderContext.getScreenDevice().repaint(); - } - } - - public void renderVertex(V v) { - vertexRenderer.paintVertex(v); - } - - public void renderVertexLabel(V v) { - vertexLabelRenderer.labelVertex(v, renderContext.getVertexLabelTransformer().apply(v)); - } - - public void renderEdge(E e) { - edgeRenderer.paintEdge(e); - } - - public void renderEdgeLabel(E e) { - edgeLabelRenderer.labelEdge(e, renderContext.getEdgeLabelTransformer().apply(e)); - } - - public void setVertexRenderer(Renderer.Vertex r) { - this.vertexRenderer = r; +public class BasicRenderer implements Renderer { + + protected Renderer.Vertex vertexRenderer; + protected Renderer.VertexLabel vertexLabelRenderer; + protected Renderer.Edge edgeRenderer; + protected Renderer.EdgeLabel edgeLabelRenderer; + + protected final Layout layout; + protected final RenderContext renderContext; + + public BasicRenderer(Layout layout, RenderContext rc) { + this.layout = layout; + this.renderContext = rc; + this.vertexRenderer = new BasicVertexRenderer(layout, rc); + this.vertexLabelRenderer = new BasicVertexLabelRenderer(layout, rc); + this.edgeRenderer = new BasicEdgeRenderer(layout, rc); + this.edgeLabelRenderer = new BasicEdgeLabelRenderer(layout, rc); + } + + @Override + public void render() { + Network network = renderContext.getNetwork(); + // paint all the edges + try { + for (E e : network.edges()) { + renderEdge(e); + renderEdgeLabel(e); + } + } catch (ConcurrentModificationException cme) { + renderContext.getScreenDevice().repaint(); } - public void setEdgeRenderer(Renderer.Edge r) { - this.edgeRenderer = r; + // paint all the vertices + try { + for (V v : network.nodes()) { + renderVertex(v); + renderVertexLabel(v); + } + } catch (ConcurrentModificationException cme) { + renderContext.getScreenDevice().repaint(); } + } + + public void renderVertex(V v) { + vertexRenderer.paintVertex(v); + } + + public void renderVertexLabel(V v) { + vertexLabelRenderer.labelVertex(v, renderContext.getVertexLabelTransformer().apply(v)); + } + + public void renderEdge(E e) { + edgeRenderer.paintEdge(e); + } + + public void renderEdgeLabel(E e) { + edgeLabelRenderer.labelEdge(e, renderContext.getEdgeLabelTransformer().apply(e)); + } + + public void setVertexRenderer(Renderer.Vertex r) { + this.vertexRenderer = r; + } + + public void setEdgeRenderer(Renderer.Edge r) { + this.edgeRenderer = r; + } + + /** @return the edgeLabelRenderer */ + public Renderer.EdgeLabel getEdgeLabelRenderer() { + return edgeLabelRenderer; + } + + /** @param edgeLabelRenderer the edgeLabelRenderer to set */ + public void setEdgeLabelRenderer(Renderer.EdgeLabel edgeLabelRenderer) { + this.edgeLabelRenderer = edgeLabelRenderer; + } + + /** @return the vertexLabelRenderer */ + public Renderer.VertexLabel getVertexLabelRenderer() { + return vertexLabelRenderer; + } + + /** @param vertexLabelRenderer the vertexLabelRenderer to set */ + public void setVertexLabelRenderer(Renderer.VertexLabel vertexLabelRenderer) { + this.vertexLabelRenderer = vertexLabelRenderer; + } + + /** @return the edgeRenderer */ + public Renderer.Edge getEdgeRenderer() { + return edgeRenderer; + } - /** - * @return the edgeLabelRenderer - */ - public Renderer.EdgeLabel getEdgeLabelRenderer() { - return edgeLabelRenderer; - } - - /** - * @param edgeLabelRenderer the edgeLabelRenderer to set - */ - public void setEdgeLabelRenderer(Renderer.EdgeLabel edgeLabelRenderer) { - this.edgeLabelRenderer = edgeLabelRenderer; - } - - /** - * @return the vertexLabelRenderer - */ - public Renderer.VertexLabel getVertexLabelRenderer() { - return vertexLabelRenderer; - } - - /** - * @param vertexLabelRenderer the vertexLabelRenderer to set - */ - public void setVertexLabelRenderer( - Renderer.VertexLabel vertexLabelRenderer) { - this.vertexLabelRenderer = vertexLabelRenderer; - } - - /** - * @return the edgeRenderer - */ - public Renderer.Edge getEdgeRenderer() { - return edgeRenderer; - } - - /** - * @return the vertexRenderer - */ - public Renderer.Vertex getVertexRenderer() { - return vertexRenderer; - } - - -} \ No newline at end of file + /** @return the vertexRenderer */ + public Renderer.Vertex getVertexRenderer() { + return vertexRenderer; + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicVertexLabelRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicVertexLabelRenderer.java index f7987948..410c7efd 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicVertexLabelRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicVertexLabelRenderer.java @@ -9,15 +9,6 @@ */ package edu.uci.ics.jung.visualization.renderers; - -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Point; -import java.awt.Shape; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; - import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.RenderContext; @@ -25,174 +16,182 @@ import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer; import edu.uci.ics.jung.visualization.transform.shape.TransformingGraphics; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; public class BasicVertexLabelRenderer implements Renderer.VertexLabel { - protected Position position = Position.SE; - private Positioner positioner = new OutsidePositioner(); - protected final Layout layout; - protected final RenderContext renderContext; - - public BasicVertexLabelRenderer(Layout layout, RenderContext rc) { - this.layout = layout; - this.renderContext = rc; - } - - /** - * @return the position - */ - public Position getPosition() { - return position; - } - - /** - * @param position the position to set - */ - public void setPosition(Position position) { - this.position = position; - } - - public Component prepareRenderer(VertexLabelRenderer graphLabelRenderer, Object value, - boolean isSelected, V vertex) { - return renderContext.getVertexLabelRenderer().getVertexLabelRendererComponent(renderContext.getScreenDevice(), value, - renderContext.getVertexFontTransformer().apply(vertex), isSelected, vertex); - } - - /** - * Labels the specified vertex with the specified label. - * Uses the font specified by this instance's - * VertexFontFunction. (If the font is unspecified, the existing - * font for the graphics context is used.) If vertex label centering - * is active, the label is centered on the position of the vertex; otherwise - * the label is offset slightly. - */ - public void labelVertex(V v, String label) { - if (renderContext.getVertexIncludePredicate().apply(v) == false) { - return; - } - Point2D pt = layout.apply(v); - pt = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, pt); - - float x = (float) pt.getX(); - float y = (float) pt.getY(); - - Component component = prepareRenderer(renderContext.getVertexLabelRenderer(), label, - renderContext.getPickedVertexState().isPicked(v), v); - GraphicsDecorator g = renderContext.getGraphicsContext(); - Dimension d = component.getPreferredSize(); - AffineTransform xform = AffineTransform.getTranslateInstance(x, y); - - Shape shape = renderContext.getVertexShapeTransformer().apply(v); - shape = xform.createTransformedShape(shape); - if(renderContext.getGraphicsContext() instanceof TransformingGraphics) { - BidirectionalTransformer transformer = ((TransformingGraphics)renderContext.getGraphicsContext()).getTransformer(); - if(transformer instanceof ShapeTransformer) { - ShapeTransformer shapeTransformer = (ShapeTransformer)transformer; - shape = shapeTransformer.transform(shape); - } - } - Rectangle2D bounds = shape.getBounds2D(); - - Point p = null; - if(position == Position.AUTO) { - Dimension vvd = renderContext.getScreenDevice().getSize(); - if(vvd.width == 0 || vvd.height == 0) { - vvd = renderContext.getScreenDevice().getPreferredSize(); - } - p = getAnchorPoint(bounds, d, positioner.getPosition(x, y, vvd)); - } else { - p = getAnchorPoint(bounds, d, position); - } - g.draw(component, renderContext.getRendererPane(), p.x, p.y, d.width, d.height, true); + protected Position position = Position.SE; + private Positioner positioner = new OutsidePositioner(); + protected final Layout layout; + protected final RenderContext renderContext; + + public BasicVertexLabelRenderer(Layout layout, RenderContext rc) { + this.layout = layout; + this.renderContext = rc; + } + + /** @return the position */ + public Position getPosition() { + return position; + } + + /** @param position the position to set */ + public void setPosition(Position position) { + this.position = position; + } + + public Component prepareRenderer( + VertexLabelRenderer graphLabelRenderer, Object value, boolean isSelected, V vertex) { + return renderContext + .getVertexLabelRenderer() + .getVertexLabelRendererComponent( + renderContext.getScreenDevice(), + value, + renderContext.getVertexFontTransformer().apply(vertex), + isSelected, + vertex); + } + + /** + * Labels the specified vertex with the specified label. Uses the font specified by this + * instance's VertexFontFunction. (If the font is unspecified, the existing font for + * the graphics context is used.) If vertex label centering is active, the label is centered on + * the position of the vertex; otherwise the label is offset slightly. + */ + public void labelVertex(V v, String label) { + if (renderContext.getVertexIncludePredicate().apply(v) == false) { + return; + } + Point2D pt = layout.apply(v); + pt = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, pt); + + float x = (float) pt.getX(); + float y = (float) pt.getY(); + + Component component = + prepareRenderer( + renderContext.getVertexLabelRenderer(), + label, + renderContext.getPickedVertexState().isPicked(v), + v); + GraphicsDecorator g = renderContext.getGraphicsContext(); + Dimension d = component.getPreferredSize(); + AffineTransform xform = AffineTransform.getTranslateInstance(x, y); + + Shape shape = renderContext.getVertexShapeTransformer().apply(v); + shape = xform.createTransformedShape(shape); + if (renderContext.getGraphicsContext() instanceof TransformingGraphics) { + BidirectionalTransformer transformer = + ((TransformingGraphics) renderContext.getGraphicsContext()).getTransformer(); + if (transformer instanceof ShapeTransformer) { + ShapeTransformer shapeTransformer = (ShapeTransformer) transformer; + shape = shapeTransformer.transform(shape); + } + } + Rectangle2D bounds = shape.getBounds2D(); + + Point p = null; + if (position == Position.AUTO) { + Dimension vvd = renderContext.getScreenDevice().getSize(); + if (vvd.width == 0 || vvd.height == 0) { + vvd = renderContext.getScreenDevice().getPreferredSize(); + } + p = getAnchorPoint(bounds, d, positioner.getPosition(x, y, vvd)); + } else { + p = getAnchorPoint(bounds, d, position); } - - protected Point getAnchorPoint(Rectangle2D vertexBounds, Dimension labelSize, Position position) { - double x; - double y; - int offset = 5; - switch(position) { - - case N: - x = vertexBounds.getCenterX()-labelSize.width/2; - y = vertexBounds.getMinY()-offset - labelSize.height; - return new Point((int)x,(int)y); - - case NE: - x = vertexBounds.getMaxX()+offset; - y = vertexBounds.getMinY()-offset-labelSize.height; - return new Point((int)x,(int)y); - - case E: - x = vertexBounds.getMaxX()+offset; - y = vertexBounds.getCenterY()-labelSize.height/2; - return new Point((int)x,(int)y); - - case SE: - x = vertexBounds.getMaxX()+offset; - y = vertexBounds.getMaxY()+offset; - return new Point((int)x,(int)y); - - case S: - x = vertexBounds.getCenterX()-labelSize.width/2; - y = vertexBounds.getMaxY()+offset; - return new Point((int)x,(int)y); - - case SW: - x = vertexBounds.getMinX()-offset-labelSize.width; - y = vertexBounds.getMaxY()+offset; - return new Point((int)x,(int)y); - - case W: - x = vertexBounds.getMinX()-offset-labelSize.width; - y = vertexBounds.getCenterY()-labelSize.height/2; - return new Point((int)x,(int)y); - - case NW: - x = vertexBounds.getMinX()-offset-labelSize.width; - y = vertexBounds.getMinY()-offset-labelSize.height; - return new Point((int)x,(int)y); - - case CNTR: - x = vertexBounds.getCenterX()-labelSize.width/2; - y = vertexBounds.getCenterY()-labelSize.height/2; - return new Point((int)x,(int)y); - - default: - return new Point(); - } - + g.draw(component, renderContext.getRendererPane(), p.x, p.y, d.width, d.height, true); + } + + protected Point getAnchorPoint(Rectangle2D vertexBounds, Dimension labelSize, Position position) { + double x; + double y; + int offset = 5; + switch (position) { + case N: + x = vertexBounds.getCenterX() - labelSize.width / 2; + y = vertexBounds.getMinY() - offset - labelSize.height; + return new Point((int) x, (int) y); + + case NE: + x = vertexBounds.getMaxX() + offset; + y = vertexBounds.getMinY() - offset - labelSize.height; + return new Point((int) x, (int) y); + + case E: + x = vertexBounds.getMaxX() + offset; + y = vertexBounds.getCenterY() - labelSize.height / 2; + return new Point((int) x, (int) y); + + case SE: + x = vertexBounds.getMaxX() + offset; + y = vertexBounds.getMaxY() + offset; + return new Point((int) x, (int) y); + + case S: + x = vertexBounds.getCenterX() - labelSize.width / 2; + y = vertexBounds.getMaxY() + offset; + return new Point((int) x, (int) y); + + case SW: + x = vertexBounds.getMinX() - offset - labelSize.width; + y = vertexBounds.getMaxY() + offset; + return new Point((int) x, (int) y); + + case W: + x = vertexBounds.getMinX() - offset - labelSize.width; + y = vertexBounds.getCenterY() - labelSize.height / 2; + return new Point((int) x, (int) y); + + case NW: + x = vertexBounds.getMinX() - offset - labelSize.width; + y = vertexBounds.getMinY() - offset - labelSize.height; + return new Point((int) x, (int) y); + + case CNTR: + x = vertexBounds.getCenterX() - labelSize.width / 2; + y = vertexBounds.getCenterY() - labelSize.height / 2; + return new Point((int) x, (int) y); + + default: + return new Point(); } - public static class InsidePositioner implements Positioner { - public Position getPosition(float x, float y, Dimension d) { - int cx = d.width/2; - int cy = d.height/2; - if(x > cx && y > cy) return Position.NW; - if(x > cx && y < cy) return Position.SW; - if(x < cx && y > cy) return Position.NE; - return Position.SE; - } + } + + public static class InsidePositioner implements Positioner { + public Position getPosition(float x, float y, Dimension d) { + int cx = d.width / 2; + int cy = d.height / 2; + if (x > cx && y > cy) return Position.NW; + if (x > cx && y < cy) return Position.SW; + if (x < cx && y > cy) return Position.NE; + return Position.SE; } - public static class OutsidePositioner implements Positioner { - public Position getPosition(float x, float y, Dimension d) { - int cx = d.width/2; - int cy = d.height/2; - if(x > cx && y > cy) return Position.SE; - if(x > cx && y < cy) return Position.NE; - if(x < cx && y > cy) return Position.SW; - return Position.NW; - } + } + + public static class OutsidePositioner implements Positioner { + public Position getPosition(float x, float y, Dimension d) { + int cx = d.width / 2; + int cy = d.height / 2; + if (x > cx && y > cy) return Position.SE; + if (x > cx && y < cy) return Position.NE; + if (x < cx && y > cy) return Position.SW; + return Position.NW; } - /** - * @return the positioner - */ - public Positioner getPositioner() { - return positioner; - } - - /** - * @param positioner the positioner to set - */ - public void setPositioner(Positioner positioner) { - this.positioner = positioner; - } + } + /** @return the positioner */ + public Positioner getPositioner() { + return positioner; + } + + /** @param positioner the positioner to set */ + public void setPositioner(Positioner positioner) { + this.positioner = positioner; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicVertexRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicVertexRenderer.java index de428ffa..ccb8474d 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicVertexRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/BasicVertexRenderer.java @@ -9,6 +9,12 @@ */ package edu.uci.ics.jung.visualization.renderers; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.transform.MutableTransformer; +import edu.uci.ics.jung.visualization.transform.MutableTransformerDecorator; +import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; import java.awt.Dimension; import java.awt.Paint; import java.awt.Rectangle; @@ -16,120 +22,111 @@ import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; - import javax.swing.Icon; import javax.swing.JComponent; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.transform.MutableTransformer; -import edu.uci.ics.jung.visualization.transform.MutableTransformerDecorator; -import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; - public class BasicVertexRenderer implements Renderer.Vertex { - protected final Layout layout; - protected final RenderContext renderContext; - - public BasicVertexRenderer(Layout layout, RenderContext rc) { - this.layout = layout; - this.renderContext = rc; - } + protected final Layout layout; + protected final RenderContext renderContext; - public void paintVertex(V v) { - if (renderContext.getVertexIncludePredicate().apply(v)) { - paintIconForVertex(v); - } - } - - /** - * Returns the vertex shape in view coordinates. - * @param v the vertex whose shape is to be returned - * @param coords the x and y view coordinates - * @return the vertex shape in view coordinates - */ - protected Shape prepareFinalVertexShape(V v, int[] coords) { + public BasicVertexRenderer(Layout layout, RenderContext rc) { + this.layout = layout; + this.renderContext = rc; + } - // get the shape to be rendered - Shape shape = renderContext.getVertexShapeTransformer().apply(v); - Point2D p = layout.apply(v); - p = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p); - float x = (float)p.getX(); - float y = (float)p.getY(); - coords[0] = (int)x; - coords[1] = (int)y; - // create a transform that translates to the location of - // the vertex to be rendered - AffineTransform xform = AffineTransform.getTranslateInstance(x,y); - // transform the vertex shape with xtransform - shape = xform.createTransformedShape(shape); - return shape; + public void paintVertex(V v) { + if (renderContext.getVertexIncludePredicate().apply(v)) { + paintIconForVertex(v); } - - /** - * Paint v's icon on g at (x,y). - * - * @param v the vertex to be painted - */ - protected void paintIconForVertex(V v) { - GraphicsDecorator g = renderContext.getGraphicsContext(); - boolean vertexHit = true; - int[] coords = new int[2]; - Shape shape = prepareFinalVertexShape(v, coords); - vertexHit = vertexHit(shape); + } + + /** + * Returns the vertex shape in view coordinates. + * + * @param v the vertex whose shape is to be returned + * @param coords the x and y view coordinates + * @return the vertex shape in view coordinates + */ + protected Shape prepareFinalVertexShape(V v, int[] coords) { + + // get the shape to be rendered + Shape shape = renderContext.getVertexShapeTransformer().apply(v); + Point2D p = layout.apply(v); + p = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p); + float x = (float) p.getX(); + float y = (float) p.getY(); + coords[0] = (int) x; + coords[1] = (int) y; + // create a transform that translates to the location of + // the vertex to be rendered + AffineTransform xform = AffineTransform.getTranslateInstance(x, y); + // transform the vertex shape with xtransform + shape = xform.createTransformedShape(shape); + return shape; + } - if (vertexHit) { - if(renderContext.getVertexIconTransformer() != null) { - Icon icon = renderContext.getVertexIconTransformer().apply(v); - if(icon != null) { - - g.draw(icon, renderContext.getScreenDevice(), shape, coords[0], coords[1]); + /** + * Paint v's icon on g at (x,y). + * + * @param v the vertex to be painted + */ + protected void paintIconForVertex(V v) { + GraphicsDecorator g = renderContext.getGraphicsContext(); + boolean vertexHit = true; + int[] coords = new int[2]; + Shape shape = prepareFinalVertexShape(v, coords); + vertexHit = vertexHit(shape); - } else { - paintShapeForVertex(v, shape); - } - } else { - paintShapeForVertex(v, shape); - } + if (vertexHit) { + if (renderContext.getVertexIconTransformer() != null) { + Icon icon = renderContext.getVertexIconTransformer().apply(v); + if (icon != null) { + + g.draw(icon, renderContext.getScreenDevice(), shape, coords[0], coords[1]); + + } else { + paintShapeForVertex(v, shape); } + } else { + paintShapeForVertex(v, shape); + } } - - protected boolean vertexHit(Shape s) { - JComponent vv = renderContext.getScreenDevice(); - Rectangle deviceRectangle = null; - if(vv != null) { - Dimension d = vv.getSize(); - deviceRectangle = new Rectangle( - 0,0, - d.width,d.height); - } - MutableTransformer vt = renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW); - if(vt instanceof MutableTransformerDecorator) { - vt = ((MutableTransformerDecorator)vt).getDelegate(); - } - return vt.transform(s).intersects(deviceRectangle); + } + + protected boolean vertexHit(Shape s) { + JComponent vv = renderContext.getScreenDevice(); + Rectangle deviceRectangle = null; + if (vv != null) { + Dimension d = vv.getSize(); + deviceRectangle = new Rectangle(0, 0, d.width, d.height); + } + MutableTransformer vt = renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW); + if (vt instanceof MutableTransformerDecorator) { + vt = ((MutableTransformerDecorator) vt).getDelegate(); } + return vt.transform(s).intersects(deviceRectangle); + } - protected void paintShapeForVertex(V v, Shape shape) { - GraphicsDecorator g = renderContext.getGraphicsContext(); - Paint oldPaint = g.getPaint(); - Paint fillPaint = renderContext.getVertexFillPaintTransformer().apply(v); - if(fillPaint != null) { - g.setPaint(fillPaint); - g.fill(shape); - g.setPaint(oldPaint); - } - Paint drawPaint = renderContext.getVertexDrawPaintTransformer().apply(v); - if(drawPaint != null) { - g.setPaint(drawPaint); - Stroke oldStroke = g.getStroke(); - Stroke stroke = renderContext.getVertexStrokeTransformer().apply(v); - if(stroke != null) { - g.setStroke(stroke); - } - g.draw(shape); - g.setPaint(oldPaint); - g.setStroke(oldStroke); - } + protected void paintShapeForVertex(V v, Shape shape) { + GraphicsDecorator g = renderContext.getGraphicsContext(); + Paint oldPaint = g.getPaint(); + Paint fillPaint = renderContext.getVertexFillPaintTransformer().apply(v); + if (fillPaint != null) { + g.setPaint(fillPaint); + g.fill(shape); + g.setPaint(oldPaint); + } + Paint drawPaint = renderContext.getVertexDrawPaintTransformer().apply(v); + if (drawPaint != null) { + g.setPaint(drawPaint); + Stroke oldStroke = g.getStroke(); + Stroke stroke = renderContext.getVertexStrokeTransformer().apply(v); + if (stroke != null) { + g.setStroke(stroke); + } + g.draw(shape); + g.setPaint(oldPaint); + g.setStroke(oldStroke); } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CachingEdgeRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CachingEdgeRenderer.java index 24b48f05..e7d90e23 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CachingEdgeRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CachingEdgeRenderer.java @@ -1,5 +1,16 @@ package edu.uci.ics.jung.visualization.renderers; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.BasicVisualizationServer; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.layout.LayoutChangeListener; +import edu.uci.ics.jung.visualization.layout.LayoutEvent; +import edu.uci.ics.jung.visualization.layout.LayoutEventSupport; +import edu.uci.ics.jung.visualization.transform.LensTransformer; +import edu.uci.ics.jung.visualization.transform.MutableTransformer; +import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; import java.awt.Dimension; import java.awt.Paint; import java.awt.Rectangle; @@ -10,175 +21,168 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - import javax.swing.JComponent; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import com.google.common.graph.Network; +public class CachingEdgeRenderer extends BasicEdgeRenderer + implements ChangeListener, LayoutChangeListener { -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.BasicVisualizationServer; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.layout.LayoutChangeListener; -import edu.uci.ics.jung.visualization.layout.LayoutEvent; -import edu.uci.ics.jung.visualization.layout.LayoutEventSupport; -import edu.uci.ics.jung.visualization.transform.LensTransformer; -import edu.uci.ics.jung.visualization.transform.MutableTransformer; -import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; + protected Map edgeShapeMap = new HashMap(); + protected Set dirtyEdges = new HashSet(); -public class CachingEdgeRenderer extends BasicEdgeRenderer - implements ChangeListener, LayoutChangeListener { - - protected Map edgeShapeMap = new HashMap(); - protected Set dirtyEdges = new HashSet(); - - public CachingEdgeRenderer(Layout layout, RenderContext rc) { - super(layout, rc); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public CachingEdgeRenderer(BasicVisualizationServer vv) { - super(vv.getGraphLayout(), vv.getRenderContext()); - vv.getRenderContext().getMultiLayerTransformer().addChangeListener(this); - Layout layout = vv.getGraphLayout(); - if(layout instanceof LayoutEventSupport) { - ((LayoutEventSupport)layout).addLayoutChangeListener(this); - } - } - /** - * Draws the edge e, whose endpoints are at (x1,y1) - * and (x2,y2), on the graphics context g. - * The Shape provided by the EdgeShapeFunction instance - * is scaled in the x-direction so that its width is equal to the distance between - * (x1,y1) and (x2,y2). - */ - @Override - protected void drawSimpleEdge(E e) { - - int[] coords = new int[4]; - boolean[] loop = new boolean[1]; - - Shape edgeShape = edgeShapeMap.get(e); - if(edgeShape == null || dirtyEdges.contains(e)) { - edgeShape = prepareFinalEdgeShape(e, coords, loop); - edgeShapeMap.put(e, edgeShape); - dirtyEdges.remove(e); - } - - int x1 = coords[0]; - int y1 = coords[1]; - int x2 = coords[2]; - int y2 = coords[3]; - boolean isLoop = loop[0]; - - GraphicsDecorator g = renderContext.getGraphicsContext(); - Network graph = renderContext.getNetwork(); - boolean edgeHit = true; - boolean arrowHit = true; - Rectangle deviceRectangle = null; - JComponent vv = renderContext.getScreenDevice(); - if(vv != null) { - Dimension d = vv.getSize(); - deviceRectangle = new Rectangle(0,0,d.width,d.height); - } - MutableTransformer vt = renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW); - if(vt instanceof LensTransformer) { - vt = ((LensTransformer)vt).getDelegate(); + public CachingEdgeRenderer(Layout layout, RenderContext rc) { + super(layout, rc); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public CachingEdgeRenderer(BasicVisualizationServer vv) { + super(vv.getGraphLayout(), vv.getRenderContext()); + vv.getRenderContext().getMultiLayerTransformer().addChangeListener(this); + Layout layout = vv.getGraphLayout(); + if (layout instanceof LayoutEventSupport) { + ((LayoutEventSupport) layout).addLayoutChangeListener(this); + } + } + /** + * Draws the edge e, whose endpoints are at (x1,y1) and (x2,y2) + * , on the graphics context g. The Shape provided by the + * EdgeShapeFunction instance is scaled in the x-direction so that its width is equal to + * the distance between (x1,y1) and (x2,y2). + */ + @Override + protected void drawSimpleEdge(E e) { + + int[] coords = new int[4]; + boolean[] loop = new boolean[1]; + + Shape edgeShape = edgeShapeMap.get(e); + if (edgeShape == null || dirtyEdges.contains(e)) { + edgeShape = prepareFinalEdgeShape(e, coords, loop); + edgeShapeMap.put(e, edgeShape); + dirtyEdges.remove(e); + } + + int x1 = coords[0]; + int y1 = coords[1]; + int x2 = coords[2]; + int y2 = coords[3]; + boolean isLoop = loop[0]; + + GraphicsDecorator g = renderContext.getGraphicsContext(); + Network graph = renderContext.getNetwork(); + boolean edgeHit = true; + boolean arrowHit = true; + Rectangle deviceRectangle = null; + JComponent vv = renderContext.getScreenDevice(); + if (vv != null) { + Dimension d = vv.getSize(); + deviceRectangle = new Rectangle(0, 0, d.width, d.height); + } + MutableTransformer vt = renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW); + if (vt instanceof LensTransformer) { + vt = ((LensTransformer) vt).getDelegate(); + } + edgeHit = vt.transform(edgeShape).intersects(deviceRectangle); + + if (edgeHit == true) { + + Paint oldPaint = g.getPaint(); + + // get Paints for filling and drawing + // (filling is done first so that drawing and label use same Paint) + Paint fill_paint = renderContext.getEdgeFillPaintTransformer().apply(e); + if (fill_paint != null) { + g.setPaint(fill_paint); + g.fill(edgeShape); + } + Paint draw_paint = renderContext.getEdgeDrawPaintTransformer().apply(e); + if (draw_paint != null) { + g.setPaint(draw_paint); + g.draw(edgeShape); + } + + float scalex = (float) g.getTransform().getScaleX(); + float scaley = (float) g.getTransform().getScaleY(); + // see if arrows are too small to bother drawing + if (scalex < .3 || scaley < .3) return; + + if (renderContext.renderEdgeArrow()) { + + Stroke new_stroke = renderContext.getEdgeArrowStrokeTransformer().apply(e); + Stroke old_stroke = g.getStroke(); + if (new_stroke != null) g.setStroke(new_stroke); + + Shape destVertexShape = + renderContext.getVertexShapeTransformer().apply(graph.incidentNodes(e).nodeV()); + + AffineTransform xf = AffineTransform.getTranslateInstance(x2, y2); + destVertexShape = xf.createTransformedShape(destVertexShape); + + arrowHit = + renderContext + .getMultiLayerTransformer() + .getTransformer(Layer.VIEW) + .transform(destVertexShape) + .intersects(deviceRectangle); + if (arrowHit) { + + AffineTransform at = + edgeArrowRenderingSupport.getArrowTransform( + renderContext, edgeShape, destVertexShape); + if (at == null) return; + Shape arrow = renderContext.getEdgeArrow(); + arrow = at.createTransformedShape(arrow); + g.setPaint(renderContext.getArrowFillPaintTransformer().apply(e)); + g.fill(arrow); + g.setPaint(renderContext.getArrowDrawPaintTransformer().apply(e)); + g.draw(arrow); } - edgeHit = vt.transform(edgeShape).intersects(deviceRectangle); - - if(edgeHit == true) { - - Paint oldPaint = g.getPaint(); - - // get Paints for filling and drawing - // (filling is done first so that drawing and label use same Paint) - Paint fill_paint = renderContext.getEdgeFillPaintTransformer().apply(e); - if (fill_paint != null) - { - g.setPaint(fill_paint); - g.fill(edgeShape); - } - Paint draw_paint = renderContext.getEdgeDrawPaintTransformer().apply(e); - if (draw_paint != null) - { - g.setPaint(draw_paint); - g.draw(edgeShape); - } - - float scalex = (float)g.getTransform().getScaleX(); - float scaley = (float)g.getTransform().getScaleY(); - // see if arrows are too small to bother drawing - if(scalex < .3 || scaley < .3) return; - - if (renderContext.renderEdgeArrow()) { - - Stroke new_stroke = renderContext.getEdgeArrowStrokeTransformer().apply(e); - Stroke old_stroke = g.getStroke(); - if (new_stroke != null) - g.setStroke(new_stroke); - - - Shape destVertexShape = - renderContext.getVertexShapeTransformer().apply(graph.incidentNodes(e).nodeV()); - - AffineTransform xf = AffineTransform.getTranslateInstance(x2, y2); - destVertexShape = xf.createTransformedShape(destVertexShape); - - arrowHit = renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW).transform(destVertexShape).intersects(deviceRectangle); - if(arrowHit) { - - AffineTransform at = - edgeArrowRenderingSupport.getArrowTransform(renderContext, edgeShape, destVertexShape); - if(at == null) return; - Shape arrow = renderContext.getEdgeArrow(); - arrow = at.createTransformedShape(arrow); - g.setPaint(renderContext.getArrowFillPaintTransformer().apply(e)); - g.fill(arrow); - g.setPaint(renderContext.getArrowDrawPaintTransformer().apply(e)); - g.draw(arrow); - } - if (!graph.isDirected()) { - Shape vertexShape = - renderContext.getVertexShapeTransformer().apply(graph.incidentNodes(e).nodeU()); - xf = AffineTransform.getTranslateInstance(x1, y1); - vertexShape = xf.createTransformedShape(vertexShape); - - arrowHit = renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW).transform(vertexShape).intersects(deviceRectangle); - - if(arrowHit) { - AffineTransform at = edgeArrowRenderingSupport.getReverseArrowTransform(renderContext, edgeShape, vertexShape, !isLoop); - if(at == null) return; - Shape arrow = renderContext.getEdgeArrow(); - arrow = at.createTransformedShape(arrow); - g.setPaint(renderContext.getArrowFillPaintTransformer().apply(e)); - g.fill(arrow); - g.setPaint(renderContext.getArrowDrawPaintTransformer().apply(e)); - g.draw(arrow); - } - } - // restore paint and stroke - if (new_stroke != null) - g.setStroke(old_stroke); - - } - - // restore old paint - g.setPaint(oldPaint); + if (!graph.isDirected()) { + Shape vertexShape = + renderContext.getVertexShapeTransformer().apply(graph.incidentNodes(e).nodeU()); + xf = AffineTransform.getTranslateInstance(x1, y1); + vertexShape = xf.createTransformedShape(vertexShape); + + arrowHit = + renderContext + .getMultiLayerTransformer() + .getTransformer(Layer.VIEW) + .transform(vertexShape) + .intersects(deviceRectangle); + + if (arrowHit) { + AffineTransform at = + edgeArrowRenderingSupport.getReverseArrowTransform( + renderContext, edgeShape, vertexShape, !isLoop); + if (at == null) return; + Shape arrow = renderContext.getEdgeArrow(); + arrow = at.createTransformedShape(arrow); + g.setPaint(renderContext.getArrowFillPaintTransformer().apply(e)); + g.fill(arrow); + g.setPaint(renderContext.getArrowDrawPaintTransformer().apply(e)); + g.draw(arrow); + } } + // restore paint and stroke + if (new_stroke != null) g.setStroke(old_stroke); + } + + // restore old paint + g.setPaint(oldPaint); } + } + + @Override + public void stateChanged(ChangeEvent evt) { + // System.err.println("got change event "+evt); + edgeShapeMap.clear(); + } - @Override - public void stateChanged(ChangeEvent evt) { -// System.err.println("got change event "+evt); - edgeShapeMap.clear(); - } - @Override - public void layoutChanged(LayoutEvent evt) { - V v = evt.getVertex(); - Network graph = renderContext.getNetwork(); - dirtyEdges.addAll(graph.incidentEdges(v)); - } + @Override + public void layoutChanged(LayoutEvent evt) { + V v = evt.getVertex(); + Network graph = renderContext.getNetwork(); + dirtyEdges.addAll(graph.incidentEdges(v)); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CachingRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CachingRenderer.java index 9b1a48ba..6f453fe7 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CachingRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CachingRenderer.java @@ -1,18 +1,17 @@ package edu.uci.ics.jung.visualization.renderers; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.RenderContext; import java.awt.Shape; import java.util.HashMap; import java.util.Map; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.RenderContext; +public class CachingRenderer extends BasicRenderer { -public class CachingRenderer extends BasicRenderer { - - protected Map edgeShapeMap = new HashMap(); - protected Map vertexShapeMap = new HashMap(); + protected Map edgeShapeMap = new HashMap(); + protected Map vertexShapeMap = new HashMap(); - public CachingRenderer(Layout layout, RenderContext rc) { - super(layout, rc); - } + public CachingRenderer(Layout layout, RenderContext rc) { + super(layout, rc); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CachingVertexRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CachingVertexRenderer.java index b82e6b30..698101b7 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CachingVertexRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CachingVertexRenderer.java @@ -1,77 +1,71 @@ package edu.uci.ics.jung.visualization.renderers; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.BasicVisualizationServer; +import edu.uci.ics.jung.visualization.layout.LayoutChangeListener; +import edu.uci.ics.jung.visualization.layout.LayoutEvent; +import edu.uci.ics.jung.visualization.layout.LayoutEventSupport; +import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; import java.awt.Shape; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; - import javax.swing.Icon; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.BasicVisualizationServer; -import edu.uci.ics.jung.visualization.layout.LayoutChangeListener; -import edu.uci.ics.jung.visualization.layout.LayoutEvent; -import edu.uci.ics.jung.visualization.layout.LayoutEventSupport; -import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; +public class CachingVertexRenderer extends BasicVertexRenderer + implements ChangeListener, LayoutChangeListener { -public class CachingVertexRenderer extends BasicVertexRenderer - implements ChangeListener, LayoutChangeListener { - - protected Map vertexShapeMap = new HashMap(); - - protected Set dirtyVertices = new HashSet(); - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public CachingVertexRenderer(BasicVisualizationServer vv) { - super(vv.getGraphLayout(), vv.getRenderContext()); - vv.getRenderContext().getMultiLayerTransformer().addChangeListener(this); - Layout layout = vv.getGraphLayout(); - if(layout instanceof LayoutEventSupport) { - ((LayoutEventSupport)layout).addLayoutChangeListener(this); - - } - } - - /** - * Paint v's icon on g at (x,y). - */ - protected void paintIconForVertex(V v) { - GraphicsDecorator g = renderContext.getGraphicsContext(); - boolean vertexHit = true; - int[] coords = new int[2]; - Shape shape = vertexShapeMap.get(v); - if(shape == null || dirtyVertices.contains(v)) { - shape = prepareFinalVertexShape(v, coords); - vertexShapeMap.put(v, shape); - dirtyVertices.remove(v); - } - vertexHit = vertexHit(shape); - if (vertexHit) { - if(renderContext.getVertexIconTransformer() != null) { - Icon icon = renderContext.getVertexIconTransformer().apply(v); - if(icon != null) { - - g.draw(icon, renderContext.getScreenDevice(), shape, coords[0], coords[1]); + protected Map vertexShapeMap = new HashMap(); + + protected Set dirtyVertices = new HashSet(); + + @SuppressWarnings({"rawtypes", "unchecked"}) + public CachingVertexRenderer(BasicVisualizationServer vv) { + super(vv.getGraphLayout(), vv.getRenderContext()); + vv.getRenderContext().getMultiLayerTransformer().addChangeListener(this); + Layout layout = vv.getGraphLayout(); + if (layout instanceof LayoutEventSupport) { + ((LayoutEventSupport) layout).addLayoutChangeListener(this); + } + } + + /** Paint v's icon on g at (x,y). */ + protected void paintIconForVertex(V v) { + GraphicsDecorator g = renderContext.getGraphicsContext(); + boolean vertexHit = true; + int[] coords = new int[2]; + Shape shape = vertexShapeMap.get(v); + if (shape == null || dirtyVertices.contains(v)) { + shape = prepareFinalVertexShape(v, coords); + vertexShapeMap.put(v, shape); + dirtyVertices.remove(v); + } + vertexHit = vertexHit(shape); + if (vertexHit) { + if (renderContext.getVertexIconTransformer() != null) { + Icon icon = renderContext.getVertexIconTransformer().apply(v); + if (icon != null) { + + g.draw(icon, renderContext.getScreenDevice(), shape, coords[0], coords[1]); - } else { - paintShapeForVertex(v, shape); - } - } else { - paintShapeForVertex(v, shape); - } + } else { + paintShapeForVertex(v, shape); } + } else { + paintShapeForVertex(v, shape); + } } + } - public void stateChanged(ChangeEvent evt) { -// System.err.println("got change event "+evt); - vertexShapeMap.clear(); - - } + public void stateChanged(ChangeEvent evt) { + // System.err.println("got change event "+evt); + vertexShapeMap.clear(); + } - public void layoutChanged(LayoutEvent evt) { - this.dirtyVertices.add(evt.getVertex()); - } + public void layoutChanged(LayoutEvent evt) { + this.dirtyVertices.add(evt.getVertex()); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CenterEdgeArrowRenderingSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CenterEdgeArrowRenderingSupport.java index 90206ba4..758316a9 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CenterEdgeArrowRenderingSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/CenterEdgeArrowRenderingSupport.java @@ -9,6 +9,7 @@ */ package edu.uci.ics.jung.visualization.renderers; +import edu.uci.ics.jung.visualization.RenderContext; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; @@ -16,143 +17,141 @@ import java.awt.geom.PathIterator; import java.awt.geom.Point2D; -import edu.uci.ics.jung.visualization.RenderContext; - -public class CenterEdgeArrowRenderingSupport implements EdgeArrowRenderingSupport { +public class CenterEdgeArrowRenderingSupport implements EdgeArrowRenderingSupport { - public AffineTransform getArrowTransform(RenderContext rc, Shape edgeShape, Shape vertexShape) { - GeneralPath path = new GeneralPath(edgeShape); - float[] seg = new float[6]; - Point2D p1=null; - Point2D p2=null; - AffineTransform at = new AffineTransform(); - // count the segments. - int middleSegment = 0; - int current = 0; - for(PathIterator i=path.getPathIterator(null,1); !i.isDone(); i.next()) { - current++; - } - middleSegment = current/2; - // find the middle segment - current = 0; - for(PathIterator i=path.getPathIterator(null,1); !i.isDone(); i.next()) { - current++; - int ret = i.currentSegment(seg); - if(ret == PathIterator.SEG_MOVETO) { - p2 = new Point2D.Float(seg[0],seg[1]); - } else if(ret == PathIterator.SEG_LINETO) { - p1 = p2; - p2 = new Point2D.Float(seg[0],seg[1]); - } - if(current > middleSegment) { // done - at = getArrowTransform(rc, new Line2D.Float(p1,p2),vertexShape); - break; - } - - } - return at; + public AffineTransform getArrowTransform( + RenderContext rc, Shape edgeShape, Shape vertexShape) { + GeneralPath path = new GeneralPath(edgeShape); + float[] seg = new float[6]; + Point2D p1 = null; + Point2D p2 = null; + AffineTransform at = new AffineTransform(); + // count the segments. + int middleSegment = 0; + int current = 0; + for (PathIterator i = path.getPathIterator(null, 1); !i.isDone(); i.next()) { + current++; + } + middleSegment = current / 2; + // find the middle segment + current = 0; + for (PathIterator i = path.getPathIterator(null, 1); !i.isDone(); i.next()) { + current++; + int ret = i.currentSegment(seg); + if (ret == PathIterator.SEG_MOVETO) { + p2 = new Point2D.Float(seg[0], seg[1]); + } else if (ret == PathIterator.SEG_LINETO) { + p1 = p2; + p2 = new Point2D.Float(seg[0], seg[1]); + } + if (current > middleSegment) { // done + at = getArrowTransform(rc, new Line2D.Float(p1, p2), vertexShape); + break; + } } + return at; + } - public AffineTransform getReverseArrowTransform(RenderContext rc, Shape edgeShape, Shape vertexShape) { - return getReverseArrowTransform(rc, edgeShape, vertexShape, true); + public AffineTransform getReverseArrowTransform( + RenderContext rc, Shape edgeShape, Shape vertexShape) { + return getReverseArrowTransform(rc, edgeShape, vertexShape, true); + } + + /** + * Returns a transform to position the arrowhead on this edge shape at the point where it + * intersects the passed vertex shape. + * + * @param rc the rendering context used for rendering the arrow + * @param edgeShape the shape used to draw the edge + * @param vertexShape the shape used to draw the vertex + * @param passedGo (ignored in this implementation) + */ + public AffineTransform getReverseArrowTransform( + RenderContext rc, Shape edgeShape, Shape vertexShape, boolean passedGo) { + GeneralPath path = new GeneralPath(edgeShape); + float[] seg = new float[6]; + Point2D p1 = null; + Point2D p2 = null; + AffineTransform at = new AffineTransform(); + // count the segments. + int middleSegment = 0; + int current = 0; + for (PathIterator i = path.getPathIterator(null, 1); !i.isDone(); i.next()) { + current++; } - - /** - * Returns a transform to position the arrowhead on this edge shape at the - * point where it intersects the passed vertex shape. - * - * @param rc the rendering context used for rendering the arrow - * @param edgeShape the shape used to draw the edge - * @param vertexShape the shape used to draw the vertex - * @param passedGo (ignored in this implementation) - */ - public AffineTransform getReverseArrowTransform(RenderContext rc, Shape edgeShape, Shape vertexShape, - boolean passedGo) { - GeneralPath path = new GeneralPath(edgeShape); - float[] seg = new float[6]; - Point2D p1=null; - Point2D p2=null; - AffineTransform at = new AffineTransform(); - // count the segments. - int middleSegment = 0; - int current = 0; - for(PathIterator i=path.getPathIterator(null,1); !i.isDone(); i.next()) { - current++; - } - middleSegment = current/2; - // find the middle segment - current = 0; - for(PathIterator i=path.getPathIterator(null,1); !i.isDone(); i.next()) { - current++; - int ret = i.currentSegment(seg); - if(ret == PathIterator.SEG_MOVETO) { - p2 = new Point2D.Float(seg[0],seg[1]); - } else if(ret == PathIterator.SEG_LINETO) { - p1 = p2; - p2 = new Point2D.Float(seg[0],seg[1]); - } - if(current > middleSegment) { // done - at = getReverseArrowTransform(rc, new Line2D.Float(p1,p2),vertexShape); - break; - } - } - return at; + middleSegment = current / 2; + // find the middle segment + current = 0; + for (PathIterator i = path.getPathIterator(null, 1); !i.isDone(); i.next()) { + current++; + int ret = i.currentSegment(seg); + if (ret == PathIterator.SEG_MOVETO) { + p2 = new Point2D.Float(seg[0], seg[1]); + } else if (ret == PathIterator.SEG_LINETO) { + p1 = p2; + p2 = new Point2D.Float(seg[0], seg[1]); + } + if (current > middleSegment) { // done + at = getReverseArrowTransform(rc, new Line2D.Float(p1, p2), vertexShape); + break; + } } + return at; + } - public AffineTransform getArrowTransform(RenderContext rc, Line2D edgeShape, Shape vertexShape) { - - // find the midpoint of the edgeShape line, and use it to make the transform - Line2D left = new Line2D.Float(); - Line2D right = new Line2D.Float(); - this.subdivide(edgeShape, left, right); - edgeShape = right; - float dx = (float) (edgeShape.getX1()-edgeShape.getX2()); - float dy = (float) (edgeShape.getY1()-edgeShape.getY2()); - double atheta = Math.atan2(dx,dy)+Math.PI/2; - AffineTransform at = - AffineTransform.getTranslateInstance(edgeShape.getX1(), edgeShape.getY1()); - at.rotate(-atheta); - return at; - } + public AffineTransform getArrowTransform( + RenderContext rc, Line2D edgeShape, Shape vertexShape) { + + // find the midpoint of the edgeShape line, and use it to make the transform + Line2D left = new Line2D.Float(); + Line2D right = new Line2D.Float(); + this.subdivide(edgeShape, left, right); + edgeShape = right; + float dx = (float) (edgeShape.getX1() - edgeShape.getX2()); + float dy = (float) (edgeShape.getY1() - edgeShape.getY2()); + double atheta = Math.atan2(dx, dy) + Math.PI / 2; + AffineTransform at = AffineTransform.getTranslateInstance(edgeShape.getX1(), edgeShape.getY1()); + at.rotate(-atheta); + return at; + } + + protected AffineTransform getReverseArrowTransform( + RenderContext rc, Line2D edgeShape, Shape vertexShape) { + // find the midpoint of the edgeShape line, and use it to make the transform + Line2D left = new Line2D.Float(); + Line2D right = new Line2D.Float(); + this.subdivide(edgeShape, left, right); + edgeShape = right; + float dx = (float) (edgeShape.getX1() - edgeShape.getX2()); + float dy = (float) (edgeShape.getY1() - edgeShape.getY2()); + // calculate the angle for the arrowhead + double atheta = Math.atan2(dx, dy) - Math.PI / 2; + AffineTransform at = AffineTransform.getTranslateInstance(edgeShape.getX1(), edgeShape.getY1()); + at.rotate(-atheta); + return at; + } + + /** + * divide a Line2D into 2 new Line2Ds that are returned in the passed left and right instances, if + * non-null + * + * @param src the line to divide + * @param left the left side, or null + * @param right the right side, or null + */ + protected void subdivide(Line2D src, Line2D left, Line2D right) { + double x1 = src.getX1(); + double y1 = src.getY1(); + double x2 = src.getX2(); + double y2 = src.getY2(); - protected AffineTransform getReverseArrowTransform(RenderContext rc, - Line2D edgeShape, Shape vertexShape) { - // find the midpoint of the edgeShape line, and use it to make the transform - Line2D left = new Line2D.Float(); - Line2D right = new Line2D.Float(); - this.subdivide(edgeShape, left, right); - edgeShape = right; - float dx = (float) (edgeShape.getX1()-edgeShape.getX2()); - float dy = (float) (edgeShape.getY1()-edgeShape.getY2()); - // calculate the angle for the arrowhead - double atheta = Math.atan2(dx,dy)-Math.PI/2; - AffineTransform at = AffineTransform.getTranslateInstance(edgeShape.getX1(),edgeShape.getY1()); - at.rotate(-atheta); - return at; + double mx = x1 + (x2 - x1) / 2.0; + double my = y1 + (y2 - y1) / 2.0; + if (left != null) { + left.setLine(x1, y1, mx, my); } - - /** - * divide a Line2D into 2 new Line2Ds that are returned - * in the passed left and right instances, if non-null - * @param src the line to divide - * @param left the left side, or null - * @param right the right side, or null - */ - protected void subdivide(Line2D src, - Line2D left, - Line2D right) { - double x1 = src.getX1(); - double y1 = src.getY1(); - double x2 = src.getX2(); - double y2 = src.getY2(); - - double mx = x1 + (x2-x1)/2.0; - double my = y1 + (y2-y1)/2.0; - if (left != null) { - left.setLine(x1, y1, mx, my); - } - if (right != null) { - right.setLine(mx, my, x2, y2); - } + if (right != null) { + right.setLine(mx, my, x2, y2); } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/Checkmark.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/Checkmark.java index a6e1495e..3b30913c 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/Checkmark.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/Checkmark.java @@ -11,53 +11,57 @@ import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.util.Collections; - import javax.swing.Icon; /** - * a simple Icon that draws a checkmark in the lower-right quadrant of its - * area. Used to draw a checkmark on Picked Vertices. + * a simple Icon that draws a checkmark in the lower-right quadrant of its area. Used to draw a + * checkmark on Picked Vertices. + * * @author Tom Nelson */ public class Checkmark implements Icon { - GeneralPath path = new GeneralPath(); - AffineTransform highlight = AffineTransform.getTranslateInstance(-1,-1); - AffineTransform lowlight = AffineTransform.getTranslateInstance(1,1); - AffineTransform shadow = AffineTransform.getTranslateInstance(2,2); - Color color; - public Checkmark() { - this(Color.green); - } - public Checkmark(Color color) { - this.color = color; - path.moveTo(10,17); - path.lineTo(13,20); - path.lineTo(20,13); - } - public void paintIcon(Component c, Graphics g, int x, int y) { - Shape shape = AffineTransform.getTranslateInstance(x, y).createTransformedShape(path); - Graphics2D g2d = (Graphics2D)g; - g2d.addRenderingHints(Collections.singletonMap(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON)); - Stroke stroke = g2d.getStroke(); - g2d.setStroke(new BasicStroke(4)); - g2d.setColor(Color.darkGray); - g2d.draw(shadow.createTransformedShape(shape)); - g2d.setColor(Color.black); - g2d.draw(lowlight.createTransformedShape(shape)); - g2d.setColor(Color.white); - g2d.draw(highlight.createTransformedShape(shape)); - g2d.setColor(color); - g2d.draw(shape); - g2d.setStroke(stroke); - } - - public int getIconWidth() { - return 20; - } - - public int getIconHeight() { - return 20; - } -} \ No newline at end of file + GeneralPath path = new GeneralPath(); + AffineTransform highlight = AffineTransform.getTranslateInstance(-1, -1); + AffineTransform lowlight = AffineTransform.getTranslateInstance(1, 1); + AffineTransform shadow = AffineTransform.getTranslateInstance(2, 2); + Color color; + + public Checkmark() { + this(Color.green); + } + + public Checkmark(Color color) { + this.color = color; + path.moveTo(10, 17); + path.lineTo(13, 20); + path.lineTo(20, 13); + } + + public void paintIcon(Component c, Graphics g, int x, int y) { + Shape shape = AffineTransform.getTranslateInstance(x, y).createTransformedShape(path); + Graphics2D g2d = (Graphics2D) g; + g2d.addRenderingHints( + Collections.singletonMap( + RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); + Stroke stroke = g2d.getStroke(); + g2d.setStroke(new BasicStroke(4)); + g2d.setColor(Color.darkGray); + g2d.draw(shadow.createTransformedShape(shape)); + g2d.setColor(Color.black); + g2d.draw(lowlight.createTransformedShape(shape)); + g2d.setColor(Color.white); + g2d.draw(highlight.createTransformedShape(shape)); + g2d.setColor(color); + g2d.draw(shape); + g2d.setStroke(stroke); + } + + public int getIconWidth() { + return 20; + } + + public int getIconHeight() { + return 20; + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/DefaultEdgeLabelRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/DefaultEdgeLabelRenderer.java index f89c058e..1afbe0e9 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/DefaultEdgeLabelRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/DefaultEdgeLabelRenderer.java @@ -15,214 +15,192 @@ import java.awt.Font; import java.awt.Rectangle; import java.io.Serializable; - import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; /** - * DefaultEdgeLabelRenderer is similar to the cell renderers - * used by the JTable and JTree jfc classes. - * - * @author Tom Nelson + * DefaultEdgeLabelRenderer is similar to the cell renderers used by the JTable and JTree jfc + * classes. * - * + * @author Tom Nelson */ @SuppressWarnings("serial") -public class DefaultEdgeLabelRenderer extends JLabel implements - EdgeLabelRenderer, Serializable { - - protected static Border noFocusBorder = new EmptyBorder(0,0,0,0); - - protected Color pickedEdgeLabelColor = Color.black; - protected boolean rotateEdgeLabels; - - public DefaultEdgeLabelRenderer(Color pickedEdgeLabelColor) { - this(pickedEdgeLabelColor, true); - } - - /** - * Creates an instance with the specified properties. - * - * @param pickedEdgeLabelColor the color to use for rendering the labels of picked edges - * @param rotateEdgeLabels whether the - */ - public DefaultEdgeLabelRenderer(Color pickedEdgeLabelColor, boolean rotateEdgeLabels) { - super(); - this.pickedEdgeLabelColor = pickedEdgeLabelColor; - this.rotateEdgeLabels = rotateEdgeLabels; - setOpaque(true); - setBorder(noFocusBorder); - } +public class DefaultEdgeLabelRenderer extends JLabel implements EdgeLabelRenderer, Serializable { - /** - * @return Returns the rotateEdgeLabels. - */ - public boolean isRotateEdgeLabels() { - return rotateEdgeLabels; - } - /** - * @param rotateEdgeLabels The rotateEdgeLabels to set. - */ - public void setRotateEdgeLabels(boolean rotateEdgeLabels) { - this.rotateEdgeLabels = rotateEdgeLabels; - } - /** - * Overrides JComponent.setForeground to assign - * the unselected-foreground color to the specified color. - * - * @param c set the foreground color to this value - */ - @Override - public void setForeground(Color c) { - super.setForeground(c); - } - - /** - * Overrides JComponent.setBackground to assign - * the unselected-background color to the specified color. - * - * @param c set the background color to this value - */ - @Override - public void setBackground(Color c) { - super.setBackground(c); - } + protected static Border noFocusBorder = new EmptyBorder(0, 0, 0, 0); - /** - * Notification from the UIManager that the look and feel - * has changed. - * Replaces the current UI object with the latest version from the - * UIManager. - * - * @see JComponent#updateUI - */ - @Override - public void updateUI() { - super.updateUI(); - setForeground(null); - setBackground(null); - } - - /** - * - * Returns the default label renderer for an Edge - * - * @param vv the VisualizationViewer to render on - * @param value the value to assign to the label for - * Edge - * @param edge the Edge - * @return the default label renderer - */ - public Component getEdgeLabelRendererComponent(JComponent vv, Object value, - Font font, boolean isSelected, E edge) { - - super.setForeground(vv.getForeground()); - if(isSelected) setForeground(pickedEdgeLabelColor); - super.setBackground(vv.getBackground()); - - if(font != null) { - setFont(font); - } else { - setFont(vv.getFont()); - } - setIcon(null); - setBorder(noFocusBorder); - setValue(value); - return this; - } - - /* - * Implementation Note - * The following methods are overridden as a performance measure to - * prune code-paths that are often called in the case of renders - * but which we know are unnecessary. Great care should be taken - * when writing your own renderer to weigh the benefits and - * drawbacks of overriding methods like these. - */ - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public boolean isOpaque() { - Color back = getBackground(); - Component p = getParent(); - if (p != null) { - p = p.getParent(); - } - boolean colorMatch = - (back != null) && (p != null) - && back.equals(p.getBackground()) && p.isOpaque(); - return !colorMatch && super.isOpaque(); + protected Color pickedEdgeLabelColor = Color.black; + protected boolean rotateEdgeLabels; + + public DefaultEdgeLabelRenderer(Color pickedEdgeLabelColor) { + this(pickedEdgeLabelColor, true); + } + + /** + * Creates an instance with the specified properties. + * + * @param pickedEdgeLabelColor the color to use for rendering the labels of picked edges + * @param rotateEdgeLabels whether the + */ + public DefaultEdgeLabelRenderer(Color pickedEdgeLabelColor, boolean rotateEdgeLabels) { + super(); + this.pickedEdgeLabelColor = pickedEdgeLabelColor; + this.rotateEdgeLabels = rotateEdgeLabels; + setOpaque(true); + setBorder(noFocusBorder); + } + + /** @return Returns the rotateEdgeLabels. */ + public boolean isRotateEdgeLabels() { + return rotateEdgeLabels; + } + /** @param rotateEdgeLabels The rotateEdgeLabels to set. */ + public void setRotateEdgeLabels(boolean rotateEdgeLabels) { + this.rotateEdgeLabels = rotateEdgeLabels; + } + /** + * Overrides JComponent.setForeground to assign the unselected-foreground color to + * the specified color. + * + * @param c set the foreground color to this value + */ + @Override + public void setForeground(Color c) { + super.setForeground(c); + } + + /** + * Overrides JComponent.setBackground to assign the unselected-background color to + * the specified color. + * + * @param c set the background color to this value + */ + @Override + public void setBackground(Color c) { + super.setBackground(c); + } + + /** + * Notification from the UIManager that the look and feel has changed. Replaces the + * current UI object with the latest version from the UIManager. + * + * @see JComponent#updateUI + */ + @Override + public void updateUI() { + super.updateUI(); + setForeground(null); + setBackground(null); + } + + /** + * Returns the default label renderer for an Edge + * + * @param vv the VisualizationViewer to render on + * @param value the value to assign to the label for Edge + * @param edge the Edge + * @return the default label renderer + */ + public Component getEdgeLabelRendererComponent( + JComponent vv, Object value, Font font, boolean isSelected, E edge) { + + super.setForeground(vv.getForeground()); + if (isSelected) setForeground(pickedEdgeLabelColor); + super.setBackground(vv.getBackground()); + + if (font != null) { + setFont(font); + } else { + setFont(vv.getFont()); } + setIcon(null); + setBorder(noFocusBorder); + setValue(value); + return this; + } + + /* + * Implementation Note + * The following methods are overridden as a performance measure to + * prune code-paths that are often called in the case of renders + * but which we know are unnecessary. Great care should be taken + * when writing your own renderer to weigh the benefits and + * drawbacks of overriding methods like these. + */ - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void validate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void revalidate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(long tm, int x, int y, int width, int height) {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(Rectangle r) { } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { - // Strings get interned... - if (propertyName=="text") { - super.firePropertyChange(propertyName, oldValue, newValue); - } + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public boolean isOpaque() { + Color back = getBackground(); + Component p = getParent(); + if (p != null) { + p = p.getParent(); } + boolean colorMatch = + (back != null) && (p != null) && back.equals(p.getBackground()) && p.isOpaque(); + return !colorMatch && super.isOpaque(); + } + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void validate() {} - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } - - /** - * Sets the String object for the cell being rendered to - * value. - * - * @param value the string value for this cell; if value is - * null it sets the text value to an empty string - * @see JLabel#setText - * - */ - protected void setValue(Object value) { - setText((value == null) ? "" : value.toString()); + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void revalidate() {} + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void repaint(long tm, int x, int y, int width, int height) {} + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void repaint(Rectangle r) {} + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + // Strings get interned... + if (propertyName == "text") { + super.firePropertyChange(propertyName, oldValue, newValue); } + } + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {} + + /** + * Sets the String object for the cell being rendered to value. + * + * @param value the string value for this cell; if value is null it sets the text + * value to an empty string + * @see JLabel#setText + */ + protected void setValue(Object value) { + setText((value == null) ? "" : value.toString()); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/DefaultVertexLabelRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/DefaultVertexLabelRenderer.java index 6a592d24..27c0036d 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/DefaultVertexLabelRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/DefaultVertexLabelRenderer.java @@ -15,190 +15,175 @@ import java.awt.Font; import java.awt.Rectangle; import java.io.Serializable; - import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; /** - * DefaultVertexLabelRenderer is similar to the cell renderers - * used by the JTable and JTree JFC classes. - * + * DefaultVertexLabelRenderer is similar to the cell renderers used by the JTable and JTree JFC + * classes. + * * @author Tom Nelson */ @SuppressWarnings("serial") -public class DefaultVertexLabelRenderer extends JLabel implements - VertexLabelRenderer, Serializable { - - protected static Border noFocusBorder = new EmptyBorder(0,0,0,0); - - protected Color pickedVertexLabelColor = Color.black; - - /** - * Creates a default table cell renderer. - * - * @param pickedVertexLabelColor the color to use for rendering the labels of picked vertices - */ - public DefaultVertexLabelRenderer(Color pickedVertexLabelColor) { - this.pickedVertexLabelColor = pickedVertexLabelColor; - setOpaque(true); - setBorder(noFocusBorder); - } +public class DefaultVertexLabelRenderer extends JLabel + implements VertexLabelRenderer, Serializable { - /** - * Overrides JComponent.setForeground to assign - * the unselected-foreground color to the specified color. - * - * @param c set the foreground color to this value - */ - @Override - public void setForeground(Color c) { - super.setForeground(c); - } - - /** - * Overrides JComponent.setBackground to assign - * the unselected-background color to the specified color. - * - * @param c set the background color to this value - */ - @Override - public void setBackground(Color c) { - super.setBackground(c); - } + protected static Border noFocusBorder = new EmptyBorder(0, 0, 0, 0); - /** - * Notification from the UIManager that the look and feel - * has changed. - * Replaces the current UI object with the latest version from the - * UIManager. - * - * @see JComponent#updateUI - */ - @Override - public void updateUI() { - super.updateUI(); - setForeground(null); - setBackground(null); - } - - /** - * - * Returns the default label renderer for a Vertex - * - * @param vv the VisualizationViewer to render on - * @param value the value to assign to the label for - * Vertex - * @param vertex the Vertex - * @return the default label renderer - */ - public Component getVertexLabelRendererComponent(JComponent vv, Object value, - Font font, boolean isSelected, V vertex) { - - super.setForeground(vv.getForeground()); - if(isSelected) setForeground(pickedVertexLabelColor); - super.setBackground(vv.getBackground()); - if(font != null) { - setFont(font); - } else { - setFont(vv.getFont()); - } - setIcon(null); - setBorder(noFocusBorder); - setValue(value); - return this; - } - - /* - * The following methods are overridden as a performance measure to - * to prune code-paths are often called in the case of renders - * but which we know are unnecessary. Great care should be taken - * when writing your own renderer to weigh the benefits and - * drawbacks of overriding methods like these. - */ - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public boolean isOpaque() { - Color back = getBackground(); - Component p = getParent(); - if (p != null) { - p = p.getParent(); - } - boolean colorMatch = (back != null) && (p != null) && - back.equals(p.getBackground()) && - p.isOpaque(); - return !colorMatch && super.isOpaque(); + protected Color pickedVertexLabelColor = Color.black; + + /** + * Creates a default table cell renderer. + * + * @param pickedVertexLabelColor the color to use for rendering the labels of picked vertices + */ + public DefaultVertexLabelRenderer(Color pickedVertexLabelColor) { + this.pickedVertexLabelColor = pickedVertexLabelColor; + setOpaque(true); + setBorder(noFocusBorder); + } + + /** + * Overrides JComponent.setForeground to assign the unselected-foreground color to + * the specified color. + * + * @param c set the foreground color to this value + */ + @Override + public void setForeground(Color c) { + super.setForeground(c); + } + + /** + * Overrides JComponent.setBackground to assign the unselected-background color to + * the specified color. + * + * @param c set the background color to this value + */ + @Override + public void setBackground(Color c) { + super.setBackground(c); + } + + /** + * Notification from the UIManager that the look and feel has changed. Replaces the + * current UI object with the latest version from the UIManager. + * + * @see JComponent#updateUI + */ + @Override + public void updateUI() { + super.updateUI(); + setForeground(null); + setBackground(null); + } + + /** + * Returns the default label renderer for a Vertex + * + * @param vv the VisualizationViewer to render on + * @param value the value to assign to the label for Vertex + * @param vertex the Vertex + * @return the default label renderer + */ + public Component getVertexLabelRendererComponent( + JComponent vv, Object value, Font font, boolean isSelected, V vertex) { + + super.setForeground(vv.getForeground()); + if (isSelected) setForeground(pickedVertexLabelColor); + super.setBackground(vv.getBackground()); + if (font != null) { + setFont(font); + } else { + setFont(vv.getFont()); } + setIcon(null); + setBorder(noFocusBorder); + setValue(value); + return this; + } - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void validate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void revalidate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(long tm, int x, int y, int width, int height) {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(Rectangle r) { } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { - // Strings get interned... - if (propertyName=="text") { - super.firePropertyChange(propertyName, oldValue, newValue); - } + /* + * The following methods are overridden as a performance measure to + * to prune code-paths are often called in the case of renders + * but which we know are unnecessary. Great care should be taken + * when writing your own renderer to weigh the benefits and + * drawbacks of overriding methods like these. + */ + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public boolean isOpaque() { + Color back = getBackground(); + Component p = getParent(); + if (p != null) { + p = p.getParent(); } + boolean colorMatch = + (back != null) && (p != null) && back.equals(p.getBackground()) && p.isOpaque(); + return !colorMatch && super.isOpaque(); + } + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void validate() {} - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } - - /** - * Sets the String object for the cell being rendered to - * value. - * - * @param value the string value for this cell; if value is - * null it sets the text value to an empty string - * @see JLabel#setText - * - */ - protected void setValue(Object value) { - setText((value == null) ? "" : value.toString()); + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void revalidate() {} + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void repaint(long tm, int x, int y, int width, int height) {} + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void repaint(Rectangle r) {} + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + // Strings get interned... + if (propertyName == "text") { + super.firePropertyChange(propertyName, oldValue, newValue); } + } + + /** + * Overridden for performance reasons. See the Implementation Note for + * more information. + */ + @Override + public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {} + + /** + * Sets the String object for the cell being rendered to value. + * + * @param value the string value for this cell; if value is null it sets the text + * value to an empty string + * @see JLabel#setText + */ + protected void setValue(Object value) { + setText((value == null) ? "" : value.toString()); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/EdgeArrowRenderingSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/EdgeArrowRenderingSupport.java index 5990d995..614f9def 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/EdgeArrowRenderingSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/EdgeArrowRenderingSupport.java @@ -1,63 +1,59 @@ package edu.uci.ics.jung.visualization.renderers; +import edu.uci.ics.jung.visualization.RenderContext; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; -import edu.uci.ics.jung.visualization.RenderContext; - public interface EdgeArrowRenderingSupport { - /** - * Returns a transform to position the arrowhead on this edge shape at the - * point where it intersects the passed vertex shape. - * - * @param rc the rendering context used for rendering the arrow - * @param edgeShape the shape used to draw the edge - * @param vertexShape the shape used to draw the vertex - * @return a transform used for positioning the arrowhead for this vertex and edge - */ - AffineTransform getArrowTransform(RenderContext rc, - Shape edgeShape, Shape vertexShape); + /** + * Returns a transform to position the arrowhead on this edge shape at the point where it + * intersects the passed vertex shape. + * + * @param rc the rendering context used for rendering the arrow + * @param edgeShape the shape used to draw the edge + * @param vertexShape the shape used to draw the vertex + * @return a transform used for positioning the arrowhead for this vertex and edge + */ + AffineTransform getArrowTransform(RenderContext rc, Shape edgeShape, Shape vertexShape); - /** - * Returns a transform to position the arrowhead on this edge shape at the - * point where it intersects the passed vertex shape. - * - * @param rc the rendering context used for rendering the arrow - * @param edgeShape the shape used to draw the edge - * @param vertexShape the shape used to draw the vertex - * @return a transform used for positioning the arrowhead for this vertex and edge - */ - AffineTransform getReverseArrowTransform( - RenderContext rc, Shape edgeShape, Shape vertexShape); + /** + * Returns a transform to position the arrowhead on this edge shape at the point where it + * intersects the passed vertex shape. + * + * @param rc the rendering context used for rendering the arrow + * @param edgeShape the shape used to draw the edge + * @param vertexShape the shape used to draw the vertex + * @return a transform used for positioning the arrowhead for this vertex and edge + */ + AffineTransform getReverseArrowTransform( + RenderContext rc, Shape edgeShape, Shape vertexShape); - /** - * Returns a transform to position the arrowhead on this edge shape at the - * point where it intersects the passed vertex shape. - * - *

            The Loop edge is a special case because its starting point is not inside - * the vertex. The passedGo flag handles this case. - * - * @param rc the rendering context used for rendering the arrow - * @param edgeShape the shape used to draw the edge - * @param vertexShape the shape used to draw the vertex - * @param passedGo used for rendering loop edges - * @return a transform used for positioning the arrowhead for this vertex and edge - */ - AffineTransform getReverseArrowTransform( - RenderContext rc, Shape edgeShape, Shape vertexShape, - boolean passedGo); + /** + * Returns a transform to position the arrowhead on this edge shape at the point where it + * intersects the passed vertex shape. + * + *

            The Loop edge is a special case because its starting point is not inside the vertex. The + * passedGo flag handles this case. + * + * @param rc the rendering context used for rendering the arrow + * @param edgeShape the shape used to draw the edge + * @param vertexShape the shape used to draw the vertex + * @param passedGo used for rendering loop edges + * @return a transform used for positioning the arrowhead for this vertex and edge + */ + AffineTransform getReverseArrowTransform( + RenderContext rc, Shape edgeShape, Shape vertexShape, boolean passedGo); - /** - * Returns a transform to position the arrowhead on this edge shape at the - * point where it intersects the passed vertex shape. - * - * @param rc the rendering context used for rendering the arrow - * @param edgeShape the shape used to draw the edge - * @param vertexShape the shape used to draw the vertex - * @return a transform used for positioning the arrowhead for this vertex and edge - */ - AffineTransform getArrowTransform(RenderContext rc, - Line2D edgeShape, Shape vertexShape); -} \ No newline at end of file + /** + * Returns a transform to position the arrowhead on this edge shape at the point where it + * intersects the passed vertex shape. + * + * @param rc the rendering context used for rendering the arrow + * @param edgeShape the shape used to draw the edge + * @param vertexShape the shape used to draw the vertex + * @return a transform used for positioning the arrowhead for this vertex and edge + */ + AffineTransform getArrowTransform(RenderContext rc, Line2D edgeShape, Shape vertexShape); +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/EdgeLabelRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/EdgeLabelRenderer.java index 13ab7761..b7b6aaa9 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/EdgeLabelRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/EdgeLabelRenderer.java @@ -12,34 +12,28 @@ import java.awt.Component; import java.awt.Font; - import javax.swing.JComponent; -/** - * @author Tom Nelson - * - * - */ +/** @author Tom Nelson */ public interface EdgeLabelRenderer { - /** - * Returns the component used for drawing the label. This method is - * used to configure the renderer appropriately before drawing. - * - * @param component the component that is asking the renderer to draw - * @param value the value of the cell to be rendered; the details of how to - * render the value are up to the renderer implementation. For example, - * if {@code value} is the string "true", it could be rendered as the - * string or as a checked checkbox. - * @param font the font to use in rendering the label - * @param isSelected whether the edge is currently selected - * @param edge the edge whose label is being drawn - * @param the edge type - * @return the component used for drawing the label - */ - Component getEdgeLabelRendererComponent(JComponent component, - Object value, Font font, boolean isSelected, E edge); - - boolean isRotateEdgeLabels(); - - void setRotateEdgeLabels(boolean state); + /** + * Returns the component used for drawing the label. This method is used to configure the renderer + * appropriately before drawing. + * + * @param component the component that is asking the renderer to draw + * @param value the value of the cell to be rendered; the details of how to render the value are + * up to the renderer implementation. For example, if {@code value} is the string "true", it + * could be rendered as the string or as a checked checkbox. + * @param font the font to use in rendering the label + * @param isSelected whether the edge is currently selected + * @param edge the edge whose label is being drawn + * @param the edge type + * @return the component used for drawing the label + */ + Component getEdgeLabelRendererComponent( + JComponent component, Object value, Font font, boolean isSelected, E edge); + + boolean isRotateEdgeLabels(); + + void setRotateEdgeLabels(boolean state); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/GradientVertexRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/GradientVertexRenderer.java index 7e2f0af1..245dac71 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/GradientVertexRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/GradientVertexRenderer.java @@ -9,6 +9,12 @@ */ package edu.uci.ics.jung.visualization.renderers; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.VisualizationServer; +import edu.uci.ics.jung.visualization.picking.PickedState; +import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; import java.awt.Color; import java.awt.Dimension; import java.awt.GradientPaint; @@ -18,128 +24,139 @@ import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; - import javax.swing.JComponent; -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.VisualizationServer; -import edu.uci.ics.jung.visualization.picking.PickedState; -import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; - /** * A renderer that will fill vertex shapes with a GradientPaint - * @author Tom Nelson * + * @author Tom Nelson * @param the vertex type * @param the edge type */ public class GradientVertexRenderer implements Renderer.Vertex { - - Color colorOne; - Color colorTwo; - Color pickedColorOne; - Color pickedColorTwo; - PickedState pickedState; - boolean cyclic; - protected final Layout layout; - protected final RenderContext renderContext; - - public GradientVertexRenderer(VisualizationServer vv, - Color colorOne, Color colorTwo, boolean cyclic) { - this.colorOne = colorOne; - this.colorTwo = colorTwo; - this.cyclic = cyclic; - this.layout = vv.getGraphLayout(); - this.renderContext = vv.getRenderContext(); - } + Color colorOne; + Color colorTwo; + Color pickedColorOne; + Color pickedColorTwo; + PickedState pickedState; + boolean cyclic; + protected final Layout layout; + protected final RenderContext renderContext; + + public GradientVertexRenderer( + VisualizationServer vv, Color colorOne, Color colorTwo, boolean cyclic) { + this.colorOne = colorOne; + this.colorTwo = colorTwo; + this.cyclic = cyclic; + this.layout = vv.getGraphLayout(); + this.renderContext = vv.getRenderContext(); + } + + public GradientVertexRenderer( + VisualizationServer vv, + Color colorOne, + Color colorTwo, + Color pickedColorOne, + Color pickedColorTwo, + boolean cyclic) { + this.colorOne = colorOne; + this.colorTwo = colorTwo; + this.pickedColorOne = pickedColorOne; + this.pickedColorTwo = pickedColorTwo; + this.pickedState = vv.getPickedVertexState(); + this.cyclic = cyclic; + this.layout = vv.getGraphLayout(); + this.renderContext = vv.getRenderContext(); + } - public GradientVertexRenderer(VisualizationServer vv, Color colorOne, Color colorTwo, - Color pickedColorOne, Color pickedColorTwo, boolean cyclic) { - this.colorOne = colorOne; - this.colorTwo = colorTwo; - this.pickedColorOne = pickedColorOne; - this.pickedColorTwo = pickedColorTwo; - this.pickedState = vv.getPickedVertexState(); - this.cyclic = cyclic; - this.layout = vv.getGraphLayout(); - this.renderContext = vv.getRenderContext(); - } + public void paintVertex(V v) { + if (renderContext.getVertexIncludePredicate().apply(v)) { + boolean vertexHit = true; + // get the shape to be rendered + Shape shape = renderContext.getVertexShapeTransformer().apply(v); + Point2D p = layout.apply(v); + p = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p); - public void paintVertex(V v) { - if (renderContext.getVertexIncludePredicate().apply(v)) { - boolean vertexHit = true; - // get the shape to be rendered - Shape shape = renderContext.getVertexShapeTransformer().apply(v); - - Point2D p = layout.apply(v); - p = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p); + float x = (float) p.getX(); + float y = (float) p.getY(); - float x = (float)p.getX(); - float y = (float)p.getY(); + // create a transform that translates to the location of + // the vertex to be rendered + AffineTransform xform = AffineTransform.getTranslateInstance(x, y); + // transform the vertex shape with xtransform + shape = xform.createTransformedShape(shape); - // create a transform that translates to the location of - // the vertex to be rendered - AffineTransform xform = AffineTransform.getTranslateInstance(x,y); - // transform the vertex shape with xtransform - shape = xform.createTransformedShape(shape); - - vertexHit = vertexHit(shape); + vertexHit = vertexHit(shape); - if (vertexHit) { - paintShapeForVertex(v, shape); - } - } + if (vertexHit) { + paintShapeForVertex(v, shape); + } } - - protected boolean vertexHit(Shape s) { - JComponent vv = renderContext.getScreenDevice(); - Rectangle deviceRectangle = null; - if(vv != null) { - Dimension d = vv.getSize(); - deviceRectangle = new Rectangle( - 0,0, - d.width,d.height); - } - return renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW).transform(s).intersects(deviceRectangle); + } + + protected boolean vertexHit(Shape s) { + JComponent vv = renderContext.getScreenDevice(); + Rectangle deviceRectangle = null; + if (vv != null) { + Dimension d = vv.getSize(); + deviceRectangle = new Rectangle(0, 0, d.width, d.height); + } + return renderContext + .getMultiLayerTransformer() + .getTransformer(Layer.VIEW) + .transform(s) + .intersects(deviceRectangle); + } + + protected void paintShapeForVertex(V v, Shape shape) { + GraphicsDecorator g = renderContext.getGraphicsContext(); + Paint oldPaint = g.getPaint(); + Rectangle r = shape.getBounds(); + float y2 = (float) r.getMaxY(); + if (cyclic) { + y2 = (float) (r.getMinY() + r.getHeight() / 2); } - protected void paintShapeForVertex(V v, Shape shape) { - GraphicsDecorator g = renderContext.getGraphicsContext(); - Paint oldPaint = g.getPaint(); - Rectangle r = shape.getBounds(); - float y2 = (float)r.getMaxY(); - if(cyclic) { - y2 = (float)(r.getMinY()+r.getHeight()/2); - } - - Paint fillPaint = null; - if(pickedState != null && pickedState.isPicked(v)) { - fillPaint = new GradientPaint((float)r.getMinX(), (float)r.getMinY(), pickedColorOne, - (float)r.getMinX(), y2, pickedColorTwo, cyclic); - } else { - fillPaint = new GradientPaint((float)r.getMinX(), (float)r.getMinY(), colorOne, - (float)r.getMinX(), y2, colorTwo, cyclic); - } - if(fillPaint != null) { - g.setPaint(fillPaint); - g.fill(shape); - g.setPaint(oldPaint); - } - Paint drawPaint = renderContext.getVertexDrawPaintTransformer().apply(v); - if(drawPaint != null) { - g.setPaint(drawPaint); - } - Stroke oldStroke = g.getStroke(); - Stroke stroke = renderContext.getVertexStrokeTransformer().apply(v); - if(stroke != null) { - g.setStroke(stroke); - } - g.draw(shape); - g.setPaint(oldPaint); - g.setStroke(oldStroke); + Paint fillPaint = null; + if (pickedState != null && pickedState.isPicked(v)) { + fillPaint = + new GradientPaint( + (float) r.getMinX(), + (float) r.getMinY(), + pickedColorOne, + (float) r.getMinX(), + y2, + pickedColorTwo, + cyclic); + } else { + fillPaint = + new GradientPaint( + (float) r.getMinX(), + (float) r.getMinY(), + colorOne, + (float) r.getMinX(), + y2, + colorTwo, + cyclic); + } + if (fillPaint != null) { + g.setPaint(fillPaint); + g.fill(shape); + g.setPaint(oldPaint); + } + Paint drawPaint = renderContext.getVertexDrawPaintTransformer().apply(v); + if (drawPaint != null) { + g.setPaint(drawPaint); + } + Stroke oldStroke = g.getStroke(); + Stroke stroke = renderContext.getVertexStrokeTransformer().apply(v); + if (stroke != null) { + g.setStroke(stroke); } + g.draw(shape); + g.setPaint(oldPaint); + g.setStroke(oldStroke); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/Renderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/Renderer.java index 56908c69..b69ee938 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/Renderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/Renderer.java @@ -1,89 +1,133 @@ /* -* Copyright (c) 2003, The JUNG Authors -* -* All rights reserved. -* -* This software is open-source under the BSD license; see either -* "license.txt" or -* https://github.com/jrtom/jung/blob/master/LICENSE for a description. -*/ + * Copyright (c) 2003, The JUNG Authors + * + * All rights reserved. + * + * This software is open-source under the BSD license; see either + * "license.txt" or + * https://github.com/jrtom/jung/blob/master/LICENSE for a description. + */ package edu.uci.ics.jung.visualization.renderers; import java.awt.Dimension; /** - * The interface for drawing vertices, edges, and their labels. - * Implementations of this class can set specific renderers for - * each element, allowing custom control of each. + * The interface for drawing vertices, edges, and their labels. Implementations of this class can + * set specific renderers for each element, allowing custom control of each. */ -public interface Renderer { - - void render(); - void renderVertex(V v); - void renderVertexLabel(V v); - void renderEdge(E e); - void renderEdgeLabel(E e); - void setVertexRenderer(Renderer.Vertex r); - void setEdgeRenderer(Renderer.Edge r); - void setVertexLabelRenderer(Renderer.VertexLabel r); - void setEdgeLabelRenderer(Renderer.EdgeLabel r); - Renderer.VertexLabel getVertexLabelRenderer(); - Renderer.Vertex getVertexRenderer(); - Renderer.Edge getEdgeRenderer(); - Renderer.EdgeLabel getEdgeLabelRenderer(); - - interface Vertex { - void paintVertex(V v); - @SuppressWarnings("rawtypes") - class NOOP implements Vertex { - public void paintVertex(Object v) {} - }; - } - - interface Edge { - void paintEdge(E e); - EdgeArrowRenderingSupport getEdgeArrowRenderingSupport(); - void setEdgeArrowRenderingSupport(EdgeArrowRenderingSupport edgeArrowRenderingSupport); - @SuppressWarnings("rawtypes") - class NOOP implements Edge { - public void paintEdge(Object e) {} - public EdgeArrowRenderingSupport getEdgeArrowRenderingSupport(){return null;} - public void setEdgeArrowRenderingSupport(EdgeArrowRenderingSupport edgeArrowRenderingSupport){} - } - } - - interface VertexLabel { - void labelVertex(V v, String label); - Position getPosition(); - void setPosition(Position position); - void setPositioner(Positioner positioner); - Positioner getPositioner(); - @SuppressWarnings("rawtypes") - class NOOP implements VertexLabel { - public void labelVertex(Object v, String label) {} - public Position getPosition() { return Position.CNTR; } - public void setPosition(Position position) {} - public Positioner getPositioner() { - return new Positioner() { - public Position getPosition(float x, float y, Dimension d) { - return Position.CNTR; - }}; - } - public void setPositioner(Positioner positioner) { - } - } - enum Position { N, NE, E, SE, S, SW, W, NW, CNTR, AUTO } - interface Positioner { - Position getPosition(float x, float y, Dimension d); - } - - } - - interface EdgeLabel { - void labelEdge(E e, String label); - @SuppressWarnings("rawtypes") - class NOOP implements EdgeLabel { - public void labelEdge(Object e, String label) {} - } - } +public interface Renderer { + + void render(); + + void renderVertex(V v); + + void renderVertexLabel(V v); + + void renderEdge(E e); + + void renderEdgeLabel(E e); + + void setVertexRenderer(Renderer.Vertex r); + + void setEdgeRenderer(Renderer.Edge r); + + void setVertexLabelRenderer(Renderer.VertexLabel r); + + void setEdgeLabelRenderer(Renderer.EdgeLabel r); + + Renderer.VertexLabel getVertexLabelRenderer(); + + Renderer.Vertex getVertexRenderer(); + + Renderer.Edge getEdgeRenderer(); + + Renderer.EdgeLabel getEdgeLabelRenderer(); + + interface Vertex { + void paintVertex(V v); + + @SuppressWarnings("rawtypes") + class NOOP implements Vertex { + public void paintVertex(Object v) {} + }; + } + + interface Edge { + void paintEdge(E e); + + EdgeArrowRenderingSupport getEdgeArrowRenderingSupport(); + + void setEdgeArrowRenderingSupport(EdgeArrowRenderingSupport edgeArrowRenderingSupport); + + @SuppressWarnings("rawtypes") + class NOOP implements Edge { + public void paintEdge(Object e) {} + + public EdgeArrowRenderingSupport getEdgeArrowRenderingSupport() { + return null; + } + + public void setEdgeArrowRenderingSupport( + EdgeArrowRenderingSupport edgeArrowRenderingSupport) {} + } + } + + interface VertexLabel { + void labelVertex(V v, String label); + + Position getPosition(); + + void setPosition(Position position); + + void setPositioner(Positioner positioner); + + Positioner getPositioner(); + + @SuppressWarnings("rawtypes") + class NOOP implements VertexLabel { + public void labelVertex(Object v, String label) {} + + public Position getPosition() { + return Position.CNTR; + } + + public void setPosition(Position position) {} + + public Positioner getPositioner() { + return new Positioner() { + public Position getPosition(float x, float y, Dimension d) { + return Position.CNTR; + } + }; + } + + public void setPositioner(Positioner positioner) {} + } + + enum Position { + N, + NE, + E, + SE, + S, + SW, + W, + NW, + CNTR, + AUTO + } + + interface Positioner { + Position getPosition(float x, float y, Dimension d); + } + } + + interface EdgeLabel { + void labelEdge(E e, String label); + + @SuppressWarnings("rawtypes") + class NOOP implements EdgeLabel { + public void labelEdge(Object e, String label) {} + } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/ReshapingEdgeRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/ReshapingEdgeRenderer.java index fcaec748..85678654 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/ReshapingEdgeRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/ReshapingEdgeRenderer.java @@ -9,6 +9,14 @@ */ package edu.uci.ics.jung.visualization.renderers; +import com.google.common.graph.EndpointPair; +import com.google.common.graph.Network; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.transform.LensTransformer; +import edu.uci.ics.jung.visualization.transform.MutableTransformer; +import edu.uci.ics.jung.visualization.transform.shape.TransformingGraphics; import java.awt.Dimension; import java.awt.Paint; import java.awt.Rectangle; @@ -18,395 +26,393 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RectangularShape; - import javax.swing.JComponent; -import com.google.common.graph.EndpointPair; -import com.google.common.graph.Network; - -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.transform.LensTransformer; -import edu.uci.ics.jung.visualization.transform.MutableTransformer; -import edu.uci.ics.jung.visualization.transform.shape.TransformingGraphics; - /** - * uses a flatness argument to break edges into - * smaller segments. This produces a more detailed + * uses a flatness argument to break edges into smaller segments. This produces a more detailed * transformation of the edge shape - * - * @author Tom Nelson - tomnelson@dev.java.net * + * @author Tom Nelson - tomnelson@dev.java.net * @param the vertex type * @param the edge type */ -public class ReshapingEdgeRenderer extends BasicEdgeRenderer - implements Renderer.Edge { - - public ReshapingEdgeRenderer(Layout layout, RenderContext rc) { - super(layout, rc); - } - - /** - * Draws the edge e, whose endpoints are at (x1,y1) - * and (x2,y2), on the graphics context g. - * The Shape provided by the EdgeShapeFunction instance - * is scaled in the x-direction so that its width is equal to the distance between - * (x1,y1) and (x2,y2). - */ - protected void drawSimpleEdge(E e) { - - TransformingGraphics g = (TransformingGraphics)renderContext.getGraphicsContext(); - Network graph = renderContext.getNetwork(); - EndpointPair endpoints = graph.incidentNodes(e); - V v1 = endpoints.nodeU(); - V v2 = endpoints.nodeV(); - Point2D p1 = layout.apply(v1); - Point2D p2 = layout.apply(v2); - p1 = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p1); - p2 = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p2); - float x1 = (float) p1.getX(); - float y1 = (float) p1.getY(); - float x2 = (float) p2.getX(); - float y2 = (float) p2.getY(); - - float flatness = 0; - MutableTransformer transformer = renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW); - if(transformer instanceof LensTransformer) { - LensTransformer ht = (LensTransformer)transformer; - RectangularShape lensShape = ht.getLensShape(); - if(lensShape.contains(x1,y1) || lensShape.contains(x2,y2)) { - flatness = .05f; - } - } +public class ReshapingEdgeRenderer extends BasicEdgeRenderer + implements Renderer.Edge { - boolean isLoop = v1.equals(v2); - Shape s2 = renderContext.getVertexShapeTransformer().apply(v2); - Shape edgeShape = renderContext.getEdgeShapeTransformer().apply(e); - - boolean edgeHit = true; - boolean arrowHit = true; - Rectangle deviceRectangle = null; - JComponent vv = renderContext.getScreenDevice(); - if(vv != null) { - Dimension d = vv.getSize(); - deviceRectangle = new Rectangle(0,0,d.width,d.height); - } + public ReshapingEdgeRenderer(Layout layout, RenderContext rc) { + super(layout, rc); + } - AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); - - if(isLoop) { - // this is a self-loop. scale it is larger than the vertex - // it decorates and translate it so that its nadir is - // at the center of the vertex. - Rectangle2D s2Bounds = s2.getBounds2D(); - xform.scale(s2Bounds.getWidth(),s2Bounds.getHeight()); - xform.translate(0, -edgeShape.getBounds2D().getWidth()/2); - } else { - // this is a normal edge. Rotate it to the angle between - // vertex endpoints, then scale it to the distance between - // the vertices - float dx = x2-x1; - float dy = y2-y1; - float thetaRadians = (float) Math.atan2(dy, dx); - xform.rotate(thetaRadians); - float dist = (float) Math.sqrt(dx*dx + dy*dy); - xform.scale(dist, 1.0); - } - - edgeShape = xform.createTransformedShape(edgeShape); - - MutableTransformer vt = renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW); - if(vt instanceof LensTransformer) { - vt = ((LensTransformer)vt).getDelegate(); + /** + * Draws the edge e, whose endpoints are at (x1,y1) and (x2,y2) + * , on the graphics context g. The Shape provided by the + * EdgeShapeFunction instance is scaled in the x-direction so that its width is equal to + * the distance between (x1,y1) and (x2,y2). + */ + protected void drawSimpleEdge(E e) { + + TransformingGraphics g = (TransformingGraphics) renderContext.getGraphicsContext(); + Network graph = renderContext.getNetwork(); + EndpointPair endpoints = graph.incidentNodes(e); + V v1 = endpoints.nodeU(); + V v2 = endpoints.nodeV(); + Point2D p1 = layout.apply(v1); + Point2D p2 = layout.apply(v2); + p1 = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p1); + p2 = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p2); + float x1 = (float) p1.getX(); + float y1 = (float) p1.getY(); + float x2 = (float) p2.getX(); + float y2 = (float) p2.getY(); + + float flatness = 0; + MutableTransformer transformer = + renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW); + if (transformer instanceof LensTransformer) { + LensTransformer ht = (LensTransformer) transformer; + RectangularShape lensShape = ht.getLensShape(); + if (lensShape.contains(x1, y1) || lensShape.contains(x2, y2)) { + flatness = .05f; + } + } + + boolean isLoop = v1.equals(v2); + Shape s2 = renderContext.getVertexShapeTransformer().apply(v2); + Shape edgeShape = renderContext.getEdgeShapeTransformer().apply(e); + + boolean edgeHit = true; + boolean arrowHit = true; + Rectangle deviceRectangle = null; + JComponent vv = renderContext.getScreenDevice(); + if (vv != null) { + Dimension d = vv.getSize(); + deviceRectangle = new Rectangle(0, 0, d.width, d.height); + } + + AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); + + if (isLoop) { + // this is a self-loop. scale it is larger than the vertex + // it decorates and translate it so that its nadir is + // at the center of the vertex. + Rectangle2D s2Bounds = s2.getBounds2D(); + xform.scale(s2Bounds.getWidth(), s2Bounds.getHeight()); + xform.translate(0, -edgeShape.getBounds2D().getWidth() / 2); + } else { + // this is a normal edge. Rotate it to the angle between + // vertex endpoints, then scale it to the distance between + // the vertices + float dx = x2 - x1; + float dy = y2 - y1; + float thetaRadians = (float) Math.atan2(dy, dx); + xform.rotate(thetaRadians); + float dist = (float) Math.sqrt(dx * dx + dy * dy); + xform.scale(dist, 1.0); + } + + edgeShape = xform.createTransformedShape(edgeShape); + + MutableTransformer vt = renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW); + if (vt instanceof LensTransformer) { + vt = ((LensTransformer) vt).getDelegate(); + } + edgeHit = vt.transform(edgeShape).intersects(deviceRectangle); + + if (edgeHit == true) { + + Paint oldPaint = g.getPaint(); + + // get Paints for filling and drawing + // (filling is done first so that drawing and label use same Paint) + Paint fill_paint = renderContext.getEdgeFillPaintTransformer().apply(e); + if (fill_paint != null) { + g.setPaint(fill_paint); + g.fill(edgeShape, flatness); + } + Paint draw_paint = renderContext.getEdgeDrawPaintTransformer().apply(e); + if (draw_paint != null) { + g.setPaint(draw_paint); + g.draw(edgeShape, flatness); + } + + float scalex = (float) g.getTransform().getScaleX(); + float scaley = (float) g.getTransform().getScaleY(); + // see if arrows are too small to bother drawing + if (scalex < .3 || scaley < .3) return; + + if (renderContext.renderEdgeArrow()) { + + Shape destVertexShape = renderContext.getVertexShapeTransformer().apply(v2); + + AffineTransform xf = AffineTransform.getTranslateInstance(x2, y2); + destVertexShape = xf.createTransformedShape(destVertexShape); + + arrowHit = + renderContext + .getMultiLayerTransformer() + .getTransformer(Layer.VIEW) + .transform(destVertexShape) + .intersects(deviceRectangle); + if (arrowHit) { + + AffineTransform at = + edgeArrowRenderingSupport.getArrowTransform( + renderContext, new GeneralPath(edgeShape), destVertexShape); + if (at == null) return; + Shape arrow = renderContext.getEdgeArrow(); + arrow = at.createTransformedShape(arrow); + g.setPaint(renderContext.getArrowFillPaintTransformer().apply(e)); + g.fill(arrow); + g.setPaint(renderContext.getArrowDrawPaintTransformer().apply(e)); + g.draw(arrow); } - edgeHit = vt.transform(edgeShape).intersects(deviceRectangle); - - if(edgeHit == true) { - - Paint oldPaint = g.getPaint(); - - // get Paints for filling and drawing - // (filling is done first so that drawing and label use same Paint) - Paint fill_paint = renderContext.getEdgeFillPaintTransformer().apply(e); - if (fill_paint != null) - { - g.setPaint(fill_paint); - g.fill(edgeShape, flatness); - } - Paint draw_paint = renderContext.getEdgeDrawPaintTransformer().apply(e); - if (draw_paint != null) - { - g.setPaint(draw_paint); - g.draw(edgeShape, flatness); - } - - float scalex = (float)g.getTransform().getScaleX(); - float scaley = (float)g.getTransform().getScaleY(); - // see if arrows are too small to bother drawing - if (scalex < .3 || scaley < .3) return; - - if (renderContext.renderEdgeArrow()) { - - Shape destVertexShape = renderContext.getVertexShapeTransformer().apply(v2); - - AffineTransform xf = AffineTransform.getTranslateInstance(x2, y2); - destVertexShape = xf.createTransformedShape(destVertexShape); - - arrowHit = renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW).transform(destVertexShape).intersects(deviceRectangle); - if(arrowHit) { - - AffineTransform at = - edgeArrowRenderingSupport.getArrowTransform(renderContext, new GeneralPath(edgeShape), destVertexShape); - if(at == null) return; - Shape arrow = renderContext.getEdgeArrow(); - arrow = at.createTransformedShape(arrow); - g.setPaint(renderContext.getArrowFillPaintTransformer().apply(e)); - g.fill(arrow); - g.setPaint(renderContext.getArrowDrawPaintTransformer().apply(e)); - g.draw(arrow); - } - if (!graph.isDirected()) { - Shape vertexShape = renderContext.getVertexShapeTransformer().apply(v1); - xf = AffineTransform.getTranslateInstance(x1, y1); - vertexShape = xf.createTransformedShape(vertexShape); - - arrowHit = renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW).transform(vertexShape).intersects(deviceRectangle); - - if(arrowHit) { - AffineTransform at = edgeArrowRenderingSupport.getReverseArrowTransform(renderContext, new GeneralPath(edgeShape), vertexShape, !isLoop); - if(at == null) return; - Shape arrow = renderContext.getEdgeArrow(); - arrow = at.createTransformedShape(arrow); - g.setPaint(renderContext.getArrowFillPaintTransformer().apply(e)); - g.fill(arrow); - g.setPaint(renderContext.getArrowDrawPaintTransformer().apply(e)); - g.draw(arrow); - } - } - } - // use existing paint for text if no draw paint specified - if (draw_paint == null) - g.setPaint(oldPaint); - - // restore old paint - g.setPaint(oldPaint); + if (!graph.isDirected()) { + Shape vertexShape = renderContext.getVertexShapeTransformer().apply(v1); + xf = AffineTransform.getTranslateInstance(x1, y1); + vertexShape = xf.createTransformedShape(vertexShape); + + arrowHit = + renderContext + .getMultiLayerTransformer() + .getTransformer(Layer.VIEW) + .transform(vertexShape) + .intersects(deviceRectangle); + + if (arrowHit) { + AffineTransform at = + edgeArrowRenderingSupport.getReverseArrowTransform( + renderContext, new GeneralPath(edgeShape), vertexShape, !isLoop); + if (at == null) return; + Shape arrow = renderContext.getEdgeArrow(); + arrow = at.createTransformedShape(arrow); + g.setPaint(renderContext.getArrowFillPaintTransformer().apply(e)); + g.fill(arrow); + g.setPaint(renderContext.getArrowDrawPaintTransformer().apply(e)); + g.draw(arrow); + } } + } + // use existing paint for text if no draw paint specified + if (draw_paint == null) g.setPaint(oldPaint); + + // restore old paint + g.setPaint(oldPaint); } - - /** - * Returns a transform to position the arrowhead on this edge shape at the - * point where it intersects the passed vertex shape. - */ -// public AffineTransform getArrowTransform(RenderContext rc, GeneralPath edgeShape, Shape vertexShape) { -// float[] seg = new float[6]; -// Point2D p1=null; -// Point2D p2=null; -// AffineTransform at = new AffineTransform(); -// // when the PathIterator is done, switch to the line-subdivide -// // method to get the arrowhead closer. -// for(PathIterator i=edgeShape.getPathIterator(null,1); !i.isDone(); i.next()) { -// int ret = i.currentSegment(seg); -// if(ret == PathIterator.SEG_MOVETO) { -// p2 = new Point2D.Float(seg[0],seg[1]); -// } else if(ret == PathIterator.SEG_LINETO) { -// p1 = p2; -// p2 = new Point2D.Float(seg[0],seg[1]); -// if(vertexShape.contains(p2)) { -// at = getArrowTransform(rc, new Line2D.Float(p1,p2),vertexShape); -// break; -// } -// } -// } -// return at; -// } - - /** - * Returns a transform to position the arrowhead on this edge shape at the - * point where it intersects the passed vertex shape. - */ -// public AffineTransform getReverseArrowTransform(RenderContext rc, GeneralPath edgeShape, Shape vertexShape) { -// return getReverseArrowTransform(rc, edgeShape, vertexShape, true); -// } - - /** - *

            Returns a transform to position the arrowhead on this edge shape at the - * point where it intersects the passed vertex shape. - * - *

            The Loop edge is a special case because its staring point is not inside - * the vertex. The passedGo flag handles this case. - * - * @param edgeShape - * @param vertexShape - * @param passedGo - used only for Loop edges - */ -// public AffineTransform getReverseArrowTransform(RenderContext rc, GeneralPath edgeShape, Shape vertexShape, -// boolean passedGo) { -// float[] seg = new float[6]; -// Point2D p1=null; -// Point2D p2=null; -// -// AffineTransform at = new AffineTransform(); -// for(PathIterator i=edgeShape.getPathIterator(null,1); !i.isDone(); i.next()) { -// int ret = i.currentSegment(seg); -// if(ret == PathIterator.SEG_MOVETO) { -// p2 = new Point2D.Float(seg[0],seg[1]); -// } else if(ret == PathIterator.SEG_LINETO) { -// p1 = p2; -// p2 = new Point2D.Float(seg[0],seg[1]); -// if(passedGo == false && vertexShape.contains(p2)) { -// passedGo = true; -// } else if(passedGo==true && -// vertexShape.contains(p2)==false) { -// at = getReverseArrowTransform(rc, new Line2D.Float(p1,p2),vertexShape); -// break; -// } -// } -// } -// return at; -// } - - /** - * This is used for the arrow of a directed and for one of the - * arrows for non-directed edges - * Get a transform to place the arrow shape on the passed edge at the - * point where it intersects the passed shape - * @param edgeShape - * @param vertexShape - * @return - */ -// public AffineTransform getArrowTransform(RenderContext rc, Line2D edgeShape, Shape vertexShape) { -// float dx = (float) (edgeShape.getX1()-edgeShape.getX2()); -// float dy = (float) (edgeShape.getY1()-edgeShape.getY2()); -// // iterate over the line until the edge shape will place the -// // arrowhead closer than 'arrowGap' to the vertex shape boundary -// while((dx*dx+dy*dy) > rc.getArrowPlacementTolerance()) { -// try { -// edgeShape = getLastOutsideSegment(edgeShape, vertexShape); -// } catch(IllegalArgumentException e) { -// System.err.println(e.toString()); -// return null; -// } -// dx = (float) (edgeShape.getX1()-edgeShape.getX2()); -// dy = (float) (edgeShape.getY1()-edgeShape.getY2()); -// } -// double atheta = Math.atan2(dx,dy)+Math.PI/2; -// AffineTransform at = -// AffineTransform.getTranslateInstance(edgeShape.getX1(), edgeShape.getY1()); -// at.rotate(-atheta); -// return at; -// } - - /** - * This is used for the reverse-arrow of a non-directed edge - * get a transform to place the arrow shape on the passed edge at the - * point where it intersects the passed shape - * @param edgeShape - * @param vertexShape - * @return - */ -// protected AffineTransform getReverseArrowTransform(RenderContext rc, Line2D edgeShape, Shape vertexShape) { -// float dx = (float) (edgeShape.getX1()-edgeShape.getX2()); -// float dy = (float) (edgeShape.getY1()-edgeShape.getY2()); -// // iterate over the line until the edge shape will place the -// // arrowhead closer than 'arrowGap' to the vertex shape boundary -// while((dx*dx+dy*dy) > rc.getArrowPlacementTolerance()) { -// try { -// edgeShape = getFirstOutsideSegment(edgeShape, vertexShape); -// } catch(IllegalArgumentException e) { -// System.err.println(e.toString()); -// return null; -// } -// dx = (float) (edgeShape.getX1()-edgeShape.getX2()); -// dy = (float) (edgeShape.getY1()-edgeShape.getY2()); -// } -// // calculate the angle for the arrowhead -// double atheta = Math.atan2(dx,dy)-Math.PI/2; -// AffineTransform at = AffineTransform.getTranslateInstance(edgeShape.getX1(),edgeShape.getY1()); -// at.rotate(-atheta); -// return at; -// } - - /** - * Passed Line's point2 must be inside the passed shape or - * an IllegalArgumentException is thrown - * @param line line to subdivide - * @param shape shape to compare with line - * @return a line that intersects the shape boundary - * @throws IllegalArgumentException if the passed line's point1 is not inside the shape - */ -// protected Line2D getLastOutsideSegment(Line2D line, Shape shape) { -// if(shape.contains(line.getP2())==false) { -// String errorString = -// "line end point: "+line.getP2()+" is not contained in shape: "+shape.getBounds2D(); -// throw new IllegalArgumentException(errorString); -// //return null; -// } -// Line2D left = new Line2D.Double(); -// Line2D right = new Line2D.Double(); -// // subdivide the line until its left segment intersects -// // the shape boundary -// do { -// subdivide(line, left, right); -// line = right; -// } while(shape.contains(line.getP1())==false); -// // now that right is completely inside shape, -// // return left, which must be partially outside -// return left; -// } - - /** - * Passed Line's point1 must be inside the passed shape or - * an IllegalArgumentException is thrown - * @param line line to subdivide - * @param shape shape to compare with line - * @return a line that intersects the shape boundary - * @throws IllegalArgumentException if the passed line's point1 is not inside the shape - */ -// protected Line2D getFirstOutsideSegment(Line2D line, Shape shape) { -// -// if(shape.contains(line.getP1())==false) { -// String errorString = -// "line start point: "+line.getP1()+" is not contained in shape: "+shape.getBounds2D(); -// throw new IllegalArgumentException(errorString); -// } -// Line2D left = new Line2D.Float(); -// Line2D right = new Line2D.Float(); -// // subdivide the line until its right side intersects the -// // shape boundary -// do { -// subdivide(line, left, right); -// line = left; -// } while(shape.contains(line.getP2())==false); -// // now that left is completely inside shape, -// // return right, which must be partially outside -// return right; -// } - - /** - * divide a Line2D into 2 new Line2Ds that are returned - * in the passed left and right instances, if non-null - * @param src the line to divide - * @param left the left side, or null - * @param right the right side, or null - */ -// protected void subdivide(Line2D src, -// Line2D left, -// Line2D right) { -// double x1 = src.getX1(); -// double y1 = src.getY1(); -// double x2 = src.getX2(); -// double y2 = src.getY2(); -// -// double mx = x1 + (x2-x1)/2.0; -// double my = y1 + (y2-y1)/2.0; -// if (left != null) { -// left.setLine(x1, y1, mx, my); -// } -// if (right != null) { -// right.setLine(mx, my, x2, y2); -// } -// } + } + + /** + * Returns a transform to position the arrowhead on this edge shape at the point where it + * intersects the passed vertex shape. + */ + // public AffineTransform getArrowTransform(RenderContext rc, GeneralPath edgeShape, Shape vertexShape) { + // float[] seg = new float[6]; + // Point2D p1=null; + // Point2D p2=null; + // AffineTransform at = new AffineTransform(); + // // when the PathIterator is done, switch to the line-subdivide + // // method to get the arrowhead closer. + // for(PathIterator i=edgeShape.getPathIterator(null,1); !i.isDone(); i.next()) { + // int ret = i.currentSegment(seg); + // if(ret == PathIterator.SEG_MOVETO) { + // p2 = new Point2D.Float(seg[0],seg[1]); + // } else if(ret == PathIterator.SEG_LINETO) { + // p1 = p2; + // p2 = new Point2D.Float(seg[0],seg[1]); + // if(vertexShape.contains(p2)) { + // at = getArrowTransform(rc, new Line2D.Float(p1,p2),vertexShape); + // break; + // } + // } + // } + // return at; + // } + + /** + * Returns a transform to position the arrowhead on this edge shape at the point where it + * intersects the passed vertex shape. + */ + // public AffineTransform getReverseArrowTransform(RenderContext rc, GeneralPath edgeShape, Shape vertexShape) { + // return getReverseArrowTransform(rc, edgeShape, vertexShape, true); + // } + + /** + * Returns a transform to position the arrowhead on this edge shape at the point where it + * intersects the passed vertex shape. + * + *

            The Loop edge is a special case because its staring point is not inside the vertex. The + * passedGo flag handles this case. + * + * @param edgeShape + * @param vertexShape + * @param passedGo - used only for Loop edges + */ + // public AffineTransform getReverseArrowTransform(RenderContext rc, GeneralPath edgeShape, Shape vertexShape, + // boolean passedGo) { + // float[] seg = new float[6]; + // Point2D p1=null; + // Point2D p2=null; + // + // AffineTransform at = new AffineTransform(); + // for(PathIterator i=edgeShape.getPathIterator(null,1); !i.isDone(); i.next()) { + // int ret = i.currentSegment(seg); + // if(ret == PathIterator.SEG_MOVETO) { + // p2 = new Point2D.Float(seg[0],seg[1]); + // } else if(ret == PathIterator.SEG_LINETO) { + // p1 = p2; + // p2 = new Point2D.Float(seg[0],seg[1]); + // if(passedGo == false && vertexShape.contains(p2)) { + // passedGo = true; + // } else if(passedGo==true && + // vertexShape.contains(p2)==false) { + // at = getReverseArrowTransform(rc, new Line2D.Float(p1,p2),vertexShape); + // break; + // } + // } + // } + // return at; + // } + + /** + * This is used for the arrow of a directed and for one of the arrows for non-directed edges Get a + * transform to place the arrow shape on the passed edge at the point where it intersects the + * passed shape + * + * @param edgeShape + * @param vertexShape + * @return + */ + // public AffineTransform getArrowTransform(RenderContext rc, Line2D edgeShape, Shape vertexShape) { + // float dx = (float) (edgeShape.getX1()-edgeShape.getX2()); + // float dy = (float) (edgeShape.getY1()-edgeShape.getY2()); + // // iterate over the line until the edge shape will place the + // // arrowhead closer than 'arrowGap' to the vertex shape boundary + // while((dx*dx+dy*dy) > rc.getArrowPlacementTolerance()) { + // try { + // edgeShape = getLastOutsideSegment(edgeShape, vertexShape); + // } catch(IllegalArgumentException e) { + // System.err.println(e.toString()); + // return null; + // } + // dx = (float) (edgeShape.getX1()-edgeShape.getX2()); + // dy = (float) (edgeShape.getY1()-edgeShape.getY2()); + // } + // double atheta = Math.atan2(dx,dy)+Math.PI/2; + // AffineTransform at = + // AffineTransform.getTranslateInstance(edgeShape.getX1(), edgeShape.getY1()); + // at.rotate(-atheta); + // return at; + // } + + /** + * This is used for the reverse-arrow of a non-directed edge get a transform to place the arrow + * shape on the passed edge at the point where it intersects the passed shape + * + * @param edgeShape + * @param vertexShape + * @return + */ + // protected AffineTransform getReverseArrowTransform(RenderContext rc, Line2D edgeShape, Shape vertexShape) { + // float dx = (float) (edgeShape.getX1()-edgeShape.getX2()); + // float dy = (float) (edgeShape.getY1()-edgeShape.getY2()); + // // iterate over the line until the edge shape will place the + // // arrowhead closer than 'arrowGap' to the vertex shape boundary + // while((dx*dx+dy*dy) > rc.getArrowPlacementTolerance()) { + // try { + // edgeShape = getFirstOutsideSegment(edgeShape, vertexShape); + // } catch(IllegalArgumentException e) { + // System.err.println(e.toString()); + // return null; + // } + // dx = (float) (edgeShape.getX1()-edgeShape.getX2()); + // dy = (float) (edgeShape.getY1()-edgeShape.getY2()); + // } + // // calculate the angle for the arrowhead + // double atheta = Math.atan2(dx,dy)-Math.PI/2; + // AffineTransform at = AffineTransform.getTranslateInstance(edgeShape.getX1(),edgeShape.getY1()); + // at.rotate(-atheta); + // return at; + // } + + /** + * Passed Line's point2 must be inside the passed shape or an IllegalArgumentException is thrown + * + * @param line line to subdivide + * @param shape shape to compare with line + * @return a line that intersects the shape boundary + * @throws IllegalArgumentException if the passed line's point1 is not inside the shape + */ + // protected Line2D getLastOutsideSegment(Line2D line, Shape shape) { + // if(shape.contains(line.getP2())==false) { + // String errorString = + // "line end point: "+line.getP2()+" is not contained in shape: "+shape.getBounds2D(); + // throw new IllegalArgumentException(errorString); + // //return null; + // } + // Line2D left = new Line2D.Double(); + // Line2D right = new Line2D.Double(); + // // subdivide the line until its left segment intersects + // // the shape boundary + // do { + // subdivide(line, left, right); + // line = right; + // } while(shape.contains(line.getP1())==false); + // // now that right is completely inside shape, + // // return left, which must be partially outside + // return left; + // } + + /** + * Passed Line's point1 must be inside the passed shape or an IllegalArgumentException is thrown + * + * @param line line to subdivide + * @param shape shape to compare with line + * @return a line that intersects the shape boundary + * @throws IllegalArgumentException if the passed line's point1 is not inside the shape + */ + // protected Line2D getFirstOutsideSegment(Line2D line, Shape shape) { + // + // if(shape.contains(line.getP1())==false) { + // String errorString = + // "line start point: "+line.getP1()+" is not contained in shape: "+shape.getBounds2D(); + // throw new IllegalArgumentException(errorString); + // } + // Line2D left = new Line2D.Float(); + // Line2D right = new Line2D.Float(); + // // subdivide the line until its right side intersects the + // // shape boundary + // do { + // subdivide(line, left, right); + // line = left; + // } while(shape.contains(line.getP2())==false); + // // now that left is completely inside shape, + // // return right, which must be partially outside + // return right; + // } + + /** + * divide a Line2D into 2 new Line2Ds that are returned in the passed left and right instances, if + * non-null + * + * @param src the line to divide + * @param left the left side, or null + * @param right the right side, or null + */ + // protected void subdivide(Line2D src, + // Line2D left, + // Line2D right) { + // double x1 = src.getX1(); + // double y1 = src.getY1(); + // double x2 = src.getX2(); + // double y2 = src.getY2(); + // + // double mx = x1 + (x2-x1)/2.0; + // double my = y1 + (y2-y1)/2.0; + // if (left != null) { + // left.setLine(x1, y1, mx, my); + // } + // if (right != null) { + // right.setLine(mx, my, x2, y2); + // } + // } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/VertexLabelAsShapeRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/VertexLabelAsShapeRenderer.java index 2694f47b..57245383 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/VertexLabelAsShapeRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/VertexLabelAsShapeRenderer.java @@ -9,6 +9,11 @@ */ package edu.uci.ics.jung.visualization.renderers; +import com.google.common.base.Function; +import edu.uci.ics.jung.algorithms.layout.Layout; +import edu.uci.ics.jung.visualization.Layer; +import edu.uci.ics.jung.visualization.RenderContext; +import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; import java.awt.Component; import java.awt.Dimension; import java.awt.Rectangle; @@ -17,106 +22,122 @@ import java.util.HashMap; import java.util.Map; -import com.google.common.base.Function; - -import edu.uci.ics.jung.algorithms.layout.Layout; -import edu.uci.ics.jung.visualization.Layer; -import edu.uci.ics.jung.visualization.RenderContext; -import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; - /** - * Renders Vertex Labels, but can also supply Shapes for vertices. - * This has the effect of making the vertex label the actual vertex - * shape. The user will probably want to center the vertex label - * on the vertex location. - * - * @author Tom Nelson + * Renders Vertex Labels, but can also supply Shapes for vertices. This has the effect of making the + * vertex label the actual vertex shape. The user will probably want to center the vertex label on + * the vertex location. * + * @author Tom Nelson * @param the vertex type * @param the edge type */ -public class VertexLabelAsShapeRenderer - implements Renderer.VertexLabel, Function { - - protected Map shapes = new HashMap(); - protected final Layout layout; - protected final RenderContext renderContext; - - public VertexLabelAsShapeRenderer(Layout layout, RenderContext rc) { - this.layout = layout; - this.renderContext = rc; - } - - public Component prepareRenderer(RenderContext rc, VertexLabelRenderer graphLabelRenderer, Object value, - boolean isSelected, V vertex) { - return renderContext.getVertexLabelRenderer().getVertexLabelRendererComponent(renderContext.getScreenDevice(), value, - renderContext.getVertexFontTransformer().apply(vertex), isSelected, vertex); - } - - /** - * Labels the specified vertex with the specified label. - * Uses the font specified by this instance's - * VertexFontFunction. (If the font is unspecified, the existing - * font for the graphics context is used.) If vertex label centering - * is active, the label is centered on the position of the vertex; otherwise - * the label is offset slightly. - */ - public void labelVertex(V v, String label) { - if (renderContext.getVertexIncludePredicate().apply(v) == false) { - return; - } - GraphicsDecorator g = renderContext.getGraphicsContext(); - Component component = prepareRenderer(renderContext, renderContext.getVertexLabelRenderer(), label, - renderContext.getPickedVertexState().isPicked(v), v); - Dimension d = component.getPreferredSize(); - - int h_offset = -d.width / 2; - int v_offset = -d.height / 2; - - Point2D p = layout.apply(v); - p = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p); - - int x = (int)p.getX(); - int y = (int)p.getY(); - - g.draw(component, renderContext.getRendererPane(), x+h_offset, y+v_offset, d.width, d.height, true); - - Dimension size = component.getPreferredSize(); - Rectangle bounds = new Rectangle(-size.width/2 -2, -size.height/2 -2, size.width+4, size.height); - shapes.put(v, bounds); +public class VertexLabelAsShapeRenderer implements Renderer.VertexLabel, Function { + + protected Map shapes = new HashMap(); + protected final Layout layout; + protected final RenderContext renderContext; + + public VertexLabelAsShapeRenderer(Layout layout, RenderContext rc) { + this.layout = layout; + this.renderContext = rc; + } + + public Component prepareRenderer( + RenderContext rc, + VertexLabelRenderer graphLabelRenderer, + Object value, + boolean isSelected, + V vertex) { + return renderContext + .getVertexLabelRenderer() + .getVertexLabelRendererComponent( + renderContext.getScreenDevice(), + value, + renderContext.getVertexFontTransformer().apply(vertex), + isSelected, + vertex); + } + + /** + * Labels the specified vertex with the specified label. Uses the font specified by this + * instance's VertexFontFunction. (If the font is unspecified, the existing font for + * the graphics context is used.) If vertex label centering is active, the label is centered on + * the position of the vertex; otherwise the label is offset slightly. + */ + public void labelVertex(V v, String label) { + if (renderContext.getVertexIncludePredicate().apply(v) == false) { + return; } + GraphicsDecorator g = renderContext.getGraphicsContext(); + Component component = + prepareRenderer( + renderContext, + renderContext.getVertexLabelRenderer(), + label, + renderContext.getPickedVertexState().isPicked(v), + v); + Dimension d = component.getPreferredSize(); + + int h_offset = -d.width / 2; + int v_offset = -d.height / 2; + + Point2D p = layout.apply(v); + p = renderContext.getMultiLayerTransformer().transform(Layer.LAYOUT, p); + + int x = (int) p.getX(); + int y = (int) p.getY(); + + g.draw( + component, + renderContext.getRendererPane(), + x + h_offset, + y + v_offset, + d.width, + d.height, + true); + + Dimension size = component.getPreferredSize(); + Rectangle bounds = + new Rectangle(-size.width / 2 - 2, -size.height / 2 - 2, size.width + 4, size.height); + shapes.put(v, bounds); + } + + public Shape apply(V v) { + Component component = + prepareRenderer( + renderContext, + renderContext.getVertexLabelRenderer(), + renderContext.getVertexLabelTransformer().apply(v), + renderContext.getPickedVertexState().isPicked(v), + v); + Dimension size = component.getPreferredSize(); + Rectangle bounds = + new Rectangle(-size.width / 2 - 2, -size.height / 2 - 2, size.width + 4, size.height); + return bounds; + // Shape shape = shapes.get(v); + // if(shape == null) { + // return new Rectangle(-20,-20,40,40); + // } + // else return shape; + } + + public Renderer.VertexLabel.Position getPosition() { + return Renderer.VertexLabel.Position.CNTR; + } + + public Renderer.VertexLabel.Positioner getPositioner() { + return new Positioner() { + public Renderer.VertexLabel.Position getPosition(float x, float y, Dimension d) { + return Renderer.VertexLabel.Position.CNTR; + } + }; + } + + public void setPosition(Renderer.VertexLabel.Position position) { + // noop + } - public Shape apply(V v) { - Component component = prepareRenderer(renderContext, - renderContext.getVertexLabelRenderer(), - renderContext.getVertexLabelTransformer().apply(v), - renderContext.getPickedVertexState().isPicked(v), v); - Dimension size = component.getPreferredSize(); - Rectangle bounds = new Rectangle(-size.width/2 -2, -size.height/2 -2, size.width+4, size.height); - return bounds; -// Shape shape = shapes.get(v); -// if(shape == null) { -// return new Rectangle(-20,-20,40,40); -// } -// else return shape; - } - - public Renderer.VertexLabel.Position getPosition() { - return Renderer.VertexLabel.Position.CNTR; - } - - public Renderer.VertexLabel.Positioner getPositioner() { - return new Positioner() { - public Renderer.VertexLabel.Position getPosition(float x, float y, Dimension d) { - return Renderer.VertexLabel.Position.CNTR; - }}; - } - - public void setPosition(Renderer.VertexLabel.Position position) { - // noop - } - - public void setPositioner(Renderer.VertexLabel.Positioner positioner) { - //noop - } + public void setPositioner(Renderer.VertexLabel.Positioner positioner) { + //noop + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/VertexLabelRenderer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/VertexLabelRenderer.java index ecbf8a23..d856ebef 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/VertexLabelRenderer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/renderers/VertexLabelRenderer.java @@ -12,30 +12,24 @@ import java.awt.Component; import java.awt.Font; - import javax.swing.JComponent; -/** - * @author Tom Nelson - * - * - */ +/** @author Tom Nelson */ public interface VertexLabelRenderer { - /** - * Returns the component used for drawing the label. This method is - * used to configure the renderer appropriately before drawing. - * - * @param vv the component that is asking the renderer to draw - * @param value the value of the cell to be rendered; the details of how to - * render the value are up to the renderer implementation. For example, - * if {@code value} is the string "true", it could be rendered as the - * string or as a checked checkbox. - * @param font the font to use in rendering the label - * @param isSelected whether the vertex is currently selected - * @param vertex the edge whose label is being drawn - * @param the vertex type - * @return the component used for drawing the label - */ - Component getVertexLabelRendererComponent(JComponent vv, Object value, - Font font, boolean isSelected, V vertex); + /** + * Returns the component used for drawing the label. This method is used to configure the renderer + * appropriately before drawing. + * + * @param vv the component that is asking the renderer to draw + * @param value the value of the cell to be rendered; the details of how to render the value are + * up to the renderer implementation. For example, if {@code value} is the string "true", it + * could be rendered as the string or as a checked checkbox. + * @param font the font to use in rendering the label + * @param isSelected whether the vertex is currently selected + * @param vertex the edge whose label is being drawn + * @param the vertex type + * @return the component used for drawing the label + */ + Component getVertexLabelRendererComponent( + JComponent vv, Object value, Font font, boolean isSelected, V vertex); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/subLayout/GraphCollapser.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/subLayout/GraphCollapser.java index a8732e28..da9b712b 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/subLayout/GraphCollapser.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/subLayout/GraphCollapser.java @@ -9,161 +9,158 @@ */ package edu.uci.ics.jung.visualization.subLayout; -import java.util.Collection; -import java.util.Set; -import java.util.logging.Logger; - import com.google.common.base.Preconditions; import com.google.common.graph.EndpointPair; import com.google.common.graph.MutableNetwork; import com.google.common.graph.Network; import com.google.common.graph.NetworkBuilder; - +import java.util.Collection; +import java.util.Set; +import java.util.logging.Logger; @SuppressWarnings({"rawtypes", "unchecked"}) -public class GraphCollapser { - - private static final Logger logger = Logger.getLogger(GraphCollapser.class.getClass().getName()); - private Network originalGraph; - private NetworkBuilder graphBuilder; - - public GraphCollapser(Network originalGraph) { - this.originalGraph = originalGraph; - this.graphBuilder = NetworkBuilder.from(originalGraph); +public class GraphCollapser { + + private static final Logger logger = Logger.getLogger(GraphCollapser.class.getClass().getName()); + private Network originalGraph; + private NetworkBuilder graphBuilder; + + public GraphCollapser(Network originalGraph) { + this.originalGraph = originalGraph; + this.graphBuilder = NetworkBuilder.from(originalGraph); + } + + public Network collapse(Network inGraph, Network clusterGraph) { + + if (clusterGraph.nodes().size() < 2) return inGraph; + + MutableNetwork graph = graphBuilder.build(); + Collection cluster = clusterGraph.nodes(); + + // add all vertices in the delegate, unless the vertex is in the + // cluster. + for (Object v : inGraph.nodes()) { + if (cluster.contains(v) == false) { + graph.addNode(v); + } } - - public Network collapse(Network inGraph, Network clusterGraph) { - - if (clusterGraph.nodes().size() < 2) return inGraph; - - MutableNetwork graph = graphBuilder.build(); - Collection cluster = clusterGraph.nodes(); - - // add all vertices in the delegate, unless the vertex is in the - // cluster. - for(Object v : inGraph.nodes()) { - if(cluster.contains(v) == false) { - graph.addNode(v); - } - } - // add the clusterGraph as a vertex - graph.addNode(clusterGraph); - - // add all edges from the inGraph, unless both endpoints of - // the edge are in the cluster - for(Object e : inGraph.edges()) { - EndpointPair endpoints = inGraph.incidentNodes(e); - Object u = endpoints.nodeU(); - Object v = endpoints.nodeV(); - // only add edges whose endpoints are not both in the cluster - if (cluster.contains(u) && cluster.contains(v)) { - continue; - } - - if (cluster.contains(u)) { - graph.addEdge(clusterGraph, v, e); - } else if (cluster.contains(v)) { - graph.addEdge(u, clusterGraph, e); - } else { - graph.addEdge(u, v, e); - } - } - return graph; + // add the clusterGraph as a vertex + graph.addNode(clusterGraph); + + // add all edges from the inGraph, unless both endpoints of + // the edge are in the cluster + for (Object e : inGraph.edges()) { + EndpointPair endpoints = inGraph.incidentNodes(e); + Object u = endpoints.nodeU(); + Object v = endpoints.nodeV(); + // only add edges whose endpoints are not both in the cluster + if (cluster.contains(u) && cluster.contains(v)) { + continue; + } + + if (cluster.contains(u)) { + graph.addEdge(clusterGraph, v, e); + } else if (cluster.contains(v)) { + graph.addEdge(u, clusterGraph, e); + } else { + graph.addEdge(u, v, e); + } } - - public Network expand(Network inGraph, Network clusterGraph) { - MutableNetwork graph = graphBuilder.build(); - Collection clusterNodes = clusterGraph.nodes(); - logger.fine("cluster to expand is " + clusterNodes); - - // put all clusterGraph vertices and edges into the new Graph - for(Object node : clusterNodes) { - graph.addNode(node); - for(Object edge : clusterGraph.incidentEdges(node)) { - Object u = clusterGraph.incidentNodes(edge).nodeU(); - Object v = clusterGraph.incidentNodes(edge).nodeV(); - graph.addEdge(u, v, edge); - } - } - // add all the vertices from the current graph except for - // the cluster we are expanding - for(Object node : inGraph.nodes()) { - if (node.equals(clusterGraph) == false) { - graph.addNode(node); - } - } + return graph; + } - // now that all vertices have been added, add the edges, - // ensuring that no edge contains a vertex that has not - // already been added - for (Object node : inGraph.nodes()) { - if (node.equals(clusterGraph) == false) { - for (Object edge : inGraph.incidentEdges(node)) { - Object u = inGraph.incidentNodes(edge).nodeU(); - Object v = inGraph.incidentNodes(edge).nodeV(); - // only add edges if both u and v are not already in the graph - if (!(clusterNodes.contains(u) && clusterNodes.contains(v))) { - continue; - } - - if (clusterGraph.equals(u)) { - Object originalU = originalGraph.incidentNodes(edge).nodeU(); - Object newU = findNode(graph, originalU); - Preconditions.checkNotNull(newU); - graph.addEdge(newU, v, edge); - } else if (clusterGraph.equals(v)) { - Object originalV = originalGraph.incidentNodes(edge).nodeV(); - Object newV = findNode(graph, originalV); - Preconditions.checkNotNull(newV); - graph.addEdge(u, newV, edge); - } else { - graph.addEdge(u, v, edge); - } - } - } - } - return graph; + public Network expand(Network inGraph, Network clusterGraph) { + MutableNetwork graph = graphBuilder.build(); + Collection clusterNodes = clusterGraph.nodes(); + logger.fine("cluster to expand is " + clusterNodes); + + // put all clusterGraph vertices and edges into the new Graph + for (Object node : clusterNodes) { + graph.addNode(node); + for (Object edge : clusterGraph.incidentEdges(node)) { + Object u = clusterGraph.incidentNodes(edge).nodeU(); + Object v = clusterGraph.incidentNodes(edge).nodeV(); + graph.addEdge(u, v, edge); + } } - - Object findNode(Network inGraph, Object inNode) { - if (inGraph.nodes().contains(inNode)) { - return inNode; - } - - for (Object node : inGraph.nodes()) { - if ((node instanceof Network) && contains((Network) node, inNode)) { - return node; - } + // add all the vertices from the current graph except for + // the cluster we are expanding + for (Object node : inGraph.nodes()) { + if (node.equals(clusterGraph) == false) { + graph.addNode(node); + } + } + + // now that all vertices have been added, add the edges, + // ensuring that no edge contains a vertex that has not + // already been added + for (Object node : inGraph.nodes()) { + if (node.equals(clusterGraph) == false) { + for (Object edge : inGraph.incidentEdges(node)) { + Object u = inGraph.incidentNodes(edge).nodeU(); + Object v = inGraph.incidentNodes(edge).nodeV(); + // only add edges if both u and v are not already in the graph + if (!(clusterNodes.contains(u) && clusterNodes.contains(v))) { + continue; + } + + if (clusterGraph.equals(u)) { + Object originalU = originalGraph.incidentNodes(edge).nodeU(); + Object newU = findNode(graph, originalU); + Preconditions.checkNotNull(newU); + graph.addEdge(newU, v, edge); + } else if (clusterGraph.equals(v)) { + Object originalV = originalGraph.incidentNodes(edge).nodeV(); + Object newV = findNode(graph, originalV); + Preconditions.checkNotNull(newV); + graph.addEdge(u, newV, edge); + } else { + graph.addEdge(u, v, edge); + } } - return null; + } } - - private boolean contains(Network inGraph, Object inNode) { - boolean contained = false; - if (inGraph.nodes().contains(inNode)) { - return true; - } - - for(Object node : inGraph.nodes()) { - contained |= (node instanceof Network) && contains((Network) node, inNode); - } - return contained; + return graph; + } + + Object findNode(Network inGraph, Object inNode) { + if (inGraph.nodes().contains(inNode)) { + return inNode; } - - public Network getClusterGraph(Network inGraph, Collection picked) { - MutableNetwork clusterGraph = graphBuilder.build(); - for(Object node : picked) { - clusterGraph.addNode(node); - Set edges = inGraph.incidentEdges(node); - for(Object edge : edges) { - Object u = inGraph.incidentNodes(edge).nodeU(); - Object v = inGraph.incidentNodes(edge).nodeV(); - if (picked.contains(u) && picked.contains(v)) { - clusterGraph.addEdge(edge, u, v); - } - } - } - return clusterGraph; + + for (Object node : inGraph.nodes()) { + if ((node instanceof Network) && contains((Network) node, inNode)) { + return node; + } + } + return null; + } + + private boolean contains(Network inGraph, Object inNode) { + boolean contained = false; + if (inGraph.nodes().contains(inNode)) { + return true; } + for (Object node : inGraph.nodes()) { + contained |= (node instanceof Network) && contains((Network) node, inNode); + } + return contained; + } + + public Network getClusterGraph(Network inGraph, Collection picked) { + MutableNetwork clusterGraph = graphBuilder.build(); + for (Object node : picked) { + clusterGraph.addNode(node); + Set edges = inGraph.incidentEdges(node); + for (Object edge : edges) { + Object u = inGraph.incidentNodes(edge).nodeU(); + Object v = inGraph.incidentNodes(edge).nodeV(); + if (picked.contains(u) && picked.contains(v)) { + clusterGraph.addEdge(edge, u, v); + } + } + } + return clusterGraph; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/subLayout/TreeCollapser.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/subLayout/TreeCollapser.java index fb17718c..7dbe993e 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/subLayout/TreeCollapser.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/subLayout/TreeCollapser.java @@ -9,45 +9,46 @@ */ package edu.uci.ics.jung.visualization.subLayout; -import java.util.Optional; - import edu.uci.ics.jung.graph.CTreeNetwork; import edu.uci.ics.jung.graph.MutableCTreeNetwork; import edu.uci.ics.jung.graph.util.TreeUtils; +import java.util.Optional; -public class TreeCollapser { +public class TreeCollapser { - /** - * Replaces the subtree of {@code tree} rooted at {@code subRoot} with a node representing - * that subtree. - * @param tree the tree whose subtree is to be collapsed - * @param subRoot the root of the subtree to be collapsed - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static MutableCTreeNetwork collapse(MutableCTreeNetwork tree, Object subRoot) { - // get the subtree rooted at subRoot - MutableCTreeNetwork subTree = TreeUtils.getSubTree(tree, subRoot); - Optional parent = tree.predecessor(subRoot); - if (parent.isPresent()) { - // subRoot has a parent, so attach its parent to subTree in its place - Object parentEdge = tree.inEdges(subRoot).iterator().next(); // THERE CAN BE ONLY ONE - tree.removeNode(subRoot); - tree.addEdge(parent.get(), subTree, parentEdge); - } else { - // subRoot is the root of tree - tree.removeNode(subRoot); - tree.addNode(subTree); - } - return subTree; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static void expand(MutableCTreeNetwork tree, CTreeNetwork subTree) { - Optional parent = tree.predecessor(subTree); - Object parentEdge = parent.isPresent() - ? tree.inEdges(subTree).iterator().next() // THERE CAN BE ONLY ONE - : null; - tree.removeNode(subTree); - TreeUtils.addSubTree(tree, subTree, parent.orElse(null), parentEdge); + /** + * Replaces the subtree of {@code tree} rooted at {@code subRoot} with a node representing that + * subtree. + * + * @param tree the tree whose subtree is to be collapsed + * @param subRoot the root of the subtree to be collapsed + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static MutableCTreeNetwork collapse(MutableCTreeNetwork tree, Object subRoot) { + // get the subtree rooted at subRoot + MutableCTreeNetwork subTree = TreeUtils.getSubTree(tree, subRoot); + Optional parent = tree.predecessor(subRoot); + if (parent.isPresent()) { + // subRoot has a parent, so attach its parent to subTree in its place + Object parentEdge = tree.inEdges(subRoot).iterator().next(); // THERE CAN BE ONLY ONE + tree.removeNode(subRoot); + tree.addEdge(parent.get(), subTree, parentEdge); + } else { + // subRoot is the root of tree + tree.removeNode(subRoot); + tree.addNode(subTree); } + return subTree; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public static void expand(MutableCTreeNetwork tree, CTreeNetwork subTree) { + Optional parent = tree.predecessor(subTree); + Object parentEdge = + parent.isPresent() + ? tree.inEdges(subTree).iterator().next() // THERE CAN BE ONLY ONE + : null; + tree.removeNode(subTree); + TreeUtils.addSubTree(tree, subTree, parent.orElse(null), parentEdge); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/AbstractLensSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/AbstractLensSupport.java index e8df7960..6b9ea128 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/AbstractLensSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/AbstractLensSupport.java @@ -10,176 +10,155 @@ package edu.uci.ics.jung.visualization.transform; +import edu.uci.ics.jung.visualization.VisualizationServer.Paintable; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.ModalGraphMouse; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.RectangularShape; - -import edu.uci.ics.jung.visualization.VisualizationServer.Paintable; -import edu.uci.ics.jung.visualization.VisualizationViewer; -import edu.uci.ics.jung.visualization.control.ModalGraphMouse; /** - * A class to make it easy to add an - * examining lens to a jung graph application. See HyperbolicTransformerDemo, - * ViewLensSupport and LayoutLensSupport - * for examples of how to use it. - * + * A class to make it easy to add an examining lens to a jung graph application. See + * HyperbolicTransformerDemo, ViewLensSupport and LayoutLensSupport for examples of how to use it. + * * @author Tom Nelson */ -public abstract class AbstractLensSupport implements LensSupport { - - protected VisualizationViewer vv; - protected VisualizationViewer.GraphMouse graphMouse; - protected LensTransformer lensTransformer; - protected ModalGraphMouse lensGraphMouse; - protected Lens lens; - protected LensControls lensControls; - protected String defaultToolTipText; - - protected static final String instructions = - "

            Mouse-Drag the Lens center to move it

            "+ - "Mouse-Drag the Lens edge to resize it

            "+ - "Ctrl+MouseWheel to change magnification

            "; - - /** - * create the base class, setting common members and creating - * a custom GraphMouse - * @param vv the VisualizationViewer to work on - * @param lensGraphMouse the GraphMouse instance to use for the lens - */ - public AbstractLensSupport(VisualizationViewer vv, ModalGraphMouse lensGraphMouse) { - this.vv = vv; - this.graphMouse = vv.getGraphMouse(); - this.defaultToolTipText = vv.getToolTipText(); - this.lensGraphMouse = lensGraphMouse; +public abstract class AbstractLensSupport implements LensSupport { + + protected VisualizationViewer vv; + protected VisualizationViewer.GraphMouse graphMouse; + protected LensTransformer lensTransformer; + protected ModalGraphMouse lensGraphMouse; + protected Lens lens; + protected LensControls lensControls; + protected String defaultToolTipText; + + protected static final String instructions = + "
            Mouse-Drag the Lens center to move it

            " + + "Mouse-Drag the Lens edge to resize it

            " + + "Ctrl+MouseWheel to change magnification

            "; + + /** + * create the base class, setting common members and creating a custom GraphMouse + * + * @param vv the VisualizationViewer to work on + * @param lensGraphMouse the GraphMouse instance to use for the lens + */ + public AbstractLensSupport(VisualizationViewer vv, ModalGraphMouse lensGraphMouse) { + this.vv = vv; + this.graphMouse = vv.getGraphMouse(); + this.defaultToolTipText = vv.getToolTipText(); + this.lensGraphMouse = lensGraphMouse; + } + + public void activate(boolean state) { + if (state) activate(); + else deactivate(); + } + + public LensTransformer getLensTransformer() { + return lensTransformer; + } + + /** @return the hyperbolicGraphMouse. */ + public ModalGraphMouse getGraphMouse() { + return lensGraphMouse; + } + + /** + * the background for the hyperbolic projection + * + * @author Tom Nelson + */ + public static class Lens implements Paintable { + LensTransformer lensTransformer; + RectangularShape lensShape; + Paint paint = Color.decode("0xdddddd"); + + public Lens(LensTransformer lensTransformer) { + this.lensTransformer = lensTransformer; + this.lensShape = lensTransformer.getLensShape(); } - public void activate(boolean state) { - if(state) activate(); - else deactivate(); + /** @return the paint */ + public Paint getPaint() { + return paint; } - - public LensTransformer getLensTransformer() { - return lensTransformer; + + /** @param paint the paint to set */ + public void setPaint(Paint paint) { + this.paint = paint; } - /** - * @return the hyperbolicGraphMouse. - */ - public ModalGraphMouse getGraphMouse() { - return lensGraphMouse; + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + g2d.setPaint(paint); + g2d.fill(lensShape); } - /** - * the background for the hyperbolic projection - * @author Tom Nelson - */ - public static class Lens implements Paintable { - LensTransformer lensTransformer; - RectangularShape lensShape; - Paint paint = Color.decode("0xdddddd"); - - public Lens(LensTransformer lensTransformer) { - this.lensTransformer = lensTransformer; - this.lensShape = lensTransformer.getLensShape(); - } - - /** - * @return the paint - */ - public Paint getPaint() { - return paint; - } - - /** - * @param paint the paint to set - */ - public void setPaint(Paint paint) { - this.paint = paint; - } - - public void paint(Graphics g) { - Graphics2D g2d = (Graphics2D)g; - g2d.setPaint(paint); - g2d.fill(lensShape); - } - - public boolean useTransform() { - return true; - } + public boolean useTransform() { + return true; } - - /** - * the background for the hyperbolic projection - * @author Tom Nelson - * - * - */ - public static class LensControls implements Paintable { - LensTransformer lensTransformer; - RectangularShape lensShape; - Paint paint = Color.gray; - - public LensControls(LensTransformer lensTransformer) { - this.lensTransformer = lensTransformer; - this.lensShape = lensTransformer.getLensShape(); - } - - /** - * @return the paint - */ - public Paint getPaint() { - return paint; - } - - /** - * @param paint the paint to set - */ - public void setPaint(Paint paint) { - this.paint = paint; - } - - public void paint(Graphics g) { - - Graphics2D g2d = (Graphics2D)g; - g2d.setPaint(paint); - g2d.draw(lensShape); - int centerX = (int)Math.round(lensShape.getCenterX()); - int centerY = (int)Math.round(lensShape.getCenterY()); - g.drawOval(centerX-10, centerY-10, 20, 20); - } - - public boolean useTransform() { - return true; - } + } + + /** + * the background for the hyperbolic projection + * + * @author Tom Nelson + */ + public static class LensControls implements Paintable { + LensTransformer lensTransformer; + RectangularShape lensShape; + Paint paint = Color.gray; + + public LensControls(LensTransformer lensTransformer) { + this.lensTransformer = lensTransformer; + this.lensShape = lensTransformer.getLensShape(); } - /** - * @return the lens - */ - public Lens getLens() { - return lens; - } - - /** - * @param lens the lens to set - */ - public void setLens(Lens lens) { - this.lens = lens; - } - - /** - * @return the lensControls - */ - public LensControls getLensControls() { - return lensControls; - } - - /** - * @param lensControls the lensControls to set - */ - public void setLensControls(LensControls lensControls) { - this.lensControls = lensControls; - } + /** @return the paint */ + public Paint getPaint() { + return paint; + } + + /** @param paint the paint to set */ + public void setPaint(Paint paint) { + this.paint = paint; + } + + public void paint(Graphics g) { + + Graphics2D g2d = (Graphics2D) g; + g2d.setPaint(paint); + g2d.draw(lensShape); + int centerX = (int) Math.round(lensShape.getCenterX()); + int centerY = (int) Math.round(lensShape.getCenterY()); + g.drawOval(centerX - 10, centerY - 10, 20, 20); + } + + public boolean useTransform() { + return true; + } + } + + /** @return the lens */ + public Lens getLens() { + return lens; + } + + /** @param lens the lens to set */ + public void setLens(Lens lens) { + this.lens = lens; + } + + /** @return the lensControls */ + public LensControls getLensControls() { + return lensControls; + } + + /** @param lensControls the lensControls to set */ + public void setLensControls(LensControls lensControls) { + this.lensControls = lensControls; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/AffineTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/AffineTransformer.java index 77d52d47..cb837a23 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/AffineTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/AffineTransformer.java @@ -10,6 +10,7 @@ package edu.uci.ics.jung.visualization.transform; +import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; @@ -17,257 +18,237 @@ import java.awt.geom.PathIterator; import java.awt.geom.Point2D; -import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer; - /** + * Provides methods to map points from one coordinate system to another, by delegating to a wrapped + * AffineTransform (uniform) and its inverse. * - * Provides methods to map points from one coordinate system to - * another, by delegating to a wrapped AffineTransform (uniform) - * and its inverse. - * * @author Tom Nelson */ public class AffineTransformer implements BidirectionalTransformer, ShapeTransformer { - protected AffineTransform inverse; - - /** - * The AffineTransform to use; initialized to identity. - * - */ - protected AffineTransform transform = new AffineTransform(); - - /** - * Create an instance that does not transform points. - */ - public AffineTransformer() { - // nothing left to do - } + protected AffineTransform inverse; - /** - * Create an instance with the supplied transform. - * @param transform the transform to use - */ - public AffineTransformer(AffineTransform transform) { - if(transform != null) - this.transform = transform; - } + /** The AffineTransform to use; initialized to identity. */ + protected AffineTransform transform = new AffineTransform(); - /** - * @return Returns the transform. - */ - public AffineTransform getTransform() { - return transform; - } - /** - * @param transform The transform to set. - */ - public void setTransform(AffineTransform transform) { - this.transform = transform; - } - - /** - * applies the inverse transform to the supplied point - * @param p the point to transform - * @return the transformed point - */ - public Point2D inverseTransform(Point2D p) { - - return getInverse().transform(p, null); - } - - public AffineTransform getInverse() { - if(inverse == null) { - try { - inverse = transform.createInverse(); - } catch (NoninvertibleTransformException e) { - e.printStackTrace(); - } - } - return inverse; - } - - /** - * @return the transform's x scale value - */ - public double getScaleX() { - return transform.getScaleX(); - } - - /** - * @return the transform's y scale value - */ - public double getScaleY() { - return transform.getScaleY(); - } + /** Create an instance that does not transform points. */ + public AffineTransformer() { + // nothing left to do + } - /** - * @return the transform's overall scale magnitude - */ - public double getScale() { - return Math.sqrt(transform.getDeterminant()); - } - - /** - * @return the transform's x shear value - */ - public double getShearX() { - return transform.getShearX(); - } - - /** - * @return the transform's y shear value - */ - public double getShearY() { - return transform.getShearY(); - } - - /** - * @return the transform's x translate value - */ - public double getTranslateX() { - return transform.getTranslateX(); - } - - /** - * @return the transform's y translate value - */ - public double getTranslateY() { - return transform.getTranslateY(); - } - - /** - * Applies the transform to the supplied point. - * - * @param p the point to be transformed - * @return the transformed point - */ - public Point2D transform(Point2D p) { - if(p == null) return null; - return transform.transform(p, null); - } - - /** - * Transform the supplied shape from graph (layout) to screen (view) coordinates. - * - * @return the GeneralPath of the transformed shape - */ - public Shape transform(Shape shape) { - GeneralPath newPath = new GeneralPath(); - float[] coords = new float[6]; - for(PathIterator iterator=shape.getPathIterator(null); - iterator.isDone() == false; - iterator.next()) { - int type = iterator.currentSegment(coords); - switch(type) { - case PathIterator.SEG_MOVETO: - Point2D p = transform(new Point2D.Float(coords[0], coords[1])); - newPath.moveTo((float)p.getX(), (float)p.getY()); - break; - - case PathIterator.SEG_LINETO: - p = transform(new Point2D.Float(coords[0], coords[1])); - newPath.lineTo((float)p.getX(), (float) p.getY()); - break; - - case PathIterator.SEG_QUADTO: - p = transform(new Point2D.Float(coords[0], coords[1])); - Point2D q = transform(new Point2D.Float(coords[2], coords[3])); - newPath.quadTo((float)p.getX(), (float)p.getY(), (float)q.getX(), (float)q.getY()); - break; - - case PathIterator.SEG_CUBICTO: - p = transform(new Point2D.Float(coords[0], coords[1])); - q = transform(new Point2D.Float(coords[2], coords[3])); - Point2D r = transform(new Point2D.Float(coords[4], coords[5])); - newPath.curveTo((float)p.getX(), (float)p.getY(), - (float)q.getX(), (float)q.getY(), - (float)r.getX(), (float)r.getY()); - break; - - case PathIterator.SEG_CLOSE: - newPath.closePath(); - break; - - } - } - return newPath; - } + /** + * Create an instance with the supplied transform. + * + * @param transform the transform to use + */ + public AffineTransformer(AffineTransform transform) { + if (transform != null) this.transform = transform; + } + + /** @return Returns the transform. */ + public AffineTransform getTransform() { + return transform; + } + /** @param transform The transform to set. */ + public void setTransform(AffineTransform transform) { + this.transform = transform; + } + + /** + * applies the inverse transform to the supplied point + * + * @param p the point to transform + * @return the transformed point + */ + public Point2D inverseTransform(Point2D p) { + + return getInverse().transform(p, null); + } + + public AffineTransform getInverse() { + if (inverse == null) { + try { + inverse = transform.createInverse(); + } catch (NoninvertibleTransformException e) { + e.printStackTrace(); + } + } + return inverse; + } - /** - * Transform the supplied shape from screen (view) to graph (layout) coordinates. - * - * @return the GeneralPath of the transformed shape - */ - public Shape inverseTransform(Shape shape) { - GeneralPath newPath = new GeneralPath(); - float[] coords = new float[6]; - for(PathIterator iterator=shape.getPathIterator(null); - iterator.isDone() == false; - iterator.next()) { - int type = iterator.currentSegment(coords); - switch(type) { - case PathIterator.SEG_MOVETO: - Point2D p = inverseTransform(new Point2D.Float(coords[0], coords[1])); - newPath.moveTo((float)p.getX(), (float)p.getY()); - break; - - case PathIterator.SEG_LINETO: - p = inverseTransform(new Point2D.Float(coords[0], coords[1])); - newPath.lineTo((float)p.getX(), (float) p.getY()); - break; - - case PathIterator.SEG_QUADTO: - p = inverseTransform(new Point2D.Float(coords[0], coords[1])); - Point2D q = inverseTransform(new Point2D.Float(coords[2], coords[3])); - newPath.quadTo((float)p.getX(), (float)p.getY(), (float)q.getX(), (float)q.getY()); - break; - - case PathIterator.SEG_CUBICTO: - p = inverseTransform(new Point2D.Float(coords[0], coords[1])); - q = inverseTransform(new Point2D.Float(coords[2], coords[3])); - Point2D r = inverseTransform(new Point2D.Float(coords[4], coords[5])); - newPath.curveTo((float)p.getX(), (float)p.getY(), - (float)q.getX(), (float)q.getY(), - (float)r.getX(), (float)r.getY()); - break; - - case PathIterator.SEG_CLOSE: - newPath.closePath(); - break; - - } - } - return newPath; + /** @return the transform's x scale value */ + public double getScaleX() { + return transform.getScaleX(); + } + + /** @return the transform's y scale value */ + public double getScaleY() { + return transform.getScaleY(); + } + + /** @return the transform's overall scale magnitude */ + public double getScale() { + return Math.sqrt(transform.getDeterminant()); + } + + /** @return the transform's x shear value */ + public double getShearX() { + return transform.getShearX(); + } + + /** @return the transform's y shear value */ + public double getShearY() { + return transform.getShearY(); + } + + /** @return the transform's x translate value */ + public double getTranslateX() { + return transform.getTranslateX(); + } + + /** @return the transform's y translate value */ + public double getTranslateY() { + return transform.getTranslateY(); + } + + /** + * Applies the transform to the supplied point. + * + * @param p the point to be transformed + * @return the transformed point + */ + public Point2D transform(Point2D p) { + if (p == null) return null; + return transform.transform(p, null); + } + + /** + * Transform the supplied shape from graph (layout) to screen (view) coordinates. + * + * @return the GeneralPath of the transformed shape + */ + public Shape transform(Shape shape) { + GeneralPath newPath = new GeneralPath(); + float[] coords = new float[6]; + for (PathIterator iterator = shape.getPathIterator(null); + iterator.isDone() == false; + iterator.next()) { + int type = iterator.currentSegment(coords); + switch (type) { + case PathIterator.SEG_MOVETO: + Point2D p = transform(new Point2D.Float(coords[0], coords[1])); + newPath.moveTo((float) p.getX(), (float) p.getY()); + break; + + case PathIterator.SEG_LINETO: + p = transform(new Point2D.Float(coords[0], coords[1])); + newPath.lineTo((float) p.getX(), (float) p.getY()); + break; + + case PathIterator.SEG_QUADTO: + p = transform(new Point2D.Float(coords[0], coords[1])); + Point2D q = transform(new Point2D.Float(coords[2], coords[3])); + newPath.quadTo((float) p.getX(), (float) p.getY(), (float) q.getX(), (float) q.getY()); + break; + + case PathIterator.SEG_CUBICTO: + p = transform(new Point2D.Float(coords[0], coords[1])); + q = transform(new Point2D.Float(coords[2], coords[3])); + Point2D r = transform(new Point2D.Float(coords[4], coords[5])); + newPath.curveTo( + (float) p.getX(), + (float) p.getY(), + (float) q.getX(), + (float) q.getY(), + (float) r.getX(), + (float) r.getY()); + break; + + case PathIterator.SEG_CLOSE: + newPath.closePath(); + break; + } } - - public double getRotation() { - double[] unitVector = new double[]{0,0,1,0}; - double[] result = new double[4]; - - transform.transform(unitVector, 0, result, 0, 2); - - double dy = Math.abs(result[3] - result[1]); - double length = Point2D.distance(result[0], result[1], result[2], result[3]); - double rotation = Math.asin(dy / length); - - if (result[3] - result[1] > 0) { - if (result[2] - result[0] < 0) { - rotation = Math.PI - rotation; - } - } else { - if (result[2] - result[0] > 0) { - rotation = 2 * Math.PI - rotation; - } else { - rotation = rotation + Math.PI; - } - } - - return rotation; + return newPath; + } + + /** + * Transform the supplied shape from screen (view) to graph (layout) coordinates. + * + * @return the GeneralPath of the transformed shape + */ + public Shape inverseTransform(Shape shape) { + GeneralPath newPath = new GeneralPath(); + float[] coords = new float[6]; + for (PathIterator iterator = shape.getPathIterator(null); + iterator.isDone() == false; + iterator.next()) { + int type = iterator.currentSegment(coords); + switch (type) { + case PathIterator.SEG_MOVETO: + Point2D p = inverseTransform(new Point2D.Float(coords[0], coords[1])); + newPath.moveTo((float) p.getX(), (float) p.getY()); + break; + + case PathIterator.SEG_LINETO: + p = inverseTransform(new Point2D.Float(coords[0], coords[1])); + newPath.lineTo((float) p.getX(), (float) p.getY()); + break; + + case PathIterator.SEG_QUADTO: + p = inverseTransform(new Point2D.Float(coords[0], coords[1])); + Point2D q = inverseTransform(new Point2D.Float(coords[2], coords[3])); + newPath.quadTo((float) p.getX(), (float) p.getY(), (float) q.getX(), (float) q.getY()); + break; + + case PathIterator.SEG_CUBICTO: + p = inverseTransform(new Point2D.Float(coords[0], coords[1])); + q = inverseTransform(new Point2D.Float(coords[2], coords[3])); + Point2D r = inverseTransform(new Point2D.Float(coords[4], coords[5])); + newPath.curveTo( + (float) p.getX(), + (float) p.getY(), + (float) q.getX(), + (float) q.getY(), + (float) r.getX(), + (float) r.getY()); + break; + + case PathIterator.SEG_CLOSE: + newPath.closePath(); + break; + } } + return newPath; + } - @Override - public String toString() { - return "Transformer using "+transform; + public double getRotation() { + double[] unitVector = new double[] {0, 0, 1, 0}; + double[] result = new double[4]; + + transform.transform(unitVector, 0, result, 0, 2); + + double dy = Math.abs(result[3] - result[1]); + double length = Point2D.distance(result[0], result[1], result[2], result[3]); + double rotation = Math.asin(dy / length); + + if (result[3] - result[1] > 0) { + if (result[2] - result[0] < 0) { + rotation = Math.PI - rotation; + } + } else { + if (result[2] - result[0] > 0) { + rotation = 2 * Math.PI - rotation; + } else { + rotation = rotation + Math.PI; + } } + + return rotation; + } + + @Override + public String toString() { + return "Transformer using " + transform; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/BidirectionalTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/BidirectionalTransformer.java index 411add6f..2191e13e 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/BidirectionalTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/BidirectionalTransformer.java @@ -13,24 +13,26 @@ import java.awt.geom.Point2D; /** - * Provides methods to map points from one coordinate system to - * another: graph to screen and screen to graph. - * - * @author Tom Nelson + * Provides methods to map points from one coordinate system to another: graph to screen and screen + * to graph. + * + * @author Tom Nelson */ public interface BidirectionalTransformer { - - /** - * convert the supplied graph coordinate to the screen coordinate - * @param p graph point to convert - * @return screen point - */ - Point2D transform(Point2D p); - - /** - * convert the supplied screen coordinate to the graph coordinate. - * @param p screen point to convert - * @return the graph point - */ - Point2D inverseTransform(Point2D p); + + /** + * convert the supplied graph coordinate to the screen coordinate + * + * @param p graph point to convert + * @return screen point + */ + Point2D transform(Point2D p); + + /** + * convert the supplied screen coordinate to the graph coordinate. + * + * @param p screen point to convert + * @return the graph point + */ + Point2D inverseTransform(Point2D p); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/HyperbolicTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/HyperbolicTransformer.java index 9bed804d..c6d4ab4f 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/HyperbolicTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/HyperbolicTransformer.java @@ -1,120 +1,116 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.visualization.transform; +import edu.uci.ics.jung.algorithms.layout.PolarPoint; import java.awt.Component; import java.awt.geom.Point2D; -import edu.uci.ics.jung.algorithms.layout.PolarPoint; - /** - * HyperbolicTransformer wraps a MutableAffineTransformer and modifies - * the transform and inverseTransform methods so that they create a - * fisheye projection of the graph points, with points near the - * center spread out and points near the edges collapsed onto the - * circumference of an ellipse. - * - * HyperbolicTransformer is not an affine transform, but it uses an - * affine transform to cause translation, scaling, rotation, and shearing - * while applying a non-affine hyperbolic filter in its transform and - * inverseTransform methods. - * - * @author Tom Nelson + * HyperbolicTransformer wraps a MutableAffineTransformer and modifies the transform and + * inverseTransform methods so that they create a fisheye projection of the graph points, with + * points near the center spread out and points near the edges collapsed onto the circumference of + * an ellipse. + * + *

            HyperbolicTransformer is not an affine transform, but it uses an affine transform to cause + * translation, scaling, rotation, and shearing while applying a non-affine hyperbolic filter in its + * transform and inverseTransform methods. + * + * @author Tom Nelson */ public class HyperbolicTransformer extends LensTransformer implements MutableTransformer { - /** - * create an instance, setting values from the passed component - * and registering to listen for size changes on the component - * @param component the component used for rendering - */ - public HyperbolicTransformer(Component component) { - this(component, new MutableAffineTransformer()); - } + /** + * create an instance, setting values from the passed component and registering to listen for size + * changes on the component + * + * @param component the component used for rendering + */ + public HyperbolicTransformer(Component component) { + this(component, new MutableAffineTransformer()); + } + + /** + * Create an instance with a possibly shared transform. + * + * @param component the component used for rendering + * @param delegate the transformer to use + */ + public HyperbolicTransformer(Component component, MutableTransformer delegate) { + super(component, delegate); + } + + /** override base class transform to project the fisheye effect */ + public Point2D transform(Point2D graphPoint) { + if (graphPoint == null) return null; + Point2D viewCenter = getViewCenter(); + double viewRadius = getViewRadius(); + double ratio = getRatio(); + // transform the point from the graph to the view + Point2D viewPoint = delegate.transform(graphPoint); + // calculate point from center + double dx = viewPoint.getX() - viewCenter.getX(); + double dy = viewPoint.getY() - viewCenter.getY(); + // factor out ellipse + dx *= ratio; + Point2D pointFromCenter = new Point2D.Double(dx, dy); + + PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); + double theta = polar.getTheta(); + double radius = polar.getRadius(); + if (radius > viewRadius) return viewPoint; + + double mag = Math.tan(Math.PI / 2 * magnification); + radius *= mag; + + radius = Math.min(radius, viewRadius); + radius /= viewRadius; + radius *= Math.PI / 2; + radius = Math.abs(Math.atan(radius)); + radius *= viewRadius; + Point2D projectedPoint = PolarPoint.polarToCartesian(theta, radius); + projectedPoint.setLocation(projectedPoint.getX() / ratio, projectedPoint.getY()); + Point2D translatedBack = + new Point2D.Double( + projectedPoint.getX() + viewCenter.getX(), projectedPoint.getY() + viewCenter.getY()); + return translatedBack; + } + + /** override base class to un-project the fisheye effect */ + public Point2D inverseTransform(Point2D viewPoint) { + + Point2D viewCenter = getViewCenter(); + double viewRadius = getViewRadius(); + double ratio = getRatio(); + double dx = viewPoint.getX() - viewCenter.getX(); + double dy = viewPoint.getY() - viewCenter.getY(); + // factor out ellipse + dx *= ratio; + + Point2D pointFromCenter = new Point2D.Double(dx, dy); - /** - * Create an instance with a possibly shared transform. - * - * @param component the component used for rendering - * @param delegate the transformer to use - */ - public HyperbolicTransformer(Component component, MutableTransformer delegate) { - super(component, delegate); - } - - /** - * override base class transform to project the fisheye effect - */ - public Point2D transform(Point2D graphPoint) { - if(graphPoint == null) return null; - Point2D viewCenter = getViewCenter(); - double viewRadius = getViewRadius(); - double ratio = getRatio(); - // transform the point from the graph to the view - Point2D viewPoint = delegate.transform(graphPoint); - // calculate point from center - double dx = viewPoint.getX() - viewCenter.getX(); - double dy = viewPoint.getY() - viewCenter.getY(); - // factor out ellipse - dx *= ratio; - Point2D pointFromCenter = new Point2D.Double(dx, dy); - - PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); - double theta = polar.getTheta(); - double radius = polar.getRadius(); - if(radius > viewRadius) return viewPoint; - - double mag = Math.tan(Math.PI/2*magnification); - radius *= mag; - - radius = Math.min(radius, viewRadius); - radius /= viewRadius; - radius *= Math.PI/2; - radius = Math.abs(Math.atan(radius)); - radius *= viewRadius; - Point2D projectedPoint = PolarPoint.polarToCartesian(theta, radius); - projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY()); - Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(), - projectedPoint.getY()+viewCenter.getY()); - return translatedBack; - } - - /** - * override base class to un-project the fisheye effect - */ - public Point2D inverseTransform(Point2D viewPoint) { - - Point2D viewCenter = getViewCenter(); - double viewRadius = getViewRadius(); - double ratio = getRatio(); - double dx = viewPoint.getX() - viewCenter.getX(); - double dy = viewPoint.getY() - viewCenter.getY(); - // factor out ellipse - dx *= ratio; + PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); - Point2D pointFromCenter = new Point2D.Double(dx, dy); - - PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); + double radius = polar.getRadius(); + if (radius > viewRadius) return delegate.inverseTransform(viewPoint); - double radius = polar.getRadius(); - if(radius > viewRadius) return delegate.inverseTransform(viewPoint); - - radius /= viewRadius; - radius = Math.abs(Math.tan(radius)); - radius /= Math.PI/2; - radius *= viewRadius; - double mag = Math.tan(Math.PI/2*magnification); - radius /= mag; - polar.setRadius(radius); - Point2D projectedPoint = PolarPoint.polarToCartesian(polar); - projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY()); - Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(), - projectedPoint.getY()+viewCenter.getY()); - return delegate.inverseTransform(translatedBack); - } -} \ No newline at end of file + radius /= viewRadius; + radius = Math.abs(Math.tan(radius)); + radius /= Math.PI / 2; + radius *= viewRadius; + double mag = Math.tan(Math.PI / 2 * magnification); + radius /= mag; + polar.setRadius(radius); + Point2D projectedPoint = PolarPoint.polarToCartesian(polar); + projectedPoint.setLocation(projectedPoint.getX() / ratio, projectedPoint.getY()); + Point2D translatedBack = + new Point2D.Double( + projectedPoint.getX() + viewCenter.getX(), projectedPoint.getY() + viewCenter.getY()); + return delegate.inverseTransform(translatedBack); + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/LayoutLensSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/LayoutLensSupport.java index 2b5b7627..57c795c0 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/LayoutLensSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/LayoutLensSupport.java @@ -10,79 +10,80 @@ package edu.uci.ics.jung.visualization.transform; -import java.awt.Dimension; - import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.VisualizationViewer; import edu.uci.ics.jung.visualization.control.ModalGraphMouse; import edu.uci.ics.jung.visualization.control.ModalLensGraphMouse; import edu.uci.ics.jung.visualization.picking.LayoutLensShapePickSupport; +import java.awt.Dimension; /** - * A class to make it easy to add an - * examining lens to a jung graph application. See HyperbolicTransformerDemo - * for an example of how to use it. - * - * @author Tom Nelson - * + * A class to make it easy to add an examining lens to a jung graph application. See + * HyperbolicTransformerDemo for an example of how to use it. * + * @author Tom Nelson */ -public class LayoutLensSupport extends AbstractLensSupport - implements LensSupport { +public class LayoutLensSupport extends AbstractLensSupport implements LensSupport { - protected NetworkElementAccessor pickSupport; - - public LayoutLensSupport(VisualizationViewer vv) { - this(vv, new HyperbolicTransformer( - vv, vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT)), - new ModalLensGraphMouse()); + protected NetworkElementAccessor pickSupport; + + public LayoutLensSupport(VisualizationViewer vv) { + this( + vv, + new HyperbolicTransformer( + vv, vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT)), + new ModalLensGraphMouse()); + } + + /** + * Create an instance with the specified parameters. + * + * @param vv the visualization viewer used for rendering + * @param lensTransformer the lens transformer to use + * @param lensGraphMouse the lens input handler + */ + public LayoutLensSupport( + VisualizationViewer vv, + LensTransformer lensTransformer, + ModalGraphMouse lensGraphMouse) { + super(vv, lensGraphMouse); + this.lensTransformer = lensTransformer; + this.pickSupport = vv.getPickSupport(); + + Dimension d = vv.getSize(); + if (d.width <= 0 || d.height <= 0) { + d = vv.getPreferredSize(); } - - /** - * Create an instance with the specified parameters. - * - * @param vv the visualization viewer used for rendering - * @param lensTransformer the lens transformer to use - * @param lensGraphMouse the lens input handler - */ - public LayoutLensSupport(VisualizationViewer vv, LensTransformer lensTransformer, - ModalGraphMouse lensGraphMouse) { - super(vv, lensGraphMouse); - this.lensTransformer = lensTransformer; - this.pickSupport = vv.getPickSupport(); + lensTransformer.setViewRadius(d.width / 5); + } - Dimension d = vv.getSize(); - if(d.width <= 0 || d.height <= 0) { - d = vv.getPreferredSize(); - } - lensTransformer.setViewRadius(d.width/5); - } - - public void activate() { - if(lens == null) { - lens = new Lens(lensTransformer); - } - if(lensControls == null) { - lensControls = new LensControls(lensTransformer); - } - vv.getRenderContext().setPickSupport(new LayoutLensShapePickSupport(vv)); - vv.getRenderContext().getMultiLayerTransformer().setTransformer(Layer.LAYOUT, lensTransformer); - vv.prependPreRenderPaintable(lens); - vv.addPostRenderPaintable(lensControls); - vv.setGraphMouse(lensGraphMouse); - vv.setToolTipText(instructions); - vv.repaint(); + public void activate() { + if (lens == null) { + lens = new Lens(lensTransformer); } - - public void deactivate() { - if(lensTransformer != null) { - vv.removePreRenderPaintable(lens); - vv.removePostRenderPaintable(lensControls); - vv.getRenderContext().getMultiLayerTransformer().setTransformer(Layer.LAYOUT, lensTransformer.getDelegate()); - } - vv.getRenderContext().setPickSupport(pickSupport); - vv.setToolTipText(defaultToolTipText); - vv.setGraphMouse(graphMouse); - vv.repaint(); + if (lensControls == null) { + lensControls = new LensControls(lensTransformer); + } + vv.getRenderContext().setPickSupport(new LayoutLensShapePickSupport(vv)); + vv.getRenderContext().getMultiLayerTransformer().setTransformer(Layer.LAYOUT, lensTransformer); + vv.prependPreRenderPaintable(lens); + vv.addPostRenderPaintable(lensControls); + vv.setGraphMouse(lensGraphMouse); + vv.setToolTipText(instructions); + vv.repaint(); + } + + public void deactivate() { + if (lensTransformer != null) { + vv.removePreRenderPaintable(lens); + vv.removePostRenderPaintable(lensControls); + vv.getRenderContext() + .getMultiLayerTransformer() + .setTransformer(Layer.LAYOUT, lensTransformer.getDelegate()); } + vv.getRenderContext().setPickSupport(pickSupport); + vv.setToolTipText(defaultToolTipText); + vv.setGraphMouse(graphMouse); + vv.repaint(); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/LensSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/LensSupport.java index 9813940d..439edda4 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/LensSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/LensSupport.java @@ -14,16 +14,18 @@ /** * basic API for implementing lens projection support - * - * @author Tom Nelson * + * @author Tom Nelson */ public interface LensSupport { - void activate(); - void deactivate(); - void activate(boolean state); - LensTransformer getLensTransformer(); - - ModalGraphMouse getGraphMouse(); -} \ No newline at end of file + void activate(); + + void deactivate(); + + void activate(boolean state); + + LensTransformer getLensTransformer(); + + ModalGraphMouse getGraphMouse(); +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/LensTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/LensTransformer.java index 4559b34a..6255a3c1 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/LensTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/LensTransformer.java @@ -1,10 +1,10 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.visualization.transform; @@ -20,156 +20,143 @@ import java.awt.geom.RectangularShape; /** - * LensTransformer wraps a MutableAffineTransformer and modifies - * the transform and inverseTransform methods so that they create a - * projection of the graph points within an elliptical lens. - * - * LensTransformer uses an - * affine transform to cause translation, scaling, rotation, and shearing - * while applying a possibly non-affine filter in its transform and - * inverseTransform methods. - * + * LensTransformer wraps a MutableAffineTransformer and modifies the transform and inverseTransform + * methods so that they create a projection of the graph points within an elliptical lens. + * + *

            LensTransformer uses an affine transform to cause translation, scaling, rotation, and shearing + * while applying a possibly non-affine filter in its transform and inverseTransform methods. + * * @author Tom Nelson */ -public abstract class LensTransformer extends MutableTransformerDecorator implements MutableTransformer { - - /** - * the area affected by the transform - */ - protected RectangularShape lensShape = new Ellipse2D.Float(); - - protected float magnification = 0.7f; - - /** - * Create an instance with a possibly shared transform. - * - * @param component the component used for rendering - * @param delegate the transformer to use - */ - public LensTransformer(Component component, MutableTransformer delegate) { - super(delegate); - setComponent(component); - component.addComponentListener(new ComponentListenerImpl()); - } - - /** - * Set values from the passed component. - * @param component the component used for rendering - */ - private void setComponent(Component component) { - Dimension d = component.getSize(); - if(d.width <= 0 || d.height <= 0) { - d = component.getPreferredSize(); - } - float ewidth = d.width/1.5f; - float eheight = d.height/1.5f; - lensShape.setFrame(d.width/2-ewidth/2, d.height/2-eheight/2, ewidth, eheight); - } - - public float getMagnification() { - return magnification; - } +public abstract class LensTransformer extends MutableTransformerDecorator + implements MutableTransformer { - public void setMagnification(float magnification) { - this.magnification = magnification; - } - - public Point2D getViewCenter() { - return new Point2D.Double(lensShape.getCenterX(), lensShape.getCenterY()); - } + /** the area affected by the transform */ + protected RectangularShape lensShape = new Ellipse2D.Float(); - public void setViewCenter(Point2D viewCenter) { - double width = lensShape.getWidth(); - double height = lensShape.getHeight(); - lensShape.setFrame(viewCenter.getX()-width/2, - viewCenter.getY()-height/2, - width, height); - } + protected float magnification = 0.7f; - public double getViewRadius() { - return lensShape.getHeight()/2; - } + /** + * Create an instance with a possibly shared transform. + * + * @param component the component used for rendering + * @param delegate the transformer to use + */ + public LensTransformer(Component component, MutableTransformer delegate) { + super(delegate); + setComponent(component); + component.addComponentListener(new ComponentListenerImpl()); + } - public void setViewRadius(double viewRadius) { - double x = lensShape.getCenterX(); - double y = lensShape.getCenterY(); - double viewRatio = getRatio(); - lensShape.setFrame(x-viewRadius/viewRatio, - y-viewRadius, - 2*viewRadius/viewRatio, - 2*viewRadius); - } - - /** - * @return the ratio between the lens height and lens width - */ - public double getRatio() { - return lensShape.getHeight()/lensShape.getWidth(); - } - - public void setLensShape(RectangularShape ellipse) { - this.lensShape = ellipse; - } - public RectangularShape getLensShape() { - return lensShape; - } - public void setToIdentity() { - this.delegate.setToIdentity(); + /** + * Set values from the passed component. + * + * @param component the component used for rendering + */ + private void setComponent(Component component) { + Dimension d = component.getSize(); + if (d.width <= 0 || d.height <= 0) { + d = component.getPreferredSize(); } + float ewidth = d.width / 1.5f; + float eheight = d.height / 1.5f; + lensShape.setFrame(d.width / 2 - ewidth / 2, d.height / 2 - eheight / 2, ewidth, eheight); + } - /** - * react to size changes on a component - */ - protected class ComponentListenerImpl extends ComponentAdapter { - public void componentResized(ComponentEvent e) { - setComponent(e.getComponent()); - } - } - - /** - * override base class transform to project the fisheye effect - */ - public abstract Point2D transform(Point2D graphPoint); - - /** - * override base class to un-project the fisheye effect - */ - public abstract Point2D inverseTransform(Point2D viewPoint); - - public double getDistanceFromCenter(Point2D p) { - - double dx = lensShape.getCenterX()-p.getX(); - double dy = lensShape.getCenterY()-p.getY(); - dx *= getRatio(); - return Math.sqrt(dx*dx + dy*dy); - } - - /** - * return the supplied shape, translated to the coordinates - * that result from calling transform on its center - */ - public Shape transform(Shape shape) { - Rectangle2D bounds = shape.getBounds2D(); - Point2D center = new Point2D.Double(bounds.getCenterX(),bounds.getCenterY()); - Point2D newCenter = transform(center); - double dx = newCenter.getX()-center.getX(); - double dy = newCenter.getY()-center.getY(); - AffineTransform at = AffineTransform.getTranslateInstance(dx,dy); - return at.createTransformedShape(shape); - } - - /** - * Returns the supplied shape, translated to the coordinates - * that result from calling inverseTransform on its center. - */ - public Shape inverseTransform(Shape shape) { - Rectangle2D bounds = shape.getBounds2D(); - Point2D center = new Point2D.Double(bounds.getCenterX(),bounds.getCenterY()); - Point2D newCenter = inverseTransform(center); - double dx = newCenter.getX()-center.getX(); - double dy = newCenter.getY()-center.getY(); - AffineTransform at = AffineTransform.getTranslateInstance(dx,dy); - return at.createTransformedShape(shape); + public float getMagnification() { + return magnification; + } + + public void setMagnification(float magnification) { + this.magnification = magnification; + } + + public Point2D getViewCenter() { + return new Point2D.Double(lensShape.getCenterX(), lensShape.getCenterY()); + } + + public void setViewCenter(Point2D viewCenter) { + double width = lensShape.getWidth(); + double height = lensShape.getHeight(); + lensShape.setFrame( + viewCenter.getX() - width / 2, viewCenter.getY() - height / 2, width, height); + } + + public double getViewRadius() { + return lensShape.getHeight() / 2; + } + + public void setViewRadius(double viewRadius) { + double x = lensShape.getCenterX(); + double y = lensShape.getCenterY(); + double viewRatio = getRatio(); + lensShape.setFrame( + x - viewRadius / viewRatio, y - viewRadius, 2 * viewRadius / viewRatio, 2 * viewRadius); + } + + /** @return the ratio between the lens height and lens width */ + public double getRatio() { + return lensShape.getHeight() / lensShape.getWidth(); + } + + public void setLensShape(RectangularShape ellipse) { + this.lensShape = ellipse; + } + + public RectangularShape getLensShape() { + return lensShape; + } + + public void setToIdentity() { + this.delegate.setToIdentity(); + } + + /** react to size changes on a component */ + protected class ComponentListenerImpl extends ComponentAdapter { + public void componentResized(ComponentEvent e) { + setComponent(e.getComponent()); } + } + + /** override base class transform to project the fisheye effect */ + public abstract Point2D transform(Point2D graphPoint); + + /** override base class to un-project the fisheye effect */ + public abstract Point2D inverseTransform(Point2D viewPoint); + + public double getDistanceFromCenter(Point2D p) { + + double dx = lensShape.getCenterX() - p.getX(); + double dy = lensShape.getCenterY() - p.getY(); + dx *= getRatio(); + return Math.sqrt(dx * dx + dy * dy); + } + + /** + * return the supplied shape, translated to the coordinates that result from calling transform on + * its center + */ + public Shape transform(Shape shape) { + Rectangle2D bounds = shape.getBounds2D(); + Point2D center = new Point2D.Double(bounds.getCenterX(), bounds.getCenterY()); + Point2D newCenter = transform(center); + double dx = newCenter.getX() - center.getX(); + double dy = newCenter.getY() - center.getY(); + AffineTransform at = AffineTransform.getTranslateInstance(dx, dy); + return at.createTransformedShape(shape); + } -} \ No newline at end of file + /** + * Returns the supplied shape, translated to the coordinates that result from calling + * inverseTransform on its center. + */ + public Shape inverseTransform(Shape shape) { + Rectangle2D bounds = shape.getBounds2D(); + Point2D center = new Point2D.Double(bounds.getCenterX(), bounds.getCenterY()); + Point2D newCenter = inverseTransform(center); + double dx = newCenter.getX() - center.getX(); + double dy = newCenter.getY() - center.getY(); + AffineTransform at = AffineTransform.getTranslateInstance(dx, dy); + return at.createTransformedShape(shape); + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MagnifyTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MagnifyTransformer.java index 9fa1c05e..6d3b01e4 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MagnifyTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MagnifyTransformer.java @@ -1,146 +1,142 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.visualization.transform; +import edu.uci.ics.jung.algorithms.layout.PolarPoint; import java.awt.Component; import java.awt.geom.Point2D; -import edu.uci.ics.jung.algorithms.layout.PolarPoint; - /** - * MagnifyTransformer wraps a MutableAffineTransformer and modifies - * the transform and inverseTransform methods so that they create an - * enlarging projection of the graph points. - * - * MagnifyTransformer uses an - * affine transform to cause translation, scaling, rotation, and shearing - * while applying a separate magnification filter in its transform and - * inverseTransform methods. - * - * @author Tom Nelson + * MagnifyTransformer wraps a MutableAffineTransformer and modifies the transform and + * inverseTransform methods so that they create an enlarging projection of the graph points. + * + *

            MagnifyTransformer uses an affine transform to cause translation, scaling, rotation, and + * shearing while applying a separate magnification filter in its transform and inverseTransform + * methods. + * + * @author Tom Nelson */ public class MagnifyTransformer extends LensTransformer implements MutableTransformer { - /** - * Create an instance, setting values from the passed component - * and registering to listen for size changes on the component. - * - * @param component the component used for rendering - */ - public MagnifyTransformer(Component component) { - this(component, new MutableAffineTransformer()); - } - - /** - * Create an instance with a possibly shared transform. - * - * @param component the component used for rendering - * @param delegate the transformer to use - */ - public MagnifyTransformer(Component component, MutableTransformer delegate) { - super(component, delegate); - this.magnification = 3.f; - } - - /** - * override base class transform to project the fisheye effect - */ - public Point2D transform(Point2D graphPoint) { - if(graphPoint == null) return null; - Point2D viewCenter = getViewCenter(); - double viewRadius = getViewRadius(); - double ratio = getRatio(); - // transform the point from the graph to the view - Point2D viewPoint = delegate.transform(graphPoint); - // calculate point from center - double dx = viewPoint.getX() - viewCenter.getX(); - double dy = viewPoint.getY() - viewCenter.getY(); - // factor out ellipse - dx *= ratio; - Point2D pointFromCenter = new Point2D.Double(dx, dy); - - PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); - double theta = polar.getTheta(); - double radius = polar.getRadius(); - if(radius > viewRadius) return viewPoint; - - double mag = magnification; - radius *= mag; - - radius = Math.min(radius, viewRadius); - Point2D projectedPoint = PolarPoint.polarToCartesian(theta, radius); - projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY()); - Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(), - projectedPoint.getY()+viewCenter.getY()); - return translatedBack; - } - - /** - * override base class to un-project the fisheye effect - */ - public Point2D inverseTransform(Point2D viewPoint) { - - Point2D viewCenter = getViewCenter(); - double viewRadius = getViewRadius(); - double ratio = getRatio(); - double dx = viewPoint.getX() - viewCenter.getX(); - double dy = viewPoint.getY() - viewCenter.getY(); - // factor out ellipse - dx *= ratio; - - Point2D pointFromCenter = new Point2D.Double(dx, dy); - - PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); - - double radius = polar.getRadius(); - if(radius > viewRadius) return delegate.inverseTransform(viewPoint); - - double mag = magnification; - radius /= mag; - polar.setRadius(radius); - Point2D projectedPoint = PolarPoint.polarToCartesian(polar); - projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY()); - Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(), - projectedPoint.getY()+viewCenter.getY()); - return delegate.inverseTransform(translatedBack); - } - - /** - * Magnifies the point, without considering the Lens. - * @param graphPoint the point to transform via magnification - * @return the transformed point - */ - public Point2D magnify(Point2D graphPoint) { - if(graphPoint == null) return null; - Point2D viewCenter = getViewCenter(); - double ratio = getRatio(); - // transform the point from the graph to the view - Point2D viewPoint = graphPoint; - // calculate point from center - double dx = viewPoint.getX() - viewCenter.getX(); - double dy = viewPoint.getY() - viewCenter.getY(); - // factor out ellipse - dx *= ratio; - Point2D pointFromCenter = new Point2D.Double(dx, dy); - - PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); - double theta = polar.getTheta(); - double radius = polar.getRadius(); - - double mag = magnification; - radius *= mag; - -// radius = Math.min(radius, viewRadius); - Point2D projectedPoint = PolarPoint.polarToCartesian(theta, radius); - projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY()); - Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(), - projectedPoint.getY()+viewCenter.getY()); - return translatedBack; - } - -} \ No newline at end of file + /** + * Create an instance, setting values from the passed component and registering to listen for size + * changes on the component. + * + * @param component the component used for rendering + */ + public MagnifyTransformer(Component component) { + this(component, new MutableAffineTransformer()); + } + + /** + * Create an instance with a possibly shared transform. + * + * @param component the component used for rendering + * @param delegate the transformer to use + */ + public MagnifyTransformer(Component component, MutableTransformer delegate) { + super(component, delegate); + this.magnification = 3.f; + } + + /** override base class transform to project the fisheye effect */ + public Point2D transform(Point2D graphPoint) { + if (graphPoint == null) return null; + Point2D viewCenter = getViewCenter(); + double viewRadius = getViewRadius(); + double ratio = getRatio(); + // transform the point from the graph to the view + Point2D viewPoint = delegate.transform(graphPoint); + // calculate point from center + double dx = viewPoint.getX() - viewCenter.getX(); + double dy = viewPoint.getY() - viewCenter.getY(); + // factor out ellipse + dx *= ratio; + Point2D pointFromCenter = new Point2D.Double(dx, dy); + + PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); + double theta = polar.getTheta(); + double radius = polar.getRadius(); + if (radius > viewRadius) return viewPoint; + + double mag = magnification; + radius *= mag; + + radius = Math.min(radius, viewRadius); + Point2D projectedPoint = PolarPoint.polarToCartesian(theta, radius); + projectedPoint.setLocation(projectedPoint.getX() / ratio, projectedPoint.getY()); + Point2D translatedBack = + new Point2D.Double( + projectedPoint.getX() + viewCenter.getX(), projectedPoint.getY() + viewCenter.getY()); + return translatedBack; + } + + /** override base class to un-project the fisheye effect */ + public Point2D inverseTransform(Point2D viewPoint) { + + Point2D viewCenter = getViewCenter(); + double viewRadius = getViewRadius(); + double ratio = getRatio(); + double dx = viewPoint.getX() - viewCenter.getX(); + double dy = viewPoint.getY() - viewCenter.getY(); + // factor out ellipse + dx *= ratio; + + Point2D pointFromCenter = new Point2D.Double(dx, dy); + + PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); + + double radius = polar.getRadius(); + if (radius > viewRadius) return delegate.inverseTransform(viewPoint); + + double mag = magnification; + radius /= mag; + polar.setRadius(radius); + Point2D projectedPoint = PolarPoint.polarToCartesian(polar); + projectedPoint.setLocation(projectedPoint.getX() / ratio, projectedPoint.getY()); + Point2D translatedBack = + new Point2D.Double( + projectedPoint.getX() + viewCenter.getX(), projectedPoint.getY() + viewCenter.getY()); + return delegate.inverseTransform(translatedBack); + } + + /** + * Magnifies the point, without considering the Lens. + * + * @param graphPoint the point to transform via magnification + * @return the transformed point + */ + public Point2D magnify(Point2D graphPoint) { + if (graphPoint == null) return null; + Point2D viewCenter = getViewCenter(); + double ratio = getRatio(); + // transform the point from the graph to the view + Point2D viewPoint = graphPoint; + // calculate point from center + double dx = viewPoint.getX() - viewCenter.getX(); + double dy = viewPoint.getY() - viewCenter.getY(); + // factor out ellipse + dx *= ratio; + Point2D pointFromCenter = new Point2D.Double(dx, dy); + + PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); + double theta = polar.getTheta(); + double radius = polar.getRadius(); + + double mag = magnification; + radius *= mag; + + // radius = Math.min(radius, viewRadius); + Point2D projectedPoint = PolarPoint.polarToCartesian(theta, radius); + projectedPoint.setLocation(projectedPoint.getX() / ratio, projectedPoint.getY()); + Point2D translatedBack = + new Point2D.Double( + projectedPoint.getX() + viewCenter.getX(), projectedPoint.getY() + viewCenter.getY()); + return translatedBack; + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MutableAffineTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MutableAffineTransformer.java index c987e540..f6d14a03 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MutableAffineTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MutableAffineTransformer.java @@ -10,209 +10,198 @@ package edu.uci.ics.jung.visualization.transform; +import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer; +import edu.uci.ics.jung.visualization.util.ChangeEventSupport; +import edu.uci.ics.jung.visualization.util.DefaultChangeEventSupport; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; - import javax.swing.event.ChangeListener; import javax.swing.event.EventListenerList; -import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer; -import edu.uci.ics.jung.visualization.util.ChangeEventSupport; -import edu.uci.ics.jung.visualization.util.DefaultChangeEventSupport; - /** + * Provides methods to mutate the AffineTransform used by AffineTransformer base class to map points + * from one coordinate system to another. * - * Provides methods to mutate the AffineTransform used by AffineTransformer - * base class to map points from one coordinate system to - * another. - * - * * @author Tom Nelson - * - * */ -public class MutableAffineTransformer extends AffineTransformer -implements MutableTransformer, ShapeTransformer, ChangeEventSupport { - - protected ChangeEventSupport changeSupport = - new DefaultChangeEventSupport(this); - - /** - * create an instance that does not transform points - */ - public MutableAffineTransformer() { - // nothing left to do - } - - /** - * Create an instance with the supplied transform - * @param transform the transform to use - */ - public MutableAffineTransformer(AffineTransform transform) { - super(transform); - } - - public String toString() { - return "MutableAffineTransformer using "+transform; - } - - /** - * setter for the scale - * fires a PropertyChangeEvent with the AffineTransforms representing - * the previous and new values for scale and offset - * @param scalex the amount to scale in the x direction - * @param scaley the amount to scale in the y direction - * @param from the point to transform - */ - public void scale(double scalex, double scaley, Point2D from) { - AffineTransform xf = AffineTransform.getTranslateInstance(from.getX(),from.getY()); - xf.scale(scalex, scaley); - xf.translate(-from.getX(), -from.getY()); - inverse = null; - transform.preConcatenate(xf); - fireStateChanged(); - } - - /** - * setter for the scale - * fires a PropertyChangeEvent with the AffineTransforms representing - * the previous and new values for scale and offset - * @param scalex the amount to scale in the x direction - * @param scaley the amount to scale in the y direction - * @param from the point to transform - */ - public void setScale(double scalex, double scaley, Point2D from) { - transform.setToIdentity(); - scale(scalex, scaley, from); - } - - /** - * shears the transform by passed parameters - * @param shx x value to shear - * @param shy y value to shear - * @param from the point to transform - */ - public void shear(double shx, double shy, Point2D from) { - inverse = null; - AffineTransform at = - AffineTransform.getTranslateInstance(from.getX(), from.getY()); - at.shear(shx, shy); - at.translate(-from.getX(), -from.getY()); - transform.preConcatenate(at); - fireStateChanged(); - } - - /** - * Replace the Transform's translate x and y values - * with the passed values, leaving the scale values - * unchanged. - * @param tx the x value of the translation - * @param ty the y value of the translation - */ - public void setTranslate(double tx, double ty) { - float scalex = (float) transform.getScaleX(); - float scaley = (float) transform.getScaleY(); - float shearx = (float) transform.getShearX(); - float sheary = (float) transform.getShearY(); - inverse = null; - transform.setTransform(scalex, - sheary, - shearx, - scaley, - tx, ty); - fireStateChanged(); - } - - /** - * Apply the passed values to the current Transform - * @param offsetx the x-value - * @param offsety the y-value - */ - public void translate(double offsetx, double offsety) { - inverse = null; - transform.translate(offsetx, offsety); - fireStateChanged(); - } - - /** - * preconcatenates the rotation at the supplied point with the current transform - * @param theta the angle by which to rotate the point - * @param from the point to transform - */ - public void rotate(double theta, Point2D from) { - AffineTransform rotate = - AffineTransform.getRotateInstance(theta, from.getX(), from.getY()); - inverse = null; - transform.preConcatenate(rotate); - - fireStateChanged(); - } - - /** - * rotates the current transform at the supplied points - * @param radians angle by which to rotate the supplied coordinates - * @param x the x coordinate of the point to transform - * @param y the y coordinate of the point to transform - */ - public void rotate(double radians, double x, double y) { - inverse = null; - transform.rotate(radians, x, y); - fireStateChanged(); - } - - public void concatenate(AffineTransform xform) { - inverse = null; - transform.concatenate(xform); - fireStateChanged(); - - } - public void preConcatenate(AffineTransform xform) { - inverse = null; - transform.preConcatenate(xform); - fireStateChanged(); - } - - - /** - * Adds a ChangeListener. - * @param l the listener to be added - */ - public void addChangeListener(ChangeListener l) { - changeSupport.addChangeListener(l); - } - - /** - * Removes a ChangeListener. - * @param l the listener to be removed - */ - public void removeChangeListener(ChangeListener l) { - changeSupport.removeChangeListener(l); - } - - /** - * Returns an array of all the ChangeListeners added - * with addChangeListener(). - * - * @return all of the ChangeListeners added or an empty - * array if no listeners have been added - */ - public ChangeListener[] getChangeListeners() { - return changeSupport.getChangeListeners(); - } - - /** - * Notifies all listeners that have registered interest for - * notification on this event type. The event instance - * is lazily created. - * @see EventListenerList - */ - public void fireStateChanged() { - changeSupport.fireStateChanged(); - } - - public void setToIdentity() { - inverse = null; - transform.setToIdentity(); - fireStateChanged(); - } +public class MutableAffineTransformer extends AffineTransformer + implements MutableTransformer, ShapeTransformer, ChangeEventSupport { + + protected ChangeEventSupport changeSupport = new DefaultChangeEventSupport(this); + + /** create an instance that does not transform points */ + public MutableAffineTransformer() { + // nothing left to do + } + + /** + * Create an instance with the supplied transform + * + * @param transform the transform to use + */ + public MutableAffineTransformer(AffineTransform transform) { + super(transform); + } + + public String toString() { + return "MutableAffineTransformer using " + transform; + } + + /** + * setter for the scale fires a PropertyChangeEvent with the AffineTransforms representing the + * previous and new values for scale and offset + * + * @param scalex the amount to scale in the x direction + * @param scaley the amount to scale in the y direction + * @param from the point to transform + */ + public void scale(double scalex, double scaley, Point2D from) { + AffineTransform xf = AffineTransform.getTranslateInstance(from.getX(), from.getY()); + xf.scale(scalex, scaley); + xf.translate(-from.getX(), -from.getY()); + inverse = null; + transform.preConcatenate(xf); + fireStateChanged(); + } + + /** + * setter for the scale fires a PropertyChangeEvent with the AffineTransforms representing the + * previous and new values for scale and offset + * + * @param scalex the amount to scale in the x direction + * @param scaley the amount to scale in the y direction + * @param from the point to transform + */ + public void setScale(double scalex, double scaley, Point2D from) { + transform.setToIdentity(); + scale(scalex, scaley, from); + } + + /** + * shears the transform by passed parameters + * + * @param shx x value to shear + * @param shy y value to shear + * @param from the point to transform + */ + public void shear(double shx, double shy, Point2D from) { + inverse = null; + AffineTransform at = AffineTransform.getTranslateInstance(from.getX(), from.getY()); + at.shear(shx, shy); + at.translate(-from.getX(), -from.getY()); + transform.preConcatenate(at); + fireStateChanged(); + } + + /** + * Replace the Transform's translate x and y values with the passed values, leaving the scale + * values unchanged. + * + * @param tx the x value of the translation + * @param ty the y value of the translation + */ + public void setTranslate(double tx, double ty) { + float scalex = (float) transform.getScaleX(); + float scaley = (float) transform.getScaleY(); + float shearx = (float) transform.getShearX(); + float sheary = (float) transform.getShearY(); + inverse = null; + transform.setTransform(scalex, sheary, shearx, scaley, tx, ty); + fireStateChanged(); + } + + /** + * Apply the passed values to the current Transform + * + * @param offsetx the x-value + * @param offsety the y-value + */ + public void translate(double offsetx, double offsety) { + inverse = null; + transform.translate(offsetx, offsety); + fireStateChanged(); + } + + /** + * preconcatenates the rotation at the supplied point with the current transform + * + * @param theta the angle by which to rotate the point + * @param from the point to transform + */ + public void rotate(double theta, Point2D from) { + AffineTransform rotate = AffineTransform.getRotateInstance(theta, from.getX(), from.getY()); + inverse = null; + transform.preConcatenate(rotate); + + fireStateChanged(); + } + + /** + * rotates the current transform at the supplied points + * + * @param radians angle by which to rotate the supplied coordinates + * @param x the x coordinate of the point to transform + * @param y the y coordinate of the point to transform + */ + public void rotate(double radians, double x, double y) { + inverse = null; + transform.rotate(radians, x, y); + fireStateChanged(); + } + + public void concatenate(AffineTransform xform) { + inverse = null; + transform.concatenate(xform); + fireStateChanged(); + } + + public void preConcatenate(AffineTransform xform) { + inverse = null; + transform.preConcatenate(xform); + fireStateChanged(); + } + + /** + * Adds a ChangeListener. + * + * @param l the listener to be added + */ + public void addChangeListener(ChangeListener l) { + changeSupport.addChangeListener(l); + } + + /** + * Removes a ChangeListener. + * + * @param l the listener to be removed + */ + public void removeChangeListener(ChangeListener l) { + changeSupport.removeChangeListener(l); + } + + /** + * Returns an array of all the ChangeListeners added with addChangeListener(). + * + * @return all of the ChangeListeners added or an empty array if no listeners have + * been added + */ + public ChangeListener[] getChangeListeners() { + return changeSupport.getChangeListeners(); + } + + /** + * Notifies all listeners that have registered interest for notification on this event type. The + * event instance is lazily created. + * + * @see EventListenerList + */ + public void fireStateChanged() { + changeSupport.fireStateChanged(); + } + + public void setToIdentity() { + inverse = null; + transform.setToIdentity(); + fireStateChanged(); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MutableTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MutableTransformer.java index c446843a..74f4295c 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MutableTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MutableTransformer.java @@ -10,58 +10,54 @@ package edu.uci.ics.jung.visualization.transform; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; - import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer; import edu.uci.ics.jung.visualization.util.ChangeEventSupport; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; /** - * Provides an API for the mutation of a Function - * and for adding listeners for changes on the Function - * - * @author Tom Nelson - * + * Provides an API for the mutation of a Function and for adding listeners for changes on the + * Function * + * @author Tom Nelson */ public interface MutableTransformer extends ShapeTransformer, ChangeEventSupport { - - void translate(double dx, double dy); - - void setTranslate(double dx, double dy); - - void scale(double sx, double sy, Point2D point); - - void setScale(double sx, double sy, Point2D point); - - void rotate(double radians, Point2D point); - - void rotate(double radians, double x, double y); - - void shear(double shx, double shy, Point2D from); - - void concatenate(AffineTransform transform); - - void preConcatenate(AffineTransform transform); - - double getScaleX(); - - double getScaleY(); - - double getScale(); - - double getTranslateX(); - - double getTranslateY(); - - double getShearX(); - - double getShearY(); - - AffineTransform getTransform(); - - void setToIdentity(); - - double getRotation(); - + + void translate(double dx, double dy); + + void setTranslate(double dx, double dy); + + void scale(double sx, double sy, Point2D point); + + void setScale(double sx, double sy, Point2D point); + + void rotate(double radians, Point2D point); + + void rotate(double radians, double x, double y); + + void shear(double shx, double shy, Point2D from); + + void concatenate(AffineTransform transform); + + void preConcatenate(AffineTransform transform); + + double getScaleX(); + + double getScaleY(); + + double getScale(); + + double getTranslateX(); + + double getTranslateY(); + + double getShearX(); + + double getShearY(); + + AffineTransform getTransform(); + + void setToIdentity(); + + double getRotation(); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MutableTransformerDecorator.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MutableTransformerDecorator.java index 36701bed..a6a70784 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MutableTransformerDecorator.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/MutableTransformerDecorator.java @@ -1,151 +1,148 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.visualization.transform; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; - import javax.swing.event.ChangeListener; /** - * a complete decorator that wraps a MutableTransformer. Subclasses - * use this to allow them to only declare methods they need to change. - * - * @author Tom Nelson + * a complete decorator that wraps a MutableTransformer. Subclasses use this to allow them to only + * declare methods they need to change. * + * @author Tom Nelson */ public abstract class MutableTransformerDecorator implements MutableTransformer { - - protected MutableTransformer delegate; - - public MutableTransformerDecorator(MutableTransformer delegate) { - if(delegate == null) { - delegate = new MutableAffineTransformer(); - } - this.delegate = delegate; - } - - public MutableTransformer getDelegate() { - return delegate; - } - - public void setDelegate(MutableTransformer delegate) { - this.delegate = delegate; - } - - public void addChangeListener(ChangeListener l) { - delegate.addChangeListener(l); - } - - public void concatenate(AffineTransform transform) { - delegate.concatenate(transform); - } - - public void fireStateChanged() { - delegate.fireStateChanged(); - } - - public ChangeListener[] getChangeListeners() { - return delegate.getChangeListeners(); - } - - public double getScale() { - return delegate.getScale(); - } - - public double getScaleX() { - return delegate.getScaleX(); - } - - public double getScaleY() { - return delegate.getScaleY(); - } - - public double getShearX() { - return delegate.getShearX(); - } - - public double getShearY() { - return delegate.getShearY(); - } - - public AffineTransform getTransform() { - return delegate.getTransform(); - } - - public double getTranslateX() { - return delegate.getTranslateX(); - } - - public double getTranslateY() { - return delegate.getTranslateY(); - } - - public Point2D inverseTransform(Point2D p) { - return delegate.inverseTransform(p); - } - - public Shape inverseTransform(Shape shape) { - return delegate.inverseTransform(shape); - } - - public void preConcatenate(AffineTransform transform) { - delegate.preConcatenate(transform); - } - - public void removeChangeListener(ChangeListener l) { - delegate.removeChangeListener(l); - } - - public void rotate(double radians, Point2D point) { - delegate.rotate(radians, point); - } - - public void scale(double sx, double sy, Point2D point) { - delegate.scale(sx, sy, point); - } - - public void setScale(double sx, double sy, Point2D point) { - delegate.setScale(sx, sy, point); - } - - public void setToIdentity() { - delegate.setToIdentity(); - } - - public void setTranslate(double dx, double dy) { - delegate.setTranslate(dx, dy); - } - - public void shear(double shx, double shy, Point2D from) { - delegate.shear(shx, shy, from); - } - - public Point2D transform(Point2D p) { - return delegate.transform(p); - } - - public Shape transform(Shape shape) { - return delegate.transform(shape); - } - public void translate(double dx, double dy) { - delegate.translate(dx, dy); - } + protected MutableTransformer delegate; - public double getRotation() { - return delegate.getRotation(); + public MutableTransformerDecorator(MutableTransformer delegate) { + if (delegate == null) { + delegate = new MutableAffineTransformer(); } + this.delegate = delegate; + } - public void rotate(double radians, double x, double y) { - delegate.rotate(radians, x, y); - } + public MutableTransformer getDelegate() { + return delegate; + } + + public void setDelegate(MutableTransformer delegate) { + this.delegate = delegate; + } + + public void addChangeListener(ChangeListener l) { + delegate.addChangeListener(l); + } + + public void concatenate(AffineTransform transform) { + delegate.concatenate(transform); + } + + public void fireStateChanged() { + delegate.fireStateChanged(); + } + + public ChangeListener[] getChangeListeners() { + return delegate.getChangeListeners(); + } + + public double getScale() { + return delegate.getScale(); + } + + public double getScaleX() { + return delegate.getScaleX(); + } + + public double getScaleY() { + return delegate.getScaleY(); + } + + public double getShearX() { + return delegate.getShearX(); + } + + public double getShearY() { + return delegate.getShearY(); + } + + public AffineTransform getTransform() { + return delegate.getTransform(); + } + + public double getTranslateX() { + return delegate.getTranslateX(); + } + + public double getTranslateY() { + return delegate.getTranslateY(); + } + + public Point2D inverseTransform(Point2D p) { + return delegate.inverseTransform(p); + } + + public Shape inverseTransform(Shape shape) { + return delegate.inverseTransform(shape); + } + + public void preConcatenate(AffineTransform transform) { + delegate.preConcatenate(transform); + } + + public void removeChangeListener(ChangeListener l) { + delegate.removeChangeListener(l); + } + + public void rotate(double radians, Point2D point) { + delegate.rotate(radians, point); + } + + public void scale(double sx, double sy, Point2D point) { + delegate.scale(sx, sy, point); + } + + public void setScale(double sx, double sy, Point2D point) { + delegate.setScale(sx, sy, point); + } + + public void setToIdentity() { + delegate.setToIdentity(); + } + + public void setTranslate(double dx, double dy) { + delegate.setTranslate(dx, dy); + } + + public void shear(double shx, double shy, Point2D from) { + delegate.shear(shx, shy, from); + } + + public Point2D transform(Point2D p) { + return delegate.transform(p); + } + + public Shape transform(Shape shape) { + return delegate.transform(shape); + } + + public void translate(double dx, double dy) { + delegate.translate(dx, dy); + } + + public double getRotation() { + return delegate.getRotation(); + } + public void rotate(double radians, double x, double y) { + delegate.rotate(radians, x, y); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/Graphics2DWrapper.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/Graphics2DWrapper.java index 6e368514..09c44542 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/Graphics2DWrapper.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/Graphics2DWrapper.java @@ -36,639 +36,655 @@ import java.text.AttributedCharacterIterator; import java.util.Map; - /** - * a complete wrapping of Graphics2D, useful as a base class. - * Contains no additional methods, other than direct calls - * to the delegate. - * - * @see GraphicsDecorator as an example subclass that - * adds additional methods. - * - * @author Tom Nelson - * + * a complete wrapping of Graphics2D, useful as a base class. Contains no additional methods, other + * than direct calls to the delegate. * + * @see GraphicsDecorator as an example subclass that adds additional methods. + * @author Tom Nelson */ public class Graphics2DWrapper { - - protected Graphics2D delegate; - - public Graphics2DWrapper() { - this(null); - } - public Graphics2DWrapper(Graphics2D delegate) { - this.delegate = delegate; - } - - public void setDelegate(Graphics2D delegate) { - this.delegate = delegate; - } - - public Graphics2D getDelegate() { - return delegate; - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#addRenderingHints(java.util.Map) - */ - public void addRenderingHints(Map hints) { - delegate.addRenderingHints(hints); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#clearRect(int, int, int, int) - */ - public void clearRect(int x, int y, int width, int height) { - delegate.clearRect(x, y, width, height); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#clip(java.awt.Shape) - */ - public void clip(Shape s) { - delegate.clip(s); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#clipRect(int, int, int, int) - */ - public void clipRect(int x, int y, int width, int height) { - delegate.clipRect(x, y, width, height); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#copyArea(int, int, int, int, int, int) - */ - public void copyArea(int x, int y, int width, int height, int dx, int dy) { - delegate.copyArea(x, y, width, height, dx, dy); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#create() - */ - public Graphics create() { - return delegate.create(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#create(int, int, int, int) - */ - public Graphics create(int x, int y, int width, int height) { - return delegate.create(x, y, width, height); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#dispose() - */ - public void dispose() { - delegate.dispose(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#draw(java.awt.Shape) - */ - public void draw(Shape s) { - delegate.draw(s); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#draw3DRect(int, int, int, int, boolean) - */ - public void draw3DRect(int x, int y, int width, int height, boolean raised) { - delegate.draw3DRect(x, y, width, height, raised); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawArc(int, int, int, int, int, int) - */ - public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { - delegate.drawArc(x, y, width, height, startAngle, arcAngle); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawBytes(byte[], int, int, int, int) - */ - public void drawBytes(byte[] data, int offset, int length, int x, int y) { - delegate.drawBytes(data, offset, length, x, y); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawChars(char[], int, int, int, int) - */ - public void drawChars(char[] data, int offset, int length, int x, int y) { - delegate.drawChars(data, offset, length, x, y); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#drawGlyphVector(java.awt.font.GlyphVector, float, float) - */ - public void drawGlyphVector(GlyphVector g, float x, float y) { - delegate.drawGlyphVector(g, x, y); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#drawImage(java.awt.image.BufferedImage, java.awt.image.BufferedImageOp, int, int) - */ - public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { - delegate.drawImage(img, op, x, y); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#drawImage(java.awt.Image, java.awt.geom.AffineTransform, java.awt.image.ImageObserver) - */ - public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { - return delegate.drawImage(img, xform, obs); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, java.awt.Color, java.awt.image.ImageObserver) - */ - public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { - return delegate.drawImage(img, x, y, bgcolor, observer); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, java.awt.image.ImageObserver) - */ - public boolean drawImage(Image img, int x, int y, ImageObserver observer) { - return delegate.drawImage(img, x, y, observer); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, java.awt.Color, java.awt.image.ImageObserver) - */ - public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { - return delegate.drawImage(img, x, y, width, height, bgcolor, observer); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, java.awt.image.ImageObserver) - */ - public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { - return delegate.drawImage(img, x, y, width, height, observer); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, int, int, int, int, java.awt.Color, java.awt.image.ImageObserver) - */ - public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) { - return delegate.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, int, int, int, int, java.awt.image.ImageObserver) - */ - public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { - return delegate.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawLine(int, int, int, int) - */ - public void drawLine(int x1, int y1, int x2, int y2) { - delegate.drawLine(x1, y1, x2, y2); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawOval(int, int, int, int) - */ - public void drawOval(int x, int y, int width, int height) { - delegate.drawOval(x, y, width, height); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawPolygon(int[], int[], int) - */ - public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { - delegate.drawPolygon(xPoints, yPoints, nPoints); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawPolygon(java.awt.Polygon) - */ - public void drawPolygon(Polygon p) { - delegate.drawPolygon(p); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawPolyline(int[], int[], int) - */ - public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { - delegate.drawPolyline(xPoints, yPoints, nPoints); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawRect(int, int, int, int) - */ - public void drawRect(int x, int y, int width, int height) { - delegate.drawRect(x, y, width, height); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#drawRenderableImage(java.awt.image.renderable.RenderableImage, java.awt.geom.AffineTransform) - */ - public void drawRenderableImage(RenderableImage img, AffineTransform xform) { - delegate.drawRenderableImage(img, xform); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#drawRenderedImage(java.awt.image.RenderedImage, java.awt.geom.AffineTransform) - */ - public void drawRenderedImage(RenderedImage img, AffineTransform xform) { - delegate.drawRenderedImage(img, xform); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#drawRoundRect(int, int, int, int, int, int) - */ - public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { - delegate.drawRoundRect(x, y, width, height, arcWidth, arcHeight); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float) - */ - public void drawString(AttributedCharacterIterator iterator, float x, float y) { - delegate.drawString(iterator, x, y); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, int, int) - */ - public void drawString(AttributedCharacterIterator iterator, int x, int y) { - delegate.drawString(iterator, x, y); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#drawString(java.lang.String, float, float) - */ - public void drawString(String s, float x, float y) { - delegate.drawString(s, x, y); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#drawString(java.lang.String, int, int) - */ - public void drawString(String str, int x, int y) { - delegate.drawString(str, x, y); - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - public boolean equals(Object obj) { - return delegate.equals(obj); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#fill(java.awt.Shape) - */ - public void fill(Shape s) { - delegate.fill(s); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#fill3DRect(int, int, int, int, boolean) - */ - public void fill3DRect(int x, int y, int width, int height, boolean raised) { - delegate.fill3DRect(x, y, width, height, raised); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#fillArc(int, int, int, int, int, int) - */ - public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { - delegate.fillArc(x, y, width, height, startAngle, arcAngle); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#fillOval(int, int, int, int) - */ - public void fillOval(int x, int y, int width, int height) { - delegate.fillOval(x, y, width, height); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#fillPolygon(int[], int[], int) - */ - public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { - delegate.fillPolygon(xPoints, yPoints, nPoints); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#fillPolygon(java.awt.Polygon) - */ - public void fillPolygon(Polygon p) { - delegate.fillPolygon(p); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#fillRect(int, int, int, int) - */ - public void fillRect(int x, int y, int width, int height) { - delegate.fillRect(x, y, width, height); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#fillRoundRect(int, int, int, int, int, int) - */ - public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { - delegate.fillRoundRect(x, y, width, height, arcWidth, arcHeight); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#finalize() - */ - public void finalize() { - delegate.finalize(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#getBackground() - */ - public Color getBackground() { - return delegate.getBackground(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#getClip() - */ - public Shape getClip() { - return delegate.getClip(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#getClipBounds() - */ - public Rectangle getClipBounds() { - return delegate.getClipBounds(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#getClipBounds(java.awt.Rectangle) - */ - public Rectangle getClipBounds(Rectangle r) { - return delegate.getClipBounds(r); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#getClipRect() - */ - @SuppressWarnings("deprecation") - public Rectangle getClipRect() { - return delegate.getClipRect(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#getColor() - */ - public Color getColor() { - return delegate.getColor(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#getComposite() - */ - public Composite getComposite() { - return delegate.getComposite(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#getDeviceConfiguration() - */ - public GraphicsConfiguration getDeviceConfiguration() { - return delegate.getDeviceConfiguration(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#getFont() - */ - public Font getFont() { - return delegate.getFont(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#getFontMetrics() - */ - public FontMetrics getFontMetrics() { - return delegate.getFontMetrics(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#getFontMetrics(java.awt.Font) - */ - public FontMetrics getFontMetrics(Font f) { - return delegate.getFontMetrics(f); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#getFontRenderContext() - */ - public FontRenderContext getFontRenderContext() { - return delegate.getFontRenderContext(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#getPaint() - */ - public Paint getPaint() { - return delegate.getPaint(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#getRenderingHint(java.awt.RenderingHints.Key) - */ - public Object getRenderingHint(Key hintKey) { - return delegate.getRenderingHint(hintKey); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#getRenderingHints() - */ - public RenderingHints getRenderingHints() { - return delegate.getRenderingHints(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#getStroke() - */ - public Stroke getStroke() { - return delegate.getStroke(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#getTransform() - */ - public AffineTransform getTransform() { - return delegate.getTransform(); - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - public int hashCode() { - return delegate.hashCode(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#hit(java.awt.Rectangle, java.awt.Shape, boolean) - */ - public boolean hit(Rectangle rect, Shape s, boolean onStroke) { - return delegate.hit(rect, s, onStroke); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#hitClip(int, int, int, int) - */ - public boolean hitClip(int x, int y, int width, int height) { - return delegate.hitClip(x, y, width, height); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#rotate(double, double, double) - */ - public void rotate(double theta, double x, double y) { - delegate.rotate(theta, x, y); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#rotate(double) - */ - public void rotate(double theta) { - delegate.rotate(theta); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#scale(double, double) - */ - public void scale(double sx, double sy) { - delegate.scale(sx, sy); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#setBackground(java.awt.Color) - */ - public void setBackground(Color color) { - delegate.setBackground(color); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#setClip(int, int, int, int) - */ - public void setClip(int x, int y, int width, int height) { - delegate.setClip(x, y, width, height); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#setClip(java.awt.Shape) - */ - public void setClip(Shape clip) { - delegate.setClip(clip); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#setColor(java.awt.Color) - */ - public void setColor(Color c) { - delegate.setColor(c); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#setComposite(java.awt.Composite) - */ - public void setComposite(Composite comp) { - delegate.setComposite(comp); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#setFont(java.awt.Font) - */ - public void setFont(Font font) { - delegate.setFont(font); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#setPaint(java.awt.Paint) - */ - public void setPaint(Paint paint) { - delegate.setPaint(paint); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#setPaintMode() - */ - public void setPaintMode() { - delegate.setPaintMode(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#setRenderingHint(java.awt.RenderingHints.Key, java.lang.Object) - */ - public void setRenderingHint(Key hintKey, Object hintValue) { - delegate.setRenderingHint(hintKey, hintValue); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#setRenderingHints(java.util.Map) - */ - public void setRenderingHints(Map hints) { - delegate.setRenderingHints(hints); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#setStroke(java.awt.Stroke) - */ - public void setStroke(Stroke s) { - delegate.setStroke(s); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#setTransform(java.awt.geom.AffineTransform) - */ - public void setTransform(AffineTransform Tx) { - delegate.setTransform(Tx); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#setXORMode(java.awt.Color) - */ - public void setXORMode(Color c1) { - delegate.setXORMode(c1); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#shear(double, double) - */ - public void shear(double shx, double shy) { - delegate.shear(shx, shy); - } - - /* (non-Javadoc) - * @see java.awt.Graphics#toString() - */ - public String toString() { - return delegate.toString(); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#transform(java.awt.geom.AffineTransform) - */ - public void transform(AffineTransform Tx) { - delegate.transform(Tx); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#translate(double, double) - */ - public void translate(double tx, double ty) { - delegate.translate(tx, ty); - } - - /* (non-Javadoc) - * @see java.awt.Graphics2D#translate(int, int) - */ - public void translate(int x, int y) { - delegate.translate(x, y); - } + protected Graphics2D delegate; + + public Graphics2DWrapper() { + this(null); + } + + public Graphics2DWrapper(Graphics2D delegate) { + this.delegate = delegate; + } + + public void setDelegate(Graphics2D delegate) { + this.delegate = delegate; + } + + public Graphics2D getDelegate() { + return delegate; + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#addRenderingHints(java.util.Map) + */ + public void addRenderingHints(Map hints) { + delegate.addRenderingHints(hints); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#clearRect(int, int, int, int) + */ + public void clearRect(int x, int y, int width, int height) { + delegate.clearRect(x, y, width, height); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#clip(java.awt.Shape) + */ + public void clip(Shape s) { + delegate.clip(s); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#clipRect(int, int, int, int) + */ + public void clipRect(int x, int y, int width, int height) { + delegate.clipRect(x, y, width, height); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#copyArea(int, int, int, int, int, int) + */ + public void copyArea(int x, int y, int width, int height, int dx, int dy) { + delegate.copyArea(x, y, width, height, dx, dy); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#create() + */ + public Graphics create() { + return delegate.create(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#create(int, int, int, int) + */ + public Graphics create(int x, int y, int width, int height) { + return delegate.create(x, y, width, height); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#dispose() + */ + public void dispose() { + delegate.dispose(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#draw(java.awt.Shape) + */ + public void draw(Shape s) { + delegate.draw(s); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#draw3DRect(int, int, int, int, boolean) + */ + public void draw3DRect(int x, int y, int width, int height, boolean raised) { + delegate.draw3DRect(x, y, width, height, raised); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawArc(int, int, int, int, int, int) + */ + public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { + delegate.drawArc(x, y, width, height, startAngle, arcAngle); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawBytes(byte[], int, int, int, int) + */ + public void drawBytes(byte[] data, int offset, int length, int x, int y) { + delegate.drawBytes(data, offset, length, x, y); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawChars(char[], int, int, int, int) + */ + public void drawChars(char[] data, int offset, int length, int x, int y) { + delegate.drawChars(data, offset, length, x, y); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#drawGlyphVector(java.awt.font.GlyphVector, float, float) + */ + public void drawGlyphVector(GlyphVector g, float x, float y) { + delegate.drawGlyphVector(g, x, y); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#drawImage(java.awt.image.BufferedImage, java.awt.image.BufferedImageOp, int, int) + */ + public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { + delegate.drawImage(img, op, x, y); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#drawImage(java.awt.Image, java.awt.geom.AffineTransform, java.awt.image.ImageObserver) + */ + public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { + return delegate.drawImage(img, xform, obs); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, java.awt.Color, java.awt.image.ImageObserver) + */ + public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { + return delegate.drawImage(img, x, y, bgcolor, observer); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, java.awt.image.ImageObserver) + */ + public boolean drawImage(Image img, int x, int y, ImageObserver observer) { + return delegate.drawImage(img, x, y, observer); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, java.awt.Color, java.awt.image.ImageObserver) + */ + public boolean drawImage( + Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { + return delegate.drawImage(img, x, y, width, height, bgcolor, observer); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, java.awt.image.ImageObserver) + */ + public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { + return delegate.drawImage(img, x, y, width, height, observer); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, int, int, int, int, java.awt.Color, java.awt.image.ImageObserver) + */ + public boolean drawImage( + Image img, + int dx1, + int dy1, + int dx2, + int dy2, + int sx1, + int sy1, + int sx2, + int sy2, + Color bgcolor, + ImageObserver observer) { + return delegate.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, int, int, int, int, java.awt.image.ImageObserver) + */ + public boolean drawImage( + Image img, + int dx1, + int dy1, + int dx2, + int dy2, + int sx1, + int sy1, + int sx2, + int sy2, + ImageObserver observer) { + return delegate.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawLine(int, int, int, int) + */ + public void drawLine(int x1, int y1, int x2, int y2) { + delegate.drawLine(x1, y1, x2, y2); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawOval(int, int, int, int) + */ + public void drawOval(int x, int y, int width, int height) { + delegate.drawOval(x, y, width, height); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawPolygon(int[], int[], int) + */ + public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { + delegate.drawPolygon(xPoints, yPoints, nPoints); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawPolygon(java.awt.Polygon) + */ + public void drawPolygon(Polygon p) { + delegate.drawPolygon(p); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawPolyline(int[], int[], int) + */ + public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { + delegate.drawPolyline(xPoints, yPoints, nPoints); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawRect(int, int, int, int) + */ + public void drawRect(int x, int y, int width, int height) { + delegate.drawRect(x, y, width, height); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#drawRenderableImage(java.awt.image.renderable.RenderableImage, java.awt.geom.AffineTransform) + */ + public void drawRenderableImage(RenderableImage img, AffineTransform xform) { + delegate.drawRenderableImage(img, xform); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#drawRenderedImage(java.awt.image.RenderedImage, java.awt.geom.AffineTransform) + */ + public void drawRenderedImage(RenderedImage img, AffineTransform xform) { + delegate.drawRenderedImage(img, xform); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#drawRoundRect(int, int, int, int, int, int) + */ + public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + delegate.drawRoundRect(x, y, width, height, arcWidth, arcHeight); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float) + */ + public void drawString(AttributedCharacterIterator iterator, float x, float y) { + delegate.drawString(iterator, x, y); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, int, int) + */ + public void drawString(AttributedCharacterIterator iterator, int x, int y) { + delegate.drawString(iterator, x, y); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#drawString(java.lang.String, float, float) + */ + public void drawString(String s, float x, float y) { + delegate.drawString(s, x, y); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#drawString(java.lang.String, int, int) + */ + public void drawString(String str, int x, int y) { + delegate.drawString(str, x, y); + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + return delegate.equals(obj); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#fill(java.awt.Shape) + */ + public void fill(Shape s) { + delegate.fill(s); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#fill3DRect(int, int, int, int, boolean) + */ + public void fill3DRect(int x, int y, int width, int height, boolean raised) { + delegate.fill3DRect(x, y, width, height, raised); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#fillArc(int, int, int, int, int, int) + */ + public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { + delegate.fillArc(x, y, width, height, startAngle, arcAngle); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#fillOval(int, int, int, int) + */ + public void fillOval(int x, int y, int width, int height) { + delegate.fillOval(x, y, width, height); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#fillPolygon(int[], int[], int) + */ + public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { + delegate.fillPolygon(xPoints, yPoints, nPoints); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#fillPolygon(java.awt.Polygon) + */ + public void fillPolygon(Polygon p) { + delegate.fillPolygon(p); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#fillRect(int, int, int, int) + */ + public void fillRect(int x, int y, int width, int height) { + delegate.fillRect(x, y, width, height); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#fillRoundRect(int, int, int, int, int, int) + */ + public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + delegate.fillRoundRect(x, y, width, height, arcWidth, arcHeight); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#finalize() + */ + public void finalize() { + delegate.finalize(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#getBackground() + */ + public Color getBackground() { + return delegate.getBackground(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#getClip() + */ + public Shape getClip() { + return delegate.getClip(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#getClipBounds() + */ + public Rectangle getClipBounds() { + return delegate.getClipBounds(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#getClipBounds(java.awt.Rectangle) + */ + public Rectangle getClipBounds(Rectangle r) { + return delegate.getClipBounds(r); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#getClipRect() + */ + @SuppressWarnings("deprecation") + public Rectangle getClipRect() { + return delegate.getClipRect(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#getColor() + */ + public Color getColor() { + return delegate.getColor(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#getComposite() + */ + public Composite getComposite() { + return delegate.getComposite(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#getDeviceConfiguration() + */ + public GraphicsConfiguration getDeviceConfiguration() { + return delegate.getDeviceConfiguration(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#getFont() + */ + public Font getFont() { + return delegate.getFont(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#getFontMetrics() + */ + public FontMetrics getFontMetrics() { + return delegate.getFontMetrics(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#getFontMetrics(java.awt.Font) + */ + public FontMetrics getFontMetrics(Font f) { + return delegate.getFontMetrics(f); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#getFontRenderContext() + */ + public FontRenderContext getFontRenderContext() { + return delegate.getFontRenderContext(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#getPaint() + */ + public Paint getPaint() { + return delegate.getPaint(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#getRenderingHint(java.awt.RenderingHints.Key) + */ + public Object getRenderingHint(Key hintKey) { + return delegate.getRenderingHint(hintKey); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#getRenderingHints() + */ + public RenderingHints getRenderingHints() { + return delegate.getRenderingHints(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#getStroke() + */ + public Stroke getStroke() { + return delegate.getStroke(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#getTransform() + */ + public AffineTransform getTransform() { + return delegate.getTransform(); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return delegate.hashCode(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#hit(java.awt.Rectangle, java.awt.Shape, boolean) + */ + public boolean hit(Rectangle rect, Shape s, boolean onStroke) { + return delegate.hit(rect, s, onStroke); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#hitClip(int, int, int, int) + */ + public boolean hitClip(int x, int y, int width, int height) { + return delegate.hitClip(x, y, width, height); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#rotate(double, double, double) + */ + public void rotate(double theta, double x, double y) { + delegate.rotate(theta, x, y); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#rotate(double) + */ + public void rotate(double theta) { + delegate.rotate(theta); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#scale(double, double) + */ + public void scale(double sx, double sy) { + delegate.scale(sx, sy); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#setBackground(java.awt.Color) + */ + public void setBackground(Color color) { + delegate.setBackground(color); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#setClip(int, int, int, int) + */ + public void setClip(int x, int y, int width, int height) { + delegate.setClip(x, y, width, height); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#setClip(java.awt.Shape) + */ + public void setClip(Shape clip) { + delegate.setClip(clip); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#setColor(java.awt.Color) + */ + public void setColor(Color c) { + delegate.setColor(c); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#setComposite(java.awt.Composite) + */ + public void setComposite(Composite comp) { + delegate.setComposite(comp); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#setFont(java.awt.Font) + */ + public void setFont(Font font) { + delegate.setFont(font); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#setPaint(java.awt.Paint) + */ + public void setPaint(Paint paint) { + delegate.setPaint(paint); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#setPaintMode() + */ + public void setPaintMode() { + delegate.setPaintMode(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#setRenderingHint(java.awt.RenderingHints.Key, java.lang.Object) + */ + public void setRenderingHint(Key hintKey, Object hintValue) { + delegate.setRenderingHint(hintKey, hintValue); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#setRenderingHints(java.util.Map) + */ + public void setRenderingHints(Map hints) { + delegate.setRenderingHints(hints); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#setStroke(java.awt.Stroke) + */ + public void setStroke(Stroke s) { + delegate.setStroke(s); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#setTransform(java.awt.geom.AffineTransform) + */ + public void setTransform(AffineTransform Tx) { + delegate.setTransform(Tx); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#setXORMode(java.awt.Color) + */ + public void setXORMode(Color c1) { + delegate.setXORMode(c1); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#shear(double, double) + */ + public void shear(double shx, double shy) { + delegate.shear(shx, shy); + } + + /* (non-Javadoc) + * @see java.awt.Graphics#toString() + */ + public String toString() { + return delegate.toString(); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#transform(java.awt.geom.AffineTransform) + */ + public void transform(AffineTransform Tx) { + delegate.transform(Tx); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#translate(double, double) + */ + public void translate(double tx, double ty) { + delegate.translate(tx, ty); + } + + /* (non-Javadoc) + * @see java.awt.Graphics2D#translate(int, int) + */ + public void translate(int x, int y) { + delegate.translate(x, y); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/GraphicsDecorator.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/GraphicsDecorator.java index 2fd8cf7c..ae34bce2 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/GraphicsDecorator.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/GraphicsDecorator.java @@ -13,38 +13,39 @@ import java.awt.Component; import java.awt.Graphics2D; import java.awt.Shape; - import javax.swing.CellRendererPane; import javax.swing.Icon; - /** - * an extendion of Graphics2DWrapper that adds enhanced - * methods for drawing icons and components - * - * @see TransformingGraphics as an example subclass - * - * @author Tom Nelson - * + * an extendion of Graphics2DWrapper that adds enhanced methods for drawing icons and components * + * @see TransformingGraphics as an example subclass + * @author Tom Nelson */ public class GraphicsDecorator extends Graphics2DWrapper { - - public GraphicsDecorator() { - this(null); - } - public GraphicsDecorator(Graphics2D delegate) { - super(delegate); - } - - public void draw(Icon icon, Component c, Shape clip, int x, int y) { - int w = icon.getIconWidth(); - int h = icon.getIconHeight(); - icon.paintIcon(c, delegate, x-w/2, y-h/2); - } - - public void draw(Component c, CellRendererPane rendererPane, - int x, int y, int w, int h, boolean shouldValidate) { - rendererPane.paintComponent(delegate, c, c.getParent(), x, y, w, h, shouldValidate); - } + + public GraphicsDecorator() { + this(null); + } + + public GraphicsDecorator(Graphics2D delegate) { + super(delegate); + } + + public void draw(Icon icon, Component c, Shape clip, int x, int y) { + int w = icon.getIconWidth(); + int h = icon.getIconHeight(); + icon.paintIcon(c, delegate, x - w / 2, y - h / 2); + } + + public void draw( + Component c, + CellRendererPane rendererPane, + int x, + int y, + int w, + int h, + boolean shouldValidate) { + rendererPane.paintComponent(delegate, c, c.getParent(), x, y, w, h, shouldValidate); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/HyperbolicShapeTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/HyperbolicShapeTransformer.java index 3510e440..26118c94 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/HyperbolicShapeTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/HyperbolicShapeTransformer.java @@ -1,226 +1,225 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.visualization.transform.shape; +import edu.uci.ics.jung.algorithms.layout.PolarPoint; +import edu.uci.ics.jung.visualization.transform.HyperbolicTransformer; +import edu.uci.ics.jung.visualization.transform.MutableTransformer; import java.awt.Component; import java.awt.Shape; import java.awt.geom.GeneralPath; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; -import edu.uci.ics.jung.algorithms.layout.PolarPoint; -import edu.uci.ics.jung.visualization.transform.HyperbolicTransformer; -import edu.uci.ics.jung.visualization.transform.MutableTransformer; - - /** - * HyperbolicShapeTransformer extends HyperbolicTransformer and - * adds implementations for methods in ShapeFlatnessTransformer. - * It modifies the shapes (Vertex, Edge, and Arrowheads) so that - * they are distorted by the hyperbolic transformation - * - * @author Tom Nelson - * + * HyperbolicShapeTransformer extends HyperbolicTransformer and adds implementations for methods in + * ShapeFlatnessTransformer. It modifies the shapes (Vertex, Edge, and Arrowheads) so that they are + * distorted by the hyperbolic transformation * + * @author Tom Nelson */ -public class HyperbolicShapeTransformer extends HyperbolicTransformer +public class HyperbolicShapeTransformer extends HyperbolicTransformer implements ShapeFlatnessTransformer { - /** - * Create an instance, setting values from the passed component - * and registering to listen for size changes on the component. - * @param component the component in which rendering takes place - */ - public HyperbolicShapeTransformer(Component component) { - this(component, null); - } - - /** - * Create an instance, setting values from the passed component - * and registering to listen for size changes on the component, - * with a possibly shared transform delegate. - * @param component the component in which rendering takes place - * @param delegate the transformer to use - */ - public HyperbolicShapeTransformer(Component component, MutableTransformer delegate) { - super(component, delegate); - } - - /** - * Transform the supplied shape with the overridden transform - * method so that the shape is distorted by the hyperbolic - * transform. - * @param shape a shape to transform - * @return a GeneralPath for the transformed shape - */ - public Shape transform(Shape shape) { - return transform(shape, 0); - } - public Shape transform(Shape shape, float flatness) { - GeneralPath newPath = new GeneralPath(); - float[] coords = new float[6]; - PathIterator iterator = null; - if(flatness == 0) { - iterator = shape.getPathIterator(null); - } else { - iterator = shape.getPathIterator(null, flatness); - } - for( ; - iterator.isDone() == false; - iterator.next()) { - int type = iterator.currentSegment(coords); - switch(type) { - case PathIterator.SEG_MOVETO: - Point2D p = _transform(new Point2D.Float(coords[0], coords[1])); - newPath.moveTo((float)p.getX(), (float)p.getY()); - break; - - case PathIterator.SEG_LINETO: - p = _transform(new Point2D.Float(coords[0], coords[1])); - newPath.lineTo((float)p.getX(), (float) p.getY()); - break; - - case PathIterator.SEG_QUADTO: - p = _transform(new Point2D.Float(coords[0], coords[1])); - Point2D q = _transform(new Point2D.Float(coords[2], coords[3])); - newPath.quadTo((float)p.getX(), (float)p.getY(), (float)q.getX(), (float)q.getY()); - break; - - case PathIterator.SEG_CUBICTO: - p = _transform(new Point2D.Float(coords[0], coords[1])); - q = _transform(new Point2D.Float(coords[2], coords[3])); - Point2D r = _transform(new Point2D.Float(coords[4], coords[5])); - newPath.curveTo((float)p.getX(), (float)p.getY(), - (float)q.getX(), (float)q.getY(), - (float)r.getX(), (float)r.getY()); - break; - - case PathIterator.SEG_CLOSE: - newPath.closePath(); - break; - - } - } - return newPath; - } + /** + * Create an instance, setting values from the passed component and registering to listen for size + * changes on the component. + * + * @param component the component in which rendering takes place + */ + public HyperbolicShapeTransformer(Component component) { + this(component, null); + } + + /** + * Create an instance, setting values from the passed component and registering to listen for size + * changes on the component, with a possibly shared transform delegate. + * + * @param component the component in which rendering takes place + * @param delegate the transformer to use + */ + public HyperbolicShapeTransformer(Component component, MutableTransformer delegate) { + super(component, delegate); + } + + /** + * Transform the supplied shape with the overridden transform method so that the shape is + * distorted by the hyperbolic transform. + * + * @param shape a shape to transform + * @return a GeneralPath for the transformed shape + */ + public Shape transform(Shape shape) { + return transform(shape, 0); + } - public Shape inverseTransform(Shape shape) { - GeneralPath newPath = new GeneralPath(); - float[] coords = new float[6]; - for(PathIterator iterator=shape.getPathIterator(null); - iterator.isDone() == false; - iterator.next()) { - int type = iterator.currentSegment(coords); - switch(type) { - case PathIterator.SEG_MOVETO: - Point2D p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); - newPath.moveTo((float)p.getX(), (float)p.getY()); - break; - - case PathIterator.SEG_LINETO: - p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); - newPath.lineTo((float)p.getX(), (float) p.getY()); - break; - - case PathIterator.SEG_QUADTO: - p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); - Point2D q = _inverseTransform(new Point2D.Float(coords[2], coords[3])); - newPath.quadTo((float)p.getX(), (float)p.getY(), (float)q.getX(), (float)q.getY()); - break; - - case PathIterator.SEG_CUBICTO: - p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); - q = _inverseTransform(new Point2D.Float(coords[2], coords[3])); - Point2D r = _inverseTransform(new Point2D.Float(coords[4], coords[5])); - newPath.curveTo((float)p.getX(), (float)p.getY(), - (float)q.getX(), (float)q.getY(), - (float)r.getX(), (float)r.getY()); - break; - - case PathIterator.SEG_CLOSE: - newPath.closePath(); - break; - - } - } - return newPath; + public Shape transform(Shape shape, float flatness) { + GeneralPath newPath = new GeneralPath(); + float[] coords = new float[6]; + PathIterator iterator = null; + if (flatness == 0) { + iterator = shape.getPathIterator(null); + } else { + iterator = shape.getPathIterator(null, flatness); } - /** - * override base class transform to project the fisheye effect - */ - private Point2D _transform(Point2D graphPoint) { - if(graphPoint == null) return null; - Point2D viewCenter = getViewCenter(); - double viewRadius = getViewRadius(); - double ratio = getRatio(); - // transform the point from the graph to the view - Point2D viewPoint = graphPoint;//delegate.transform(graphPoint); - // calculate point from center - double dx = viewPoint.getX() - viewCenter.getX(); - double dy = viewPoint.getY() - viewCenter.getY(); - // factor out ellipse - dx *= ratio; - Point2D pointFromCenter = new Point2D.Double(dx, dy); - - PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); - double theta = polar.getTheta(); - double radius = polar.getRadius(); - if(radius > viewRadius) return viewPoint; - - double mag = Math.tan(Math.PI/2*magnification); - radius *= mag; - - radius = Math.min(radius, viewRadius); - radius /= viewRadius; - radius *= Math.PI/2; - radius = Math.abs(Math.atan(radius)); - radius *= viewRadius; - Point2D projectedPoint = PolarPoint.polarToCartesian(theta, radius); - projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY()); - Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(), - projectedPoint.getY()+viewCenter.getY()); - return translatedBack; + for (; iterator.isDone() == false; iterator.next()) { + int type = iterator.currentSegment(coords); + switch (type) { + case PathIterator.SEG_MOVETO: + Point2D p = _transform(new Point2D.Float(coords[0], coords[1])); + newPath.moveTo((float) p.getX(), (float) p.getY()); + break; + + case PathIterator.SEG_LINETO: + p = _transform(new Point2D.Float(coords[0], coords[1])); + newPath.lineTo((float) p.getX(), (float) p.getY()); + break; + + case PathIterator.SEG_QUADTO: + p = _transform(new Point2D.Float(coords[0], coords[1])); + Point2D q = _transform(new Point2D.Float(coords[2], coords[3])); + newPath.quadTo((float) p.getX(), (float) p.getY(), (float) q.getX(), (float) q.getY()); + break; + + case PathIterator.SEG_CUBICTO: + p = _transform(new Point2D.Float(coords[0], coords[1])); + q = _transform(new Point2D.Float(coords[2], coords[3])); + Point2D r = _transform(new Point2D.Float(coords[4], coords[5])); + newPath.curveTo( + (float) p.getX(), + (float) p.getY(), + (float) q.getX(), + (float) q.getY(), + (float) r.getX(), + (float) r.getY()); + break; + + case PathIterator.SEG_CLOSE: + newPath.closePath(); + break; + } } - - /** - * override base class to un-project the fisheye effect - */ - private Point2D _inverseTransform(Point2D viewPoint) { - - viewPoint = delegate.inverseTransform(viewPoint); - Point2D viewCenter = getViewCenter(); - double viewRadius = getViewRadius(); - double ratio = getRatio(); - double dx = viewPoint.getX() - viewCenter.getX(); - double dy = viewPoint.getY() - viewCenter.getY(); - // factor out ellipse - dx *= ratio; - - Point2D pointFromCenter = new Point2D.Double(dx, dy); - - PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); - - double radius = polar.getRadius(); - if(radius > viewRadius) return viewPoint;//elegate.inverseTransform(viewPoint); - - radius /= viewRadius; - radius = Math.abs(Math.tan(radius)); - radius /= Math.PI/2; - radius *= viewRadius; - double mag = Math.tan(Math.PI/2*magnification); - radius /= mag; - polar.setRadius(radius); - Point2D projectedPoint = PolarPoint.polarToCartesian(polar); - projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY()); - Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(), - projectedPoint.getY()+viewCenter.getY()); - return translatedBack; - //delegate.inverseTransform(translatedBack); + return newPath; + } + + public Shape inverseTransform(Shape shape) { + GeneralPath newPath = new GeneralPath(); + float[] coords = new float[6]; + for (PathIterator iterator = shape.getPathIterator(null); + iterator.isDone() == false; + iterator.next()) { + int type = iterator.currentSegment(coords); + switch (type) { + case PathIterator.SEG_MOVETO: + Point2D p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); + newPath.moveTo((float) p.getX(), (float) p.getY()); + break; + + case PathIterator.SEG_LINETO: + p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); + newPath.lineTo((float) p.getX(), (float) p.getY()); + break; + + case PathIterator.SEG_QUADTO: + p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); + Point2D q = _inverseTransform(new Point2D.Float(coords[2], coords[3])); + newPath.quadTo((float) p.getX(), (float) p.getY(), (float) q.getX(), (float) q.getY()); + break; + + case PathIterator.SEG_CUBICTO: + p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); + q = _inverseTransform(new Point2D.Float(coords[2], coords[3])); + Point2D r = _inverseTransform(new Point2D.Float(coords[4], coords[5])); + newPath.curveTo( + (float) p.getX(), + (float) p.getY(), + (float) q.getX(), + (float) q.getY(), + (float) r.getX(), + (float) r.getY()); + break; + + case PathIterator.SEG_CLOSE: + newPath.closePath(); + break; + } } -} \ No newline at end of file + return newPath; + } + /** override base class transform to project the fisheye effect */ + private Point2D _transform(Point2D graphPoint) { + if (graphPoint == null) return null; + Point2D viewCenter = getViewCenter(); + double viewRadius = getViewRadius(); + double ratio = getRatio(); + // transform the point from the graph to the view + Point2D viewPoint = graphPoint; //delegate.transform(graphPoint); + // calculate point from center + double dx = viewPoint.getX() - viewCenter.getX(); + double dy = viewPoint.getY() - viewCenter.getY(); + // factor out ellipse + dx *= ratio; + Point2D pointFromCenter = new Point2D.Double(dx, dy); + + PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); + double theta = polar.getTheta(); + double radius = polar.getRadius(); + if (radius > viewRadius) return viewPoint; + + double mag = Math.tan(Math.PI / 2 * magnification); + radius *= mag; + + radius = Math.min(radius, viewRadius); + radius /= viewRadius; + radius *= Math.PI / 2; + radius = Math.abs(Math.atan(radius)); + radius *= viewRadius; + Point2D projectedPoint = PolarPoint.polarToCartesian(theta, radius); + projectedPoint.setLocation(projectedPoint.getX() / ratio, projectedPoint.getY()); + Point2D translatedBack = + new Point2D.Double( + projectedPoint.getX() + viewCenter.getX(), projectedPoint.getY() + viewCenter.getY()); + return translatedBack; + } + + /** override base class to un-project the fisheye effect */ + private Point2D _inverseTransform(Point2D viewPoint) { + + viewPoint = delegate.inverseTransform(viewPoint); + Point2D viewCenter = getViewCenter(); + double viewRadius = getViewRadius(); + double ratio = getRatio(); + double dx = viewPoint.getX() - viewCenter.getX(); + double dy = viewPoint.getY() - viewCenter.getY(); + // factor out ellipse + dx *= ratio; + + Point2D pointFromCenter = new Point2D.Double(dx, dy); + + PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); + + double radius = polar.getRadius(); + if (radius > viewRadius) return viewPoint; //elegate.inverseTransform(viewPoint); + + radius /= viewRadius; + radius = Math.abs(Math.tan(radius)); + radius /= Math.PI / 2; + radius *= viewRadius; + double mag = Math.tan(Math.PI / 2 * magnification); + radius /= mag; + polar.setRadius(radius); + Point2D projectedPoint = PolarPoint.polarToCartesian(polar); + projectedPoint.setLocation(projectedPoint.getX() / ratio, projectedPoint.getY()); + Point2D translatedBack = + new Point2D.Double( + projectedPoint.getX() + viewCenter.getX(), projectedPoint.getY() + viewCenter.getY()); + return translatedBack; + //delegate.inverseTransform(translatedBack); + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/Intersector.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/Intersector.java index 403eadb8..ab28dd25 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/Intersector.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/Intersector.java @@ -1,10 +1,10 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.visualization.transform.shape; @@ -15,102 +15,103 @@ import java.util.Set; public class Intersector { - - protected Rectangle rectangle; - Line2D line; - Set points = new HashSet(); - public Intersector(Rectangle rectangle) { - this.rectangle = rectangle; - } + protected Rectangle rectangle; + Line2D line; + Set points = new HashSet(); - public Intersector(Rectangle rectangle, Line2D line) { - this.rectangle = rectangle; - intersectLine(line); - } - - public void intersectLine(Line2D line) { - this.line = line; - points.clear(); - float rx0 = (float) rectangle.getMinX(); - float ry0 = (float) rectangle.getMinY(); - float rx1 = (float) rectangle.getMaxX(); - float ry1 = (float) rectangle.getMaxY(); - - float x1 = (float) line.getX1(); - float y1 = (float) line.getY1(); - float x2 = (float) line.getX2(); - float y2 = (float) line.getY2(); - - float dy = y2 - y1; - float dx = x2 - x1; - - if(dx != 0) { - float m = dy/dx; - float b = y1 - m*x1; - - // base of rect where y == ry0 - float x = (ry0 - b) / m; - - if(rx0 <= x && x <= rx1) { - points.add(new Point2D.Float(x, ry0)); - } - - // top where y == ry1 - x = (ry1 - b) / m; - if(rx0 <= x && x <= rx1) { - points.add(new Point2D.Float(x, ry1)); - } - - // left side, where x == rx0 - float y = m * rx0 + b; - if(ry0 <= y && y <= ry1) { - points.add(new Point2D.Float(rx0, y)); - } - - - // right side, where x == rx1 - y = m * rx1 + b; - if(ry0 <= y && y <= ry1) { - points.add(new Point2D.Float(rx1, y)); - } - - } else { - - // base, where y == ry0 - float x = x1; - if(rx0 <= x && x <= rx1) { - points.add(new Point2D.Float(x, ry0)); - } - - // top, where y == ry1 - x = x2; - if(rx0 <= x && x <= rx1) { - points.add(new Point2D.Float(x, ry1)); - } - } - } - public Line2D getLine() { - return line; - } - public Set getPoints() { - return points; - } - public Rectangle getRectangle() { - return rectangle; - } + public Intersector(Rectangle rectangle) { + this.rectangle = rectangle; + } - public String toString() { - return "Rectangle: "+rectangle+", points:"+points; - } - - public static void main(String[] args) { - Rectangle rectangle = new Rectangle(0,0,10,10); - Line2D line = new Line2D.Float(4,4,5,5); - System.err.println(""+new Intersector(rectangle, line)); - System.err.println(""+new Intersector(rectangle, new Line2D.Float(9,11,11,9))); - System.err.println(""+new Intersector(rectangle, new Line2D.Float(1,1,3,2))); - System.err.println(""+new Intersector(rectangle, new Line2D.Float(4,6,6,4))); + public Intersector(Rectangle rectangle, Line2D line) { + this.rectangle = rectangle; + intersectLine(line); + } + + public void intersectLine(Line2D line) { + this.line = line; + points.clear(); + float rx0 = (float) rectangle.getMinX(); + float ry0 = (float) rectangle.getMinY(); + float rx1 = (float) rectangle.getMaxX(); + float ry1 = (float) rectangle.getMaxY(); + + float x1 = (float) line.getX1(); + float y1 = (float) line.getY1(); + float x2 = (float) line.getX2(); + float y2 = (float) line.getY2(); + + float dy = y2 - y1; + float dx = x2 - x1; + + if (dx != 0) { + float m = dy / dx; + float b = y1 - m * x1; + + // base of rect where y == ry0 + float x = (ry0 - b) / m; + + if (rx0 <= x && x <= rx1) { + points.add(new Point2D.Float(x, ry0)); + } + + // top where y == ry1 + x = (ry1 - b) / m; + if (rx0 <= x && x <= rx1) { + points.add(new Point2D.Float(x, ry1)); + } + + // left side, where x == rx0 + float y = m * rx0 + b; + if (ry0 <= y && y <= ry1) { + points.add(new Point2D.Float(rx0, y)); + } + + // right side, where x == rx1 + y = m * rx1 + b; + if (ry0 <= y && y <= ry1) { + points.add(new Point2D.Float(rx1, y)); + } + + } else { + + // base, where y == ry0 + float x = x1; + if (rx0 <= x && x <= rx1) { + points.add(new Point2D.Float(x, ry0)); + } + + // top, where y == ry1 + x = x2; + if (rx0 <= x && x <= rx1) { + points.add(new Point2D.Float(x, ry1)); + } } + } + + public Line2D getLine() { + return line; + } + + public Set getPoints() { + return points; + } + + public Rectangle getRectangle() { + return rectangle; + } + + public String toString() { + return "Rectangle: " + rectangle + ", points:" + points; + } + public static void main(String[] args) { + Rectangle rectangle = new Rectangle(0, 0, 10, 10); + Line2D line = new Line2D.Float(4, 4, 5, 5); + System.err.println("" + new Intersector(rectangle, line)); + System.err.println("" + new Intersector(rectangle, new Line2D.Float(9, 11, 11, 9))); + System.err.println("" + new Intersector(rectangle, new Line2D.Float(1, 1, 3, 2))); + System.err.println("" + new Intersector(rectangle, new Line2D.Float(4, 6, 6, 4))); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/MagnifyIconGraphics.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/MagnifyIconGraphics.java index 71ffaca8..c2fe8675 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/MagnifyIconGraphics.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/MagnifyIconGraphics.java @@ -10,76 +10,71 @@ package edu.uci.ics.jung.visualization.transform.shape; +import edu.uci.ics.jung.visualization.transform.BidirectionalTransformer; import java.awt.Component; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Rectangle2D; - import javax.swing.Icon; -import edu.uci.ics.jung.visualization.transform.BidirectionalTransformer; - - /** * Subclassed to apply a magnification transform to an icon. - * - * @author Tom Nelson - * * + * @author Tom Nelson */ public class MagnifyIconGraphics extends TransformingFlatnessGraphics { - - public MagnifyIconGraphics(BidirectionalTransformer transformer) { - this(transformer, null); - } - - public MagnifyIconGraphics(BidirectionalTransformer Function, Graphics2D delegate) { - super(Function, delegate); - } - - public void draw(Icon icon, Component c, Shape clip, int x, int y) { - - if(transformer instanceof MagnifyShapeTransformer) { - MagnifyShapeTransformer mst = (MagnifyShapeTransformer)transformer; - int w = icon.getIconWidth(); - int h = icon.getIconHeight(); - Rectangle2D r = new Rectangle2D.Double(x-w/2,y-h/2,w,h); - Shape lens = mst.getLensShape(); - if(lens.intersects(r)) { - // magnify the whole icon - Rectangle2D s = mst.magnify(r).getBounds2D(); - if(lens.intersects(s)) { - clip = mst.transform(clip); - double sx = s.getWidth()/r.getWidth(); - double sy = s.getHeight()/r.getHeight(); - AffineTransform old = delegate.getTransform(); - AffineTransform xform = new AffineTransform(old); - xform.translate(s.getMinX(), s.getMinY()); - xform.scale(sx, sy); - xform.translate(-s.getMinX(), -s.getMinY()); - Shape oldClip = delegate.getClip(); - delegate.clip(clip); - delegate.setTransform(xform); - icon.paintIcon(c, delegate, (int)s.getMinX(), (int)s.getMinY()); - delegate.setTransform(old); - delegate.setClip(oldClip); - } else { - // clip out the lens so the small icon doesn't get drawn - // inside of it - Shape oldClip = delegate.getClip(); - Area viewBounds = new Area(oldClip); - viewBounds.subtract(new Area(lens)); - delegate.setClip(viewBounds); - icon.paintIcon(c, delegate, (int)r.getMinX(),(int)r.getMinY()); - delegate.setClip(oldClip); - } + public MagnifyIconGraphics(BidirectionalTransformer transformer) { + this(transformer, null); + } + + public MagnifyIconGraphics(BidirectionalTransformer Function, Graphics2D delegate) { + super(Function, delegate); + } + + public void draw(Icon icon, Component c, Shape clip, int x, int y) { + + if (transformer instanceof MagnifyShapeTransformer) { + MagnifyShapeTransformer mst = (MagnifyShapeTransformer) transformer; + int w = icon.getIconWidth(); + int h = icon.getIconHeight(); + Rectangle2D r = new Rectangle2D.Double(x - w / 2, y - h / 2, w, h); + Shape lens = mst.getLensShape(); + if (lens.intersects(r)) { + // magnify the whole icon + Rectangle2D s = mst.magnify(r).getBounds2D(); + if (lens.intersects(s)) { + clip = mst.transform(clip); + double sx = s.getWidth() / r.getWidth(); + double sy = s.getHeight() / r.getHeight(); + + AffineTransform old = delegate.getTransform(); + AffineTransform xform = new AffineTransform(old); + xform.translate(s.getMinX(), s.getMinY()); + xform.scale(sx, sy); + xform.translate(-s.getMinX(), -s.getMinY()); + Shape oldClip = delegate.getClip(); + delegate.clip(clip); + delegate.setTransform(xform); + icon.paintIcon(c, delegate, (int) s.getMinX(), (int) s.getMinY()); + delegate.setTransform(old); + delegate.setClip(oldClip); + } else { + // clip out the lens so the small icon doesn't get drawn + // inside of it + Shape oldClip = delegate.getClip(); + Area viewBounds = new Area(oldClip); + viewBounds.subtract(new Area(lens)); + delegate.setClip(viewBounds); + icon.paintIcon(c, delegate, (int) r.getMinX(), (int) r.getMinY()); + delegate.setClip(oldClip); + } - } else { - icon.paintIcon(c, delegate, (int)r.getMinX(),(int)r.getMinY()); - } - } + } else { + icon.paintIcon(c, delegate, (int) r.getMinX(), (int) r.getMinY()); + } } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/MagnifyImageLensSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/MagnifyImageLensSupport.java index 59e5aede..34766a1c 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/MagnifyImageLensSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/MagnifyImageLensSupport.java @@ -10,8 +10,6 @@ package edu.uci.ics.jung.visualization.transform.shape; -import java.awt.Dimension; - import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.RenderContext; @@ -24,83 +22,91 @@ import edu.uci.ics.jung.visualization.renderers.ReshapingEdgeRenderer; import edu.uci.ics.jung.visualization.transform.AbstractLensSupport; import edu.uci.ics.jung.visualization.transform.LensTransformer; +import java.awt.Dimension; /** - * Changes various visualization settings to activate or deactivate an - * examining lens for a jung graph application. - * + * Changes various visualization settings to activate or deactivate an examining lens for a jung + * graph application. + * * @author Tom Nelson */ -public class MagnifyImageLensSupport extends AbstractLensSupport { - - protected RenderContext renderContext; - protected GraphicsDecorator lensGraphicsDecorator; - protected GraphicsDecorator savedGraphicsDecorator; - protected Renderer renderer; - protected Renderer transformingRenderer; - protected NetworkElementAccessor pickSupport; - protected Renderer.Edge savedEdgeRenderer; - protected Renderer.Edge reshapingEdgeRenderer; +public class MagnifyImageLensSupport extends AbstractLensSupport { - static final String instructions = - "

            Mouse-Drag the Lens center to move it

            "+ - "Mouse-Drag the Lens edge to resize it

            "+ - "Ctrl+MouseWheel to change magnification

            "; - - public MagnifyImageLensSupport(VisualizationViewer vv) { - this(vv, new MagnifyShapeTransformer(vv), - new ModalLensGraphMouse()); - } - - public MagnifyImageLensSupport(VisualizationViewer vv, LensTransformer lensTransformer, - ModalGraphMouse lensGraphMouse) { - super(vv, lensGraphMouse); - this.renderContext = vv.getRenderContext(); - this.pickSupport = renderContext.getPickSupport(); - this.renderer = vv.getRenderer(); - this.transformingRenderer = new BasicRenderer(vv.getGraphLayout(), renderContext); - this.savedGraphicsDecorator = renderContext.getGraphicsContext(); - this.lensTransformer = lensTransformer; - this.savedEdgeRenderer = vv.getRenderer().getEdgeRenderer(); - this.reshapingEdgeRenderer = new ReshapingEdgeRenderer(vv.getGraphLayout(), renderContext); - this.reshapingEdgeRenderer.setEdgeArrowRenderingSupport(savedEdgeRenderer.getEdgeArrowRenderingSupport()); + protected RenderContext renderContext; + protected GraphicsDecorator lensGraphicsDecorator; + protected GraphicsDecorator savedGraphicsDecorator; + protected Renderer renderer; + protected Renderer transformingRenderer; + protected NetworkElementAccessor pickSupport; + protected Renderer.Edge savedEdgeRenderer; + protected Renderer.Edge reshapingEdgeRenderer; - Dimension d = vv.getSize(); - if(d.width == 0 || d.height == 0) { - d = vv.getPreferredSize(); - } - lensTransformer.setViewRadius(d.width/5); - this.lensGraphicsDecorator = new MagnifyIconGraphics(lensTransformer); + static final String instructions = + "
            Mouse-Drag the Lens center to move it

            " + + "Mouse-Drag the Lens edge to resize it

            " + + "Ctrl+MouseWheel to change magnification

            "; + + public MagnifyImageLensSupport(VisualizationViewer vv) { + this(vv, new MagnifyShapeTransformer(vv), new ModalLensGraphMouse()); + } + + public MagnifyImageLensSupport( + VisualizationViewer vv, + LensTransformer lensTransformer, + ModalGraphMouse lensGraphMouse) { + super(vv, lensGraphMouse); + this.renderContext = vv.getRenderContext(); + this.pickSupport = renderContext.getPickSupport(); + this.renderer = vv.getRenderer(); + this.transformingRenderer = new BasicRenderer(vv.getGraphLayout(), renderContext); + this.savedGraphicsDecorator = renderContext.getGraphicsContext(); + this.lensTransformer = lensTransformer; + this.savedEdgeRenderer = vv.getRenderer().getEdgeRenderer(); + this.reshapingEdgeRenderer = + new ReshapingEdgeRenderer(vv.getGraphLayout(), renderContext); + this.reshapingEdgeRenderer.setEdgeArrowRenderingSupport( + savedEdgeRenderer.getEdgeArrowRenderingSupport()); + + Dimension d = vv.getSize(); + if (d.width == 0 || d.height == 0) { + d = vv.getPreferredSize(); } - - public void activate() { - lensTransformer.setDelegate(vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)); - if(lens == null) { - lens = new Lens(lensTransformer); - } - if(lensControls == null) { - lensControls = new LensControls(lensTransformer); - } - renderContext.setPickSupport(new ViewLensShapePickSupport(vv)); - lensTransformer.setDelegate(vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)); - vv.getRenderContext().getMultiLayerTransformer().setTransformer(Layer.VIEW, lensTransformer); - this.renderContext.setGraphicsContext(lensGraphicsDecorator); - vv.getRenderer().setEdgeRenderer(reshapingEdgeRenderer); - vv.addPreRenderPaintable(lens); - vv.addPostRenderPaintable(lensControls); - vv.setGraphMouse(lensGraphMouse); - vv.setToolTipText(instructions); - vv.repaint(); + lensTransformer.setViewRadius(d.width / 5); + this.lensGraphicsDecorator = new MagnifyIconGraphics(lensTransformer); + } + + public void activate() { + lensTransformer.setDelegate( + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)); + if (lens == null) { + lens = new Lens(lensTransformer); } - - public void deactivate() { - renderContext.setPickSupport(pickSupport); - vv.getRenderContext().getMultiLayerTransformer().setTransformer(Layer.VIEW, lensTransformer.getDelegate()); - vv.removePreRenderPaintable(lens); - vv.removePostRenderPaintable(lensControls); - this.renderContext.setGraphicsContext(savedGraphicsDecorator); - vv.getRenderer().setEdgeRenderer(savedEdgeRenderer); - vv.setToolTipText(defaultToolTipText); - vv.setGraphMouse(graphMouse); - vv.repaint(); + if (lensControls == null) { + lensControls = new LensControls(lensTransformer); } + renderContext.setPickSupport(new ViewLensShapePickSupport(vv)); + lensTransformer.setDelegate( + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)); + vv.getRenderContext().getMultiLayerTransformer().setTransformer(Layer.VIEW, lensTransformer); + this.renderContext.setGraphicsContext(lensGraphicsDecorator); + vv.getRenderer().setEdgeRenderer(reshapingEdgeRenderer); + vv.addPreRenderPaintable(lens); + vv.addPostRenderPaintable(lensControls); + vv.setGraphMouse(lensGraphMouse); + vv.setToolTipText(instructions); + vv.repaint(); + } + + public void deactivate() { + renderContext.setPickSupport(pickSupport); + vv.getRenderContext() + .getMultiLayerTransformer() + .setTransformer(Layer.VIEW, lensTransformer.getDelegate()); + vv.removePreRenderPaintable(lens); + vv.removePostRenderPaintable(lensControls); + this.renderContext.setGraphicsContext(savedGraphicsDecorator); + vv.getRenderer().setEdgeRenderer(savedEdgeRenderer); + vv.setToolTipText(defaultToolTipText); + vv.setGraphMouse(graphMouse); + vv.repaint(); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/MagnifyShapeTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/MagnifyShapeTransformer.java index 46f7afc9..e2abc5a0 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/MagnifyShapeTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/MagnifyShapeTransformer.java @@ -1,271 +1,275 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.visualization.transform.shape; +import edu.uci.ics.jung.algorithms.layout.PolarPoint; +import edu.uci.ics.jung.visualization.transform.MagnifyTransformer; +import edu.uci.ics.jung.visualization.transform.MutableTransformer; import java.awt.Component; import java.awt.Shape; import java.awt.geom.GeneralPath; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; -import edu.uci.ics.jung.algorithms.layout.PolarPoint; -import edu.uci.ics.jung.visualization.transform.MagnifyTransformer; -import edu.uci.ics.jung.visualization.transform.MutableTransformer; - /** - * MagnifyShapeTransformer extends MagnifyTransformer and - * adds implementations for methods in ShapeTransformer. - * It modifies the shapes (Vertex, Edge, and Arrowheads) so that - * they are enlarged by the magnify transformation. - * + * MagnifyShapeTransformer extends MagnifyTransformer and adds implementations for methods in + * ShapeTransformer. It modifies the shapes (Vertex, Edge, and Arrowheads) so that they are enlarged + * by the magnify transformation. + * * @author Tom Nelson */ -public class MagnifyShapeTransformer extends MagnifyTransformer +public class MagnifyShapeTransformer extends MagnifyTransformer implements ShapeFlatnessTransformer { - /** - * Create an instance, setting values from the passed component - * and registering to listen for size changes on the component. - * - * @param component the component used for rendering - */ - public MagnifyShapeTransformer(Component component) { - this(component, null); - } - - /** - * Create an instance, setting values from the passed component - * and registering to listen for size changes on the component, - * with a possibly shared transform delegate. - * - * @param component the component used for rendering - * @param delegate the transformer to use - */ - public MagnifyShapeTransformer(Component component, MutableTransformer delegate) { - super(component, delegate); - } - - /** - * Transform the supplied shape with the overridden transform - * method so that the shape is distorted by the magnify - * transform. - * @param shape a shape to transform - * @return a GeneralPath for the transformed shape - */ - public Shape transform(Shape shape) { - return transform(shape, 0); - } - public Shape transform(Shape shape, float flatness) { - GeneralPath newPath = new GeneralPath(); - float[] coords = new float[6]; - PathIterator iterator = null; - if(flatness == 0) { - iterator = shape.getPathIterator(null); - } else { - iterator = shape.getPathIterator(null, flatness); - } - for( ; - iterator.isDone() == false; - iterator.next()) { - int type = iterator.currentSegment(coords); - switch(type) { - case PathIterator.SEG_MOVETO: - Point2D p = _transform(new Point2D.Float(coords[0], coords[1])); - newPath.moveTo((float)p.getX(), (float)p.getY()); - break; - - case PathIterator.SEG_LINETO: - p = _transform(new Point2D.Float(coords[0], coords[1])); - newPath.lineTo((float)p.getX(), (float) p.getY()); - break; - - case PathIterator.SEG_QUADTO: - p = _transform(new Point2D.Float(coords[0], coords[1])); - Point2D q = _transform(new Point2D.Float(coords[2], coords[3])); - newPath.quadTo((float)p.getX(), (float)p.getY(), (float)q.getX(), (float)q.getY()); - break; - - case PathIterator.SEG_CUBICTO: - p = _transform(new Point2D.Float(coords[0], coords[1])); - q = _transform(new Point2D.Float(coords[2], coords[3])); - Point2D r = _transform(new Point2D.Float(coords[4], coords[5])); - newPath.curveTo((float)p.getX(), (float)p.getY(), - (float)q.getX(), (float)q.getY(), - (float)r.getX(), (float)r.getY()); - break; - - case PathIterator.SEG_CLOSE: - newPath.closePath(); - break; - - } - } - return newPath; - } + /** + * Create an instance, setting values from the passed component and registering to listen for size + * changes on the component. + * + * @param component the component used for rendering + */ + public MagnifyShapeTransformer(Component component) { + this(component, null); + } - public Shape inverseTransform(Shape shape) { - GeneralPath newPath = new GeneralPath(); - float[] coords = new float[6]; - for(PathIterator iterator=shape.getPathIterator(null); - iterator.isDone() == false; - iterator.next()) { - int type = iterator.currentSegment(coords); - switch(type) { - case PathIterator.SEG_MOVETO: - Point2D p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); - newPath.moveTo((float)p.getX(), (float)p.getY()); - break; - - case PathIterator.SEG_LINETO: - p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); - newPath.lineTo((float)p.getX(), (float) p.getY()); - break; - - case PathIterator.SEG_QUADTO: - p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); - Point2D q = _inverseTransform(new Point2D.Float(coords[2], coords[3])); - newPath.quadTo((float)p.getX(), (float)p.getY(), (float)q.getX(), (float)q.getY()); - break; - - case PathIterator.SEG_CUBICTO: - p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); - q = _inverseTransform(new Point2D.Float(coords[2], coords[3])); - Point2D r = _inverseTransform(new Point2D.Float(coords[4], coords[5])); - newPath.curveTo((float)p.getX(), (float)p.getY(), - (float)q.getX(), (float)q.getY(), - (float)r.getX(), (float)r.getY()); - break; - - case PathIterator.SEG_CLOSE: - newPath.closePath(); - break; - - } - } - return newPath; - } - - private Point2D _transform(Point2D graphPoint) { - if(graphPoint == null) return null; - Point2D viewCenter = getViewCenter(); - double viewRadius = getViewRadius(); - double ratio = getRatio(); - // transform the point from the graph to the view - Point2D viewPoint = graphPoint; - // calculate point from center - double dx = viewPoint.getX() - viewCenter.getX(); - double dy = viewPoint.getY() - viewCenter.getY(); - // factor out ellipse - dx *= ratio; - Point2D pointFromCenter = new Point2D.Double(dx, dy); - - PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); - double theta = polar.getTheta(); - double radius = polar.getRadius(); - if(radius > viewRadius) return viewPoint; - - double mag = magnification; - radius *= mag; - - radius = Math.min(radius, viewRadius); - Point2D projectedPoint = PolarPoint.polarToCartesian(theta, radius); - projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY()); - Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(), - projectedPoint.getY()+viewCenter.getY()); - return translatedBack; + /** + * Create an instance, setting values from the passed component and registering to listen for size + * changes on the component, with a possibly shared transform delegate. + * + * @param component the component used for rendering + * @param delegate the transformer to use + */ + public MagnifyShapeTransformer(Component component, MutableTransformer delegate) { + super(component, delegate); + } + + /** + * Transform the supplied shape with the overridden transform method so that the shape is + * distorted by the magnify transform. + * + * @param shape a shape to transform + * @return a GeneralPath for the transformed shape + */ + public Shape transform(Shape shape) { + return transform(shape, 0); + } + + public Shape transform(Shape shape, float flatness) { + GeneralPath newPath = new GeneralPath(); + float[] coords = new float[6]; + PathIterator iterator = null; + if (flatness == 0) { + iterator = shape.getPathIterator(null); + } else { + iterator = shape.getPathIterator(null, flatness); } - - /** - * override base class to un-project the fisheye effect - */ - private Point2D _inverseTransform(Point2D viewPoint) { - - viewPoint = delegate.inverseTransform(viewPoint); - Point2D viewCenter = getViewCenter(); - double viewRadius = getViewRadius(); - double ratio = getRatio(); - double dx = viewPoint.getX() - viewCenter.getX(); - double dy = viewPoint.getY() - viewCenter.getY(); - // factor out ellipse - dx *= ratio; + for (; iterator.isDone() == false; iterator.next()) { + int type = iterator.currentSegment(coords); + switch (type) { + case PathIterator.SEG_MOVETO: + Point2D p = _transform(new Point2D.Float(coords[0], coords[1])); + newPath.moveTo((float) p.getX(), (float) p.getY()); + break; + + case PathIterator.SEG_LINETO: + p = _transform(new Point2D.Float(coords[0], coords[1])); + newPath.lineTo((float) p.getX(), (float) p.getY()); + break; - Point2D pointFromCenter = new Point2D.Double(dx, dy); - - PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); + case PathIterator.SEG_QUADTO: + p = _transform(new Point2D.Float(coords[0], coords[1])); + Point2D q = _transform(new Point2D.Float(coords[2], coords[3])); + newPath.quadTo((float) p.getX(), (float) p.getY(), (float) q.getX(), (float) q.getY()); + break; - double radius = polar.getRadius(); - if(radius > viewRadius) return viewPoint; - - double mag = magnification; - radius /= mag; - polar.setRadius(radius); - Point2D projectedPoint = PolarPoint.polarToCartesian(polar); - projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY()); - Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(), - projectedPoint.getY()+viewCenter.getY()); - return translatedBack; + case PathIterator.SEG_CUBICTO: + p = _transform(new Point2D.Float(coords[0], coords[1])); + q = _transform(new Point2D.Float(coords[2], coords[3])); + Point2D r = _transform(new Point2D.Float(coords[4], coords[5])); + newPath.curveTo( + (float) p.getX(), + (float) p.getY(), + (float) q.getX(), + (float) q.getY(), + (float) r.getX(), + (float) r.getY()); + break; + + case PathIterator.SEG_CLOSE: + newPath.closePath(); + break; + } } - - /** - * Magnify the shape, without considering the Lens. - * @param shape the shape to magnify - * @return the transformed shape - */ - public Shape magnify(Shape shape) { - return magnify(shape, 0); + return newPath; + } + + public Shape inverseTransform(Shape shape) { + GeneralPath newPath = new GeneralPath(); + float[] coords = new float[6]; + for (PathIterator iterator = shape.getPathIterator(null); + iterator.isDone() == false; + iterator.next()) { + int type = iterator.currentSegment(coords); + switch (type) { + case PathIterator.SEG_MOVETO: + Point2D p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); + newPath.moveTo((float) p.getX(), (float) p.getY()); + break; + + case PathIterator.SEG_LINETO: + p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); + newPath.lineTo((float) p.getX(), (float) p.getY()); + break; + + case PathIterator.SEG_QUADTO: + p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); + Point2D q = _inverseTransform(new Point2D.Float(coords[2], coords[3])); + newPath.quadTo((float) p.getX(), (float) p.getY(), (float) q.getX(), (float) q.getY()); + break; + + case PathIterator.SEG_CUBICTO: + p = _inverseTransform(new Point2D.Float(coords[0], coords[1])); + q = _inverseTransform(new Point2D.Float(coords[2], coords[3])); + Point2D r = _inverseTransform(new Point2D.Float(coords[4], coords[5])); + newPath.curveTo( + (float) p.getX(), + (float) p.getY(), + (float) q.getX(), + (float) q.getY(), + (float) r.getX(), + (float) r.getY()); + break; + + case PathIterator.SEG_CLOSE: + newPath.closePath(); + break; + } } - public Shape magnify(Shape shape, float flatness) { - GeneralPath newPath = new GeneralPath(); - float[] coords = new float[6]; - PathIterator iterator = null; - if(flatness == 0) { - iterator = shape.getPathIterator(null); - } else { - iterator = shape.getPathIterator(null, flatness); - } - for( ; - iterator.isDone() == false; - iterator.next()) { - int type = iterator.currentSegment(coords); - switch(type) { - case PathIterator.SEG_MOVETO: - Point2D p = magnify(new Point2D.Float(coords[0], coords[1])); - newPath.moveTo((float)p.getX(), (float)p.getY()); - break; - - case PathIterator.SEG_LINETO: - p = magnify(new Point2D.Float(coords[0], coords[1])); - newPath.lineTo((float)p.getX(), (float) p.getY()); - break; - - case PathIterator.SEG_QUADTO: - p = magnify(new Point2D.Float(coords[0], coords[1])); - Point2D q = magnify(new Point2D.Float(coords[2], coords[3])); - newPath.quadTo((float)p.getX(), (float)p.getY(), (float)q.getX(), (float)q.getY()); - break; - - case PathIterator.SEG_CUBICTO: - p = magnify(new Point2D.Float(coords[0], coords[1])); - q = magnify(new Point2D.Float(coords[2], coords[3])); - Point2D r = magnify(new Point2D.Float(coords[4], coords[5])); - newPath.curveTo((float)p.getX(), (float)p.getY(), - (float)q.getX(), (float)q.getY(), - (float)r.getX(), (float)r.getY()); - break; - - case PathIterator.SEG_CLOSE: - newPath.closePath(); - break; - - } - } - return newPath; + return newPath; + } + + private Point2D _transform(Point2D graphPoint) { + if (graphPoint == null) return null; + Point2D viewCenter = getViewCenter(); + double viewRadius = getViewRadius(); + double ratio = getRatio(); + // transform the point from the graph to the view + Point2D viewPoint = graphPoint; + // calculate point from center + double dx = viewPoint.getX() - viewCenter.getX(); + double dy = viewPoint.getY() - viewCenter.getY(); + // factor out ellipse + dx *= ratio; + Point2D pointFromCenter = new Point2D.Double(dx, dy); + + PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); + double theta = polar.getTheta(); + double radius = polar.getRadius(); + if (radius > viewRadius) return viewPoint; + + double mag = magnification; + radius *= mag; + + radius = Math.min(radius, viewRadius); + Point2D projectedPoint = PolarPoint.polarToCartesian(theta, radius); + projectedPoint.setLocation(projectedPoint.getX() / ratio, projectedPoint.getY()); + Point2D translatedBack = + new Point2D.Double( + projectedPoint.getX() + viewCenter.getX(), projectedPoint.getY() + viewCenter.getY()); + return translatedBack; + } + + /** override base class to un-project the fisheye effect */ + private Point2D _inverseTransform(Point2D viewPoint) { + + viewPoint = delegate.inverseTransform(viewPoint); + Point2D viewCenter = getViewCenter(); + double viewRadius = getViewRadius(); + double ratio = getRatio(); + double dx = viewPoint.getX() - viewCenter.getX(); + double dy = viewPoint.getY() - viewCenter.getY(); + // factor out ellipse + dx *= ratio; + + Point2D pointFromCenter = new Point2D.Double(dx, dy); + + PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter); + + double radius = polar.getRadius(); + if (radius > viewRadius) return viewPoint; + + double mag = magnification; + radius /= mag; + polar.setRadius(radius); + Point2D projectedPoint = PolarPoint.polarToCartesian(polar); + projectedPoint.setLocation(projectedPoint.getX() / ratio, projectedPoint.getY()); + Point2D translatedBack = + new Point2D.Double( + projectedPoint.getX() + viewCenter.getX(), projectedPoint.getY() + viewCenter.getY()); + return translatedBack; + } + + /** + * Magnify the shape, without considering the Lens. + * + * @param shape the shape to magnify + * @return the transformed shape + */ + public Shape magnify(Shape shape) { + return magnify(shape, 0); + } + + public Shape magnify(Shape shape, float flatness) { + GeneralPath newPath = new GeneralPath(); + float[] coords = new float[6]; + PathIterator iterator = null; + if (flatness == 0) { + iterator = shape.getPathIterator(null); + } else { + iterator = shape.getPathIterator(null, flatness); } + for (; iterator.isDone() == false; iterator.next()) { + int type = iterator.currentSegment(coords); + switch (type) { + case PathIterator.SEG_MOVETO: + Point2D p = magnify(new Point2D.Float(coords[0], coords[1])); + newPath.moveTo((float) p.getX(), (float) p.getY()); + break; -} \ No newline at end of file + case PathIterator.SEG_LINETO: + p = magnify(new Point2D.Float(coords[0], coords[1])); + newPath.lineTo((float) p.getX(), (float) p.getY()); + break; + + case PathIterator.SEG_QUADTO: + p = magnify(new Point2D.Float(coords[0], coords[1])); + Point2D q = magnify(new Point2D.Float(coords[2], coords[3])); + newPath.quadTo((float) p.getX(), (float) p.getY(), (float) q.getX(), (float) q.getY()); + break; + + case PathIterator.SEG_CUBICTO: + p = magnify(new Point2D.Float(coords[0], coords[1])); + q = magnify(new Point2D.Float(coords[2], coords[3])); + Point2D r = magnify(new Point2D.Float(coords[4], coords[5])); + newPath.curveTo( + (float) p.getX(), + (float) p.getY(), + (float) q.getX(), + (float) q.getY(), + (float) r.getX(), + (float) r.getY()); + break; + + case PathIterator.SEG_CLOSE: + newPath.closePath(); + break; + } + } + return newPath; + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/ShapeFlatnessTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/ShapeFlatnessTransformer.java index 2d81e79f..59848908 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/ShapeFlatnessTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/ShapeFlatnessTransformer.java @@ -13,23 +13,20 @@ import java.awt.Shape; /** - * Provides methods to map points from one coordinate system to - * another: graph to screen and screen to graph. - * The flatness parameter is used to break a curved shape into - * smaller segments in order to perform a more detailed - * transformation. - * - * @author Tom Nelson + * Provides methods to map points from one coordinate system to another: graph to screen and screen + * to graph. The flatness parameter is used to break a curved shape into smaller segments in order + * to perform a more detailed transformation. + * + * @author Tom Nelson */ -public interface ShapeFlatnessTransformer extends ShapeTransformer { - - /** - * map a shape from graph coordinate system to the - * screen coordinate system - * @param shape the shape to be transformed - * @param flatness used to break the supplied shape into segments - * @return a GeneralPath (Shape) representing the screen points of the shape - */ - Shape transform(Shape shape, float flatness); - +public interface ShapeFlatnessTransformer extends ShapeTransformer { + + /** + * map a shape from graph coordinate system to the screen coordinate system + * + * @param shape the shape to be transformed + * @param flatness used to break the supplied shape into segments + * @return a GeneralPath (Shape) representing the screen points of the shape + */ + Shape transform(Shape shape, float flatness); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/ShapeTransformer.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/ShapeTransformer.java index b90df288..ea4097e1 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/ShapeTransformer.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/ShapeTransformer.java @@ -10,25 +10,24 @@ package edu.uci.ics.jung.visualization.transform.shape; -import java.awt.Shape; - import edu.uci.ics.jung.visualization.transform.BidirectionalTransformer; +import java.awt.Shape; /** - * Provides methods to map points from one coordinate system to - * another: graph to screen and screen to graph. - * - * @author Tom Nelson + * Provides methods to map points from one coordinate system to another: graph to screen and screen + * to graph. + * + * @author Tom Nelson */ public interface ShapeTransformer extends BidirectionalTransformer { - - /** - * map a shape from graph coordinate system to the - * screen coordinate system - * @param shape the Shape to transform - * @return a GeneralPath (Shape) representing the screen points of the shape - */ - Shape transform(Shape shape); - - Shape inverseTransform(Shape shape); + + /** + * map a shape from graph coordinate system to the screen coordinate system + * + * @param shape the Shape to transform + * @return a GeneralPath (Shape) representing the screen points of the shape + */ + Shape transform(Shape shape); + + Shape inverseTransform(Shape shape); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/TransformingFlatnessGraphics.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/TransformingFlatnessGraphics.java index c403aa2f..533be7e5 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/TransformingFlatnessGraphics.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/TransformingFlatnessGraphics.java @@ -10,53 +10,47 @@ package edu.uci.ics.jung.visualization.transform.shape; -import java.awt.Graphics2D; -import java.awt.Shape; - import edu.uci.ics.jung.visualization.transform.BidirectionalTransformer; import edu.uci.ics.jung.visualization.transform.HyperbolicTransformer; - +import java.awt.Graphics2D; +import java.awt.Shape; /** - * subclassed to pass certain operations thru the Function - * before the base class method is applied - * This is useful when you want to apply non-affine transformations - * to the Graphics2D used to draw elements of the graph. - * - * @author Tom Nelson - * + * subclassed to pass certain operations thru the Function before the base class method is applied + * This is useful when you want to apply non-affine transformations to the Graphics2D used to draw + * elements of the graph. * + * @author Tom Nelson */ public class TransformingFlatnessGraphics extends TransformingGraphics { - - float flatness = 0; - public TransformingFlatnessGraphics(BidirectionalTransformer transformer) { - this(transformer, null); - } - - public TransformingFlatnessGraphics(BidirectionalTransformer transformer, Graphics2D delegate) { - super(transformer, delegate); - } - - public void draw(Shape s, float flatness) { - Shape shape = null; - if(transformer instanceof ShapeFlatnessTransformer) { - shape = ((ShapeFlatnessTransformer)transformer).transform(s, flatness); - } else { - shape = ((ShapeTransformer)transformer).transform(s); - } - delegate.draw(shape); - + float flatness = 0; + + public TransformingFlatnessGraphics(BidirectionalTransformer transformer) { + this(transformer, null); + } + + public TransformingFlatnessGraphics(BidirectionalTransformer transformer, Graphics2D delegate) { + super(transformer, delegate); + } + + public void draw(Shape s, float flatness) { + Shape shape = null; + if (transformer instanceof ShapeFlatnessTransformer) { + shape = ((ShapeFlatnessTransformer) transformer).transform(s, flatness); + } else { + shape = ((ShapeTransformer) transformer).transform(s); } - - public void fill(Shape s, float flatness) { - Shape shape = null; - if(transformer instanceof HyperbolicTransformer) { - shape = ((HyperbolicShapeTransformer)transformer).transform(s, flatness); - } else { - shape = ((ShapeTransformer)transformer).transform(s); - } - delegate.fill(shape); + delegate.draw(shape); + } + + public void fill(Shape s, float flatness) { + Shape shape = null; + if (transformer instanceof HyperbolicTransformer) { + shape = ((HyperbolicShapeTransformer) transformer).transform(s, flatness); + } else { + shape = ((ShapeTransformer) transformer).transform(s); } + delegate.fill(shape); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/TransformingGraphics.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/TransformingGraphics.java index 3609a22e..caa5edb5 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/TransformingGraphics.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/TransformingGraphics.java @@ -10,6 +10,7 @@ package edu.uci.ics.jung.visualization.transform.shape; +import edu.uci.ics.jung.visualization.transform.BidirectionalTransformer; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; @@ -19,132 +20,111 @@ import java.awt.geom.Rectangle2D; import java.awt.image.ImageObserver; -import edu.uci.ics.jung.visualization.transform.BidirectionalTransformer; - - /** - * subclassed to pass certain operations thru the Function - * before the base class method is applied - * This is useful when you want to apply non-affine transformations - * to the Graphics2D used to draw elements of the graph. - * - * @author Tom Nelson - * + * subclassed to pass certain operations thru the Function before the base class method is applied + * This is useful when you want to apply non-affine transformations to the Graphics2D used to draw + * elements of the graph. * + * @author Tom Nelson */ public class TransformingGraphics extends GraphicsDecorator { - - /** - * the Function to apply - */ - protected BidirectionalTransformer transformer; - - public TransformingGraphics(BidirectionalTransformer transformer) { - this(transformer, null); - } - - public TransformingGraphics(BidirectionalTransformer Function, Graphics2D delegate) { - super(delegate); - this.transformer = Function; - } - - /** - * @return Returns the Function. - */ - public BidirectionalTransformer getTransformer() { - return transformer; - } - - /** - * @param Function The Function to set. - */ - public void setTransformer(BidirectionalTransformer Function) { - this.transformer = Function; - } - - /** - * transform the shape before letting the delegate draw it - */ - public void draw(Shape s) { - Shape shape = ((ShapeTransformer)transformer).transform(s); - delegate.draw(shape); - } - - public void draw(Shape s, float flatness) { - Shape shape = null; - if(transformer instanceof ShapeFlatnessTransformer) { - shape = ((ShapeFlatnessTransformer)transformer).transform(s, flatness); - } else { - shape = ((ShapeTransformer)transformer).transform(s); - } - delegate.draw(shape); - - } - - /** - * transform the shape before letting the delegate fill it - */ - public void fill(Shape s) { - Shape shape = ((ShapeTransformer)transformer).transform(s); - delegate.fill(shape); - } - - public void fill(Shape s, float flatness) { - Shape shape = null; - if(transformer instanceof ShapeFlatnessTransformer) { - shape = ((ShapeFlatnessTransformer)transformer).transform(s, flatness); - } else { - shape = ((ShapeTransformer)transformer).transform(s); - } - delegate.fill(shape); - } - - public boolean drawImage(Image img, int x, int y, ImageObserver observer) { - Image image = null; - if(transformer instanceof ShapeFlatnessTransformer) { - Rectangle2D r = new Rectangle2D.Double(x,y,img.getWidth(observer),img.getHeight(observer)); - Rectangle2D s = ((ShapeTransformer)transformer).transform(r).getBounds2D(); - image = img.getScaledInstance((int)s.getWidth(), (int)s.getHeight(), Image.SCALE_SMOOTH); - x = (int) s.getMinX(); - y = (int) s.getMinY(); - } else { - image = img; - } - return delegate.drawImage(image, x, y, observer); - } - public boolean drawImage(Image img, AffineTransform at, ImageObserver observer) { - Image image = null; - int x = (int)at.getTranslateX(); - int y = (int)at.getTranslateY(); - if(transformer instanceof ShapeFlatnessTransformer) { - Rectangle2D r = new Rectangle2D.Double(x,y,img.getWidth(observer),img.getHeight(observer)); - Rectangle2D s = ((ShapeTransformer)transformer).transform(r).getBounds2D(); - image = img.getScaledInstance((int)s.getWidth(), (int)s.getHeight(), Image.SCALE_SMOOTH); - x = (int) s.getMinX(); - y = (int) s.getMinY(); - at.setToTranslation(s.getMinX(), s.getMinY()); - } else { - image = img; - } - return delegate.drawImage(image, at, observer); + /** the Function to apply */ + protected BidirectionalTransformer transformer; + + public TransformingGraphics(BidirectionalTransformer transformer) { + this(transformer, null); + } + + public TransformingGraphics(BidirectionalTransformer Function, Graphics2D delegate) { + super(delegate); + this.transformer = Function; + } + + /** @return Returns the Function. */ + public BidirectionalTransformer getTransformer() { + return transformer; + } + + /** @param Function The Function to set. */ + public void setTransformer(BidirectionalTransformer Function) { + this.transformer = Function; + } + + /** transform the shape before letting the delegate draw it */ + public void draw(Shape s) { + Shape shape = ((ShapeTransformer) transformer).transform(s); + delegate.draw(shape); + } + + public void draw(Shape s, float flatness) { + Shape shape = null; + if (transformer instanceof ShapeFlatnessTransformer) { + shape = ((ShapeFlatnessTransformer) transformer).transform(s, flatness); + } else { + shape = ((ShapeTransformer) transformer).transform(s); } + delegate.draw(shape); + } - /** - * transform the shape before letting the delegate apply 'hit' - * with it - */ - public boolean hit(Rectangle rect, Shape s, boolean onStroke) { - Shape shape = ((ShapeTransformer)transformer).transform(s); - return delegate.hit(rect, shape, onStroke); + /** transform the shape before letting the delegate fill it */ + public void fill(Shape s) { + Shape shape = ((ShapeTransformer) transformer).transform(s); + delegate.fill(shape); + } + + public void fill(Shape s, float flatness) { + Shape shape = null; + if (transformer instanceof ShapeFlatnessTransformer) { + shape = ((ShapeFlatnessTransformer) transformer).transform(s, flatness); + } else { + shape = ((ShapeTransformer) transformer).transform(s); } - - public Graphics create() { - return delegate.create(); + delegate.fill(shape); + } + + public boolean drawImage(Image img, int x, int y, ImageObserver observer) { + Image image = null; + if (transformer instanceof ShapeFlatnessTransformer) { + Rectangle2D r = new Rectangle2D.Double(x, y, img.getWidth(observer), img.getHeight(observer)); + Rectangle2D s = ((ShapeTransformer) transformer).transform(r).getBounds2D(); + image = img.getScaledInstance((int) s.getWidth(), (int) s.getHeight(), Image.SCALE_SMOOTH); + x = (int) s.getMinX(); + y = (int) s.getMinY(); + } else { + image = img; } - - public void dispose() { - delegate.dispose(); + return delegate.drawImage(image, x, y, observer); + } + + public boolean drawImage(Image img, AffineTransform at, ImageObserver observer) { + Image image = null; + int x = (int) at.getTranslateX(); + int y = (int) at.getTranslateY(); + if (transformer instanceof ShapeFlatnessTransformer) { + Rectangle2D r = new Rectangle2D.Double(x, y, img.getWidth(observer), img.getHeight(observer)); + Rectangle2D s = ((ShapeTransformer) transformer).transform(r).getBounds2D(); + image = img.getScaledInstance((int) s.getWidth(), (int) s.getHeight(), Image.SCALE_SMOOTH); + x = (int) s.getMinX(); + y = (int) s.getMinY(); + at.setToTranslation(s.getMinX(), s.getMinY()); + } else { + image = img; } - + return delegate.drawImage(image, at, observer); + } + + /** transform the shape before letting the delegate apply 'hit' with it */ + public boolean hit(Rectangle rect, Shape s, boolean onStroke) { + Shape shape = ((ShapeTransformer) transformer).transform(s); + return delegate.hit(rect, shape, onStroke); + } + + public Graphics create() { + return delegate.create(); + } + + public void dispose() { + delegate.dispose(); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/ViewLensSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/ViewLensSupport.java index 6cc4a0b9..f6bc0467 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/ViewLensSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/transform/shape/ViewLensSupport.java @@ -1,15 +1,13 @@ /* * Copyright (c) 2003, The JUNG Authors * All rights reserved. - * + * * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. - * + * */ package edu.uci.ics.jung.visualization.transform.shape; -import java.awt.Dimension; - import edu.uci.ics.jung.algorithms.layout.NetworkElementAccessor; import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.RenderContext; @@ -21,73 +19,77 @@ import edu.uci.ics.jung.visualization.transform.AbstractLensSupport; import edu.uci.ics.jung.visualization.transform.LensSupport; import edu.uci.ics.jung.visualization.transform.LensTransformer; +import java.awt.Dimension; /** - * Uses a LensTransformer to use in the view - * transform. This one will distort Vertex shapes. - * - * @author Tom Nelson - * + * Uses a LensTransformer to use in the view transform. This one will distort Vertex shapes. * + * @author Tom Nelson */ -public class ViewLensSupport extends AbstractLensSupport - implements LensSupport { - - protected RenderContext renderContext; - protected GraphicsDecorator lensGraphicsDecorator; - protected GraphicsDecorator savedGraphicsDecorator; - protected NetworkElementAccessor pickSupport; - protected Renderer.Edge savedEdgeRenderer; - protected Renderer.Edge reshapingEdgeRenderer; +public class ViewLensSupport extends AbstractLensSupport implements LensSupport { - public ViewLensSupport(VisualizationViewer vv, - LensTransformer lensTransformer, - ModalGraphMouse lensGraphMouse) { - super(vv, lensGraphMouse); - this.renderContext = vv.getRenderContext(); - this.pickSupport = renderContext.getPickSupport(); - this.savedGraphicsDecorator = renderContext.getGraphicsContext(); - this.lensTransformer = lensTransformer; - Dimension d = vv.getSize(); - lensTransformer.setViewRadius(d.width/5); - this.lensGraphicsDecorator = new TransformingFlatnessGraphics(lensTransformer); - this.savedEdgeRenderer = vv.getRenderer().getEdgeRenderer(); - this.reshapingEdgeRenderer = new ReshapingEdgeRenderer(vv.getGraphLayout(), renderContext); - this.reshapingEdgeRenderer.setEdgeArrowRenderingSupport(savedEdgeRenderer.getEdgeArrowRenderingSupport()); + protected RenderContext renderContext; + protected GraphicsDecorator lensGraphicsDecorator; + protected GraphicsDecorator savedGraphicsDecorator; + protected NetworkElementAccessor pickSupport; + protected Renderer.Edge savedEdgeRenderer; + protected Renderer.Edge reshapingEdgeRenderer; + public ViewLensSupport( + VisualizationViewer vv, + LensTransformer lensTransformer, + ModalGraphMouse lensGraphMouse) { + super(vv, lensGraphMouse); + this.renderContext = vv.getRenderContext(); + this.pickSupport = renderContext.getPickSupport(); + this.savedGraphicsDecorator = renderContext.getGraphicsContext(); + this.lensTransformer = lensTransformer; + Dimension d = vv.getSize(); + lensTransformer.setViewRadius(d.width / 5); + this.lensGraphicsDecorator = new TransformingFlatnessGraphics(lensTransformer); + this.savedEdgeRenderer = vv.getRenderer().getEdgeRenderer(); + this.reshapingEdgeRenderer = + new ReshapingEdgeRenderer(vv.getGraphLayout(), renderContext); + this.reshapingEdgeRenderer.setEdgeArrowRenderingSupport( + savedEdgeRenderer.getEdgeArrowRenderingSupport()); + } + + public void activate() { + lensTransformer.setDelegate( + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)); + if (lens == null) { + lens = new Lens(lensTransformer); } - public void activate() { - lensTransformer.setDelegate(vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)); - if(lens == null) { - lens = new Lens(lensTransformer); - } - if(lensControls == null) { - lensControls = new LensControls(lensTransformer); - } - renderContext.setPickSupport(new ViewLensShapePickSupport(vv)); - lensTransformer.setDelegate(vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)); - vv.getRenderContext().getMultiLayerTransformer().setTransformer(Layer.VIEW, lensTransformer); - this.renderContext.setGraphicsContext(lensGraphicsDecorator); - vv.getRenderer().setEdgeRenderer(reshapingEdgeRenderer); - vv.prependPreRenderPaintable(lens); - vv.addPostRenderPaintable(lensControls); - vv.setGraphMouse(lensGraphMouse); - vv.setToolTipText(instructions); - vv.repaint(); + if (lensControls == null) { + lensControls = new LensControls(lensTransformer); } + renderContext.setPickSupport(new ViewLensShapePickSupport(vv)); + lensTransformer.setDelegate( + vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)); + vv.getRenderContext().getMultiLayerTransformer().setTransformer(Layer.VIEW, lensTransformer); + this.renderContext.setGraphicsContext(lensGraphicsDecorator); + vv.getRenderer().setEdgeRenderer(reshapingEdgeRenderer); + vv.prependPreRenderPaintable(lens); + vv.addPostRenderPaintable(lensControls); + vv.setGraphMouse(lensGraphMouse); + vv.setToolTipText(instructions); + vv.repaint(); + } - public void deactivate() { -// savedViewTransformer.setTransform(lensTransformer.getDelegate().getTransform()); -// vv.setViewTransformer(savedViewTransformer); - renderContext.setPickSupport(pickSupport); - vv.getRenderContext().getMultiLayerTransformer().setTransformer(Layer.VIEW, lensTransformer.getDelegate()); - vv.removePreRenderPaintable(lens); - vv.removePostRenderPaintable(lensControls); - this.renderContext.setGraphicsContext(savedGraphicsDecorator); - vv.setRenderContext(renderContext); - vv.setToolTipText(defaultToolTipText); - vv.setGraphMouse(graphMouse); - vv.getRenderer().setEdgeRenderer(savedEdgeRenderer); - vv.repaint(); - } -} \ No newline at end of file + public void deactivate() { + // savedViewTransformer.setTransform(lensTransformer.getDelegate().getTransform()); + // vv.setViewTransformer(savedViewTransformer); + renderContext.setPickSupport(pickSupport); + vv.getRenderContext() + .getMultiLayerTransformer() + .setTransformer(Layer.VIEW, lensTransformer.getDelegate()); + vv.removePreRenderPaintable(lens); + vv.removePostRenderPaintable(lensControls); + this.renderContext.setGraphicsContext(savedGraphicsDecorator); + vv.setRenderContext(renderContext); + vv.setToolTipText(defaultToolTipText); + vv.setGraphMouse(graphMouse); + vv.getRenderer().setEdgeRenderer(savedEdgeRenderer); + vv.repaint(); + } +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/Animator.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/Animator.java index f31c3cfe..bbed6c43 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/Animator.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/Animator.java @@ -5,78 +5,65 @@ * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. * - * + * */ package edu.uci.ics.jung.visualization.util; import edu.uci.ics.jung.algorithms.util.IterativeContext; -/** - * - * - * @author Tom Nelson - tomnelson@dev.java.net - * - */ +/** @author Tom Nelson - tomnelson@dev.java.net */ public class Animator implements Runnable { - - protected IterativeContext process; - protected boolean stop; - protected Thread thread; - - /** - * how long the relaxer thread pauses between iteration loops. - */ - protected long sleepTime = 10L; - - public Animator(IterativeContext process) { - this(process, 10L); - } + protected IterativeContext process; + protected boolean stop; + protected Thread thread; + + /** how long the relaxer thread pauses between iteration loops. */ + protected long sleepTime = 10L; + + public Animator(IterativeContext process) { + this(process, 10L); + } + + public Animator(IterativeContext process, long sleepTime) { + this.process = process; + this.sleepTime = sleepTime; + } + + /** @return the relaxer thread sleep time */ + public long getSleepTime() { + return sleepTime; + } - public Animator(IterativeContext process, long sleepTime) { - this.process = process; - this.sleepTime = sleepTime; - } + /** @param sleepTime the relaxer thread sleep time to set */ + public void setSleepTime(long sleepTime) { + this.sleepTime = sleepTime; + } - /** - * @return the relaxer thread sleep time - */ - public long getSleepTime() { - return sleepTime; - } + public void start() { + // in case its running + stop(); + stop = false; + thread = new Thread(this); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + } - /** - * @param sleepTime the relaxer thread sleep time to set - */ - public void setSleepTime(long sleepTime) { - this.sleepTime = sleepTime; - } - - public void start() { - // in case its running - stop(); - stop = false; - thread = new Thread(this); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); - } - - public synchronized void stop() { - stop = true; - } + public synchronized void stop() { + stop = true; + } - public void run() { - while (!process.done() && !stop) { + public void run() { + while (!process.done() && !stop) { - process.step(); + process.step(); - if (stop) - return; + if (stop) return; - try { - Thread.sleep(sleepTime); - } catch (InterruptedException ie) { - } - } - } + try { + Thread.sleep(sleepTime); + } catch (InterruptedException ie) { + } + } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/ArrowFactory.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/ArrowFactory.java index beadc1c1..54506adf 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/ArrowFactory.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/ArrowFactory.java @@ -1,7 +1,7 @@ /* * Created on Oct 19, 2004 * - * Copyright (c) 2004, The JUNG Authors + * Copyright (c) 2004, The JUNG Authors * * All rights reserved. * @@ -15,51 +15,45 @@ /** * A utility class for creating arrowhead shapes. - * + * * @author Joshua O'Madadhain */ -public class ArrowFactory -{ - /** - * Returns an arrowhead in the shape of a simple isosceles triangle - * with the specified base and height measurements. It is placed - * with the vertical axis along the negative x-axis, with its base - * centered on (0,0). - * - * @param base the width of the arrow's base - * @param height the arrow's height - * @return a path in the form of an isosceles triangle with dimensions {@code (base, height)} - */ - public static GeneralPath getWedgeArrow(float base, float height) - { - GeneralPath arrow = new GeneralPath(); - arrow.moveTo(0,0); - arrow.lineTo( - height, base/2.0f); - arrow.lineTo( - height, -base/2.0f); - arrow.lineTo( 0, 0 ); - return arrow; - } +public class ArrowFactory { + /** + * Returns an arrowhead in the shape of a simple isosceles triangle with the specified base and + * height measurements. It is placed with the vertical axis along the negative x-axis, with its + * base centered on (0,0). + * + * @param base the width of the arrow's base + * @param height the arrow's height + * @return a path in the form of an isosceles triangle with dimensions {@code (base, height)} + */ + public static GeneralPath getWedgeArrow(float base, float height) { + GeneralPath arrow = new GeneralPath(); + arrow.moveTo(0, 0); + arrow.lineTo(-height, base / 2.0f); + arrow.lineTo(-height, -base / 2.0f); + arrow.lineTo(0, 0); + return arrow; + } - /** - * Returns an arrowhead in the shape of an isosceles triangle - * with an isoceles-triangle notch taken out of the base, - * with the specified base and height measurements. It is placed - * with the vertical axis along the negative x-axis, with its base - * centered on (0,0). - * - * @param base the width of the arrow's base - * @param height the arrow's height - * @param notch_height the height of the arrow's notch - * @return a path in the form of a notched isosceles triangle - */ - public static GeneralPath getNotchedArrow(float base, float height, float notch_height) - { - GeneralPath arrow = new GeneralPath(); - arrow.moveTo(0,0); - arrow.lineTo(-height, base/2.0f); - arrow.lineTo(-(height - notch_height), 0); - arrow.lineTo(-height, -base/2.0f); - arrow.lineTo(0,0); - return arrow; - } + /** + * Returns an arrowhead in the shape of an isosceles triangle with an isoceles-triangle notch + * taken out of the base, with the specified base and height measurements. It is placed with the + * vertical axis along the negative x-axis, with its base centered on (0,0). + * + * @param base the width of the arrow's base + * @param height the arrow's height + * @param notch_height the height of the arrow's notch + * @return a path in the form of a notched isosceles triangle + */ + public static GeneralPath getNotchedArrow(float base, float height, float notch_height) { + GeneralPath arrow = new GeneralPath(); + arrow.moveTo(0, 0); + arrow.lineTo(-height, base / 2.0f); + arrow.lineTo(-(height - notch_height), 0); + arrow.lineTo(-height, -base / 2.0f); + arrow.lineTo(0, 0); + return arrow; + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/Caching.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/Caching.java index 51a1083e..38db5767 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/Caching.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/Caching.java @@ -5,20 +5,16 @@ * This software is open-source under the BSD license; see either "license.txt" * or https://github.com/jrtom/jung/blob/master/LICENSE for a description. * - * + * */ package edu.uci.ics.jung.visualization.util; /** - * Interface to provide external controls to an - * implementing class that manages a cache. - * @author Tom Nelson - tomnelson@dev.java.net + * Interface to provide external controls to an implementing class that manages a cache. * + * @author Tom Nelson - tomnelson@dev.java.net */ public interface Caching { - /** - * clear cache - * - */ - void clear(); + /** clear cache */ + void clear(); } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/ChangeEventSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/ChangeEventSupport.java index 7efbb2f4..13060c6b 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/ChangeEventSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/ChangeEventSupport.java @@ -14,29 +14,27 @@ /** * the implementing class provides support for ChangeEvents. - * - * @author Tom Nelson - tomnelson@dev.java.net * + * @author Tom Nelson - tomnelson@dev.java.net */ public interface ChangeEventSupport { - void addChangeListener(ChangeListener l); + void addChangeListener(ChangeListener l); - /** - * Removes a ChangeListener. - * @param l the listener to be removed - */ - void removeChangeListener(ChangeListener l); + /** + * Removes a ChangeListener. + * + * @param l the listener to be removed + */ + void removeChangeListener(ChangeListener l); - /** - * Returns an array of all the ChangeListeners added - * with addChangeListener(). - * - * @return all of the ChangeListeners added or an empty - * array if no listeners have been added - */ - ChangeListener[] getChangeListeners(); - - void fireStateChanged(); + /** + * Returns an array of all the ChangeListeners added with addChangeListener(). + * + * @return all of the ChangeListeners added or an empty array if no listeners have + * been added + */ + ChangeListener[] getChangeListeners(); -} \ No newline at end of file + void fireStateChanged(); +} diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/DefaultChangeEventSupport.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/DefaultChangeEventSupport.java index 02b143b3..cceeb388 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/DefaultChangeEventSupport.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/DefaultChangeEventSupport.java @@ -15,65 +15,56 @@ import javax.swing.event.EventListenerList; /** - * Basic implementation of ChangeEventSupport, using - * standard jdk classes - * - * @author Tom Nelson - tomnelson@dev.java.net + * Basic implementation of ChangeEventSupport, using standard jdk classes * + * @author Tom Nelson - tomnelson@dev.java.net */ public class DefaultChangeEventSupport implements ChangeEventSupport { - - Object eventSource; - /** - * holds the registered listeners - */ - protected EventListenerList listenerList = new EventListenerList(); - /** - * Only one ChangeEvent is needed - * instance since the - * event's only state is the source property. The source of events - * generated is always "this". - */ - protected transient ChangeEvent changeEvent; - - public DefaultChangeEventSupport(Object eventSource) { - this.eventSource = eventSource; - } + Object eventSource; + /** holds the registered listeners */ + protected EventListenerList listenerList = new EventListenerList(); - public void addChangeListener(ChangeListener l) { - listenerList.add(ChangeListener.class, l); - } - - public void removeChangeListener(ChangeListener l) { - listenerList.remove(ChangeListener.class, l); - } - - public ChangeListener[] getChangeListeners() { - return listenerList.getListeners(ChangeListener.class); - } + /** + * Only one ChangeEvent is needed instance since the event's only state is the source + * property. The source of events generated is always "this". + */ + protected transient ChangeEvent changeEvent; + + public DefaultChangeEventSupport(Object eventSource) { + this.eventSource = eventSource; + } + + public void addChangeListener(ChangeListener l) { + listenerList.add(ChangeListener.class, l); + } - /** - * Notifies all listeners that have registered interest for - * notification on this event type. The event instance - * is lazily created. - * The primary listeners will be views that need to be repainted - * because of changes in this model instance - * @see EventListenerList - */ - public void fireStateChanged() { - // Guaranteed to return a non-null array - Object[] listeners = listenerList.getListenerList(); - // Process the listeners last to first, notifying - // those that are interested in this event - for (int i = listeners.length-2; i>=0; i-=2) { - if (listeners[i]==ChangeListener.class) { - // Lazily create the event: - if (changeEvent == null) - changeEvent = new ChangeEvent(eventSource); - ((ChangeListener)listeners[i+1]).stateChanged(changeEvent); - } - } - } + public void removeChangeListener(ChangeListener l) { + listenerList.remove(ChangeListener.class, l); + } + public ChangeListener[] getChangeListeners() { + return listenerList.getListeners(ChangeListener.class); + } + + /** + * Notifies all listeners that have registered interest for notification on this event type. The + * event instance is lazily created. The primary listeners will be views that need to be repainted + * because of changes in this model instance + * + * @see EventListenerList + */ + public void fireStateChanged() { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ChangeListener.class) { + // Lazily create the event: + if (changeEvent == null) changeEvent = new ChangeEvent(eventSource); + ((ChangeListener) listeners[i + 1]).stateChanged(changeEvent); + } + } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/GeneralPathAsString.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/GeneralPathAsString.java index 78dfe428..4956748b 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/GeneralPathAsString.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/GeneralPathAsString.java @@ -6,46 +6,44 @@ public class GeneralPathAsString { - public static String toString(GeneralPath newPath) { - StringBuilder sb = new StringBuilder(); - float[] coords = new float[6]; - for(PathIterator iterator=newPath.getPathIterator(null); - iterator.isDone() == false; - iterator.next()) { - int type = iterator.currentSegment(coords); - switch(type) { - case PathIterator.SEG_MOVETO: - Point2D p = new Point2D.Float(coords[0], coords[1]); - sb.append("moveTo "+p+"--"); - break; - - case PathIterator.SEG_LINETO: - p = new Point2D.Float(coords[0], coords[1]); - sb.append("lineTo "+p+"--"); - break; - - case PathIterator.SEG_QUADTO: - p = new Point2D.Float(coords[0], coords[1]); - Point2D q = new Point2D.Float(coords[2], coords[3]); - sb.append("quadTo "+p+" controlled by "+q); - break; - - case PathIterator.SEG_CUBICTO: - p = new Point2D.Float(coords[0], coords[1]); - q = new Point2D.Float(coords[2], coords[3]); - Point2D r = new Point2D.Float(coords[4], coords[5]); - sb.append("cubeTo "+p+" controlled by "+q+","+r); - - break; - - case PathIterator.SEG_CLOSE: - newPath.closePath(); - sb.append("close"); - break; - - } - } - return sb.toString(); - } + public static String toString(GeneralPath newPath) { + StringBuilder sb = new StringBuilder(); + float[] coords = new float[6]; + for (PathIterator iterator = newPath.getPathIterator(null); + iterator.isDone() == false; + iterator.next()) { + int type = iterator.currentSegment(coords); + switch (type) { + case PathIterator.SEG_MOVETO: + Point2D p = new Point2D.Float(coords[0], coords[1]); + sb.append("moveTo " + p + "--"); + break; + + case PathIterator.SEG_LINETO: + p = new Point2D.Float(coords[0], coords[1]); + sb.append("lineTo " + p + "--"); + break; + + case PathIterator.SEG_QUADTO: + p = new Point2D.Float(coords[0], coords[1]); + Point2D q = new Point2D.Float(coords[2], coords[3]); + sb.append("quadTo " + p + " controlled by " + q); + break; + case PathIterator.SEG_CUBICTO: + p = new Point2D.Float(coords[0], coords[1]); + q = new Point2D.Float(coords[2], coords[3]); + Point2D r = new Point2D.Float(coords[4], coords[5]); + sb.append("cubeTo " + p + " controlled by " + q + "," + r); + + break; + + case PathIterator.SEG_CLOSE: + newPath.closePath(); + sb.append("close"); + break; + } + } + return sb.toString(); + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/ImageShapeUtils.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/ImageShapeUtils.java index 72e4cc46..4cfbf6c4 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/ImageShapeUtils.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/ImageShapeUtils.java @@ -10,6 +10,7 @@ package edu.uci.ics.jung.visualization.util; +import edu.uci.ics.jung.visualization.FourPassImageShaper; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; @@ -17,89 +18,86 @@ import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.IOException; - import javax.imageio.ImageIO; -import edu.uci.ics.jung.visualization.FourPassImageShaper; - public class ImageShapeUtils { - - /** - * Given the fileName of an image, possibly with a transparent - * background, return the Shape of the opaque part of the image - * @param fileName name of the image, loaded from the classpath - * @return the Shape - */ - public static Shape getShape(String fileName) { - return getShape(fileName, Integer.MAX_VALUE); - } - - /** - * Given the fileName of an image, possibly with a transparent - * background, return the Shape of the opaque part of the image - * @param fileName name of the image, loaded from the classpath - * @param max the maximum dimension of the traced shape - * @return the Shape - * - * @see #getShape(Image, int) - */ - public static Shape getShape(String fileName, int max) { - BufferedImage image = null; - try { - image = ImageIO.read(ImageShapeUtils.class.getResource(fileName)); - } catch(IOException ex) { - ex.printStackTrace(); - } - return getShape(image, max); - } - - /** - * Given an image, possibly with a transparent background, return - * the Shape of the opaque part of the image - * @param image the image whose shape is to be returned - * @return the Shape - */ - public static Shape getShape(Image image) { - return getShape(image, Integer.MAX_VALUE); - } - public static Shape getShape(Image image, int max) { - BufferedImage bi = - new BufferedImage(image.getWidth(null), image.getHeight(null), - BufferedImage.TYPE_INT_ARGB); - Graphics g = bi.createGraphics(); - g.drawImage(image, 0, 0, null); - g.dispose(); - return getShape(bi, max); + + /** + * Given the fileName of an image, possibly with a transparent background, return the Shape of the + * opaque part of the image + * + * @param fileName name of the image, loaded from the classpath + * @return the Shape + */ + public static Shape getShape(String fileName) { + return getShape(fileName, Integer.MAX_VALUE); + } + + /** + * Given the fileName of an image, possibly with a transparent background, return the Shape of the + * opaque part of the image + * + * @param fileName name of the image, loaded from the classpath + * @param max the maximum dimension of the traced shape + * @return the Shape + * @see #getShape(Image, int) + */ + public static Shape getShape(String fileName, int max) { + BufferedImage image = null; + try { + image = ImageIO.read(ImageShapeUtils.class.getResource(fileName)); + } catch (IOException ex) { + ex.printStackTrace(); } - - /** - * Given an image, possibly with a transparent background, return - * the Shape of the opaque part of the image - * - * If the image is larger than max in either direction, scale the - * image down to max-by-max, do the trace (on fewer points) then - * scale the resulting shape back up to the size of the original - * image. - * - * @param image the image to trace - * @param max used to restrict number of points in the resulting shape - * @return the Shape - */ - public static Shape getShape(BufferedImage image, int max) { - float width = image.getWidth(); - float height = image.getHeight(); - if(width > max || height > max) { - BufferedImage smaller = - new BufferedImage(max, max, BufferedImage.TYPE_INT_ARGB); - Graphics g = smaller.createGraphics(); - AffineTransform at = AffineTransform.getScaleInstance(max/width,max/height); - AffineTransform back = AffineTransform.getScaleInstance(width/max,height/max); - Graphics2D g2 = (Graphics2D)g; - g2.drawImage(image, at, null); - g2.dispose(); - return back.createTransformedShape(getShape(smaller)); - } else { - return FourPassImageShaper.getShape(image); - } + return getShape(image, max); + } + + /** + * Given an image, possibly with a transparent background, return the Shape of the opaque part of + * the image + * + * @param image the image whose shape is to be returned + * @return the Shape + */ + public static Shape getShape(Image image) { + return getShape(image, Integer.MAX_VALUE); + } + + public static Shape getShape(Image image, int max) { + BufferedImage bi = + new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB); + Graphics g = bi.createGraphics(); + g.drawImage(image, 0, 0, null); + g.dispose(); + return getShape(bi, max); + } + + /** + * Given an image, possibly with a transparent background, return the Shape of the opaque part of + * the image + * + *

            If the image is larger than max in either direction, scale the image down to max-by-max, do + * the trace (on fewer points) then scale the resulting shape back up to the size of the original + * image. + * + * @param image the image to trace + * @param max used to restrict number of points in the resulting shape + * @return the Shape + */ + public static Shape getShape(BufferedImage image, int max) { + float width = image.getWidth(); + float height = image.getHeight(); + if (width > max || height > max) { + BufferedImage smaller = new BufferedImage(max, max, BufferedImage.TYPE_INT_ARGB); + Graphics g = smaller.createGraphics(); + AffineTransform at = AffineTransform.getScaleInstance(max / width, max / height); + AffineTransform back = AffineTransform.getScaleInstance(width / max, height / max); + Graphics2D g2 = (Graphics2D) g; + g2.drawImage(image, at, null); + g2.dispose(); + return back.createTransformedShape(getShape(smaller)); + } else { + return FourPassImageShaper.getShape(image); } + } } diff --git a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/LabelWrapper.java b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/LabelWrapper.java index 3a87f0c1..f8613d62 100644 --- a/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/LabelWrapper.java +++ b/jung-visualization/src/main/java/edu/uci/ics/jung/visualization/util/LabelWrapper.java @@ -11,77 +11,71 @@ import com.google.common.base.Function; /** - * A utility to wrap long lines, creating html strings - * with line breaks at a settable max line length - * - * @author Tom Nelson - tomnelson@dev.java.net + * A utility to wrap long lines, creating html strings with line breaks at a settable max line + * length * + * @author Tom Nelson - tomnelson@dev.java.net */ -public class LabelWrapper implements Function { +public class LabelWrapper implements Function { + + int lineLength; + public static final String breaker = "

            "; + + /** Create an instance with default line break length = 10 */ + public LabelWrapper() { + this(10); + } + + /** + * Create an instance with passed line break length + * + * @param lineLength the max length for lines + */ + public LabelWrapper(int lineLength) { + this.lineLength = lineLength; + } + + /** call 'wrap' to transform the passed String */ + public String apply(String str) { + if (str != null) { + return wrap(str); + } else { + return null; + } + } - int lineLength; - public static final String breaker = "

            "; - - /** - * Create an instance with default line break length = 10 - * - */ - public LabelWrapper() { - this(10); - } - - /** - * Create an instance with passed line break length - * @param lineLength the max length for lines - */ - public LabelWrapper(int lineLength) { - this.lineLength = lineLength; - } + /** + * line-wrap the passed String as an html string with break Strings inserted. + * + * @param str + * @return + */ + private String wrap(String str) { + StringBuilder buf = new StringBuilder(str); + int len = lineLength; + while (len < buf.length()) { + int idx = buf.lastIndexOf(" ", len); + if (idx != -1) { + buf.replace(idx, idx + 1, breaker); + len = idx + breaker.length() + lineLength; + } else { + buf.insert(len, breaker); + len += breaker.length() + lineLength; + } + } + buf.insert(0, ""); + return buf.toString(); + } - /** - * call 'wrap' to transform the passed String - */ - public String apply(String str) { - if(str != null) { - return wrap(str); - } else { - return null; - } - } - - /** - * line-wrap the passed String as an html string with - * break Strings inserted. - * - * @param str - * @return - */ - private String wrap(String str) { - StringBuilder buf = new StringBuilder(str); - int len = lineLength; - while(len < buf.length()) { - int idx = buf.lastIndexOf(" ", len); - if(idx != -1) { - buf.replace(idx, idx+1, breaker); - len = idx + breaker.length() +lineLength; - } else { - buf.insert(len, breaker); - len += breaker.length() + lineLength; - } - } - buf.insert(0, ""); - return buf.toString(); - } - - public static void main(String[] args) { - String[] lines = { - "This is a line with many short words that I will break into shorter lines.", - "thisisalinewithnobreakssowhoknowswhereitwillwrap", - "short line" - }; - LabelWrapper w = new LabelWrapper(10); - for(int i=0; iShapes for drawing vertices. - * The available shapes include rectangles, rounded rectangles, ellipses, - * regular polygons, and regular stars. The dimensions of the requested - * shapes are defined by the specified vertex size function (specified by - * a {@code Function}) and vertex aspect ratio function - * (specified by a {@code Function}) implementations: the width - * of the bounding box of the shape is given by the vertex size, and the - * height is given by the size multiplied by the vertex's aspect ratio. - * + * A utility class for generating Shapes for drawing vertices. The available shapes + * include rectangles, rounded rectangles, ellipses, regular polygons, and regular stars. The + * dimensions of the requested shapes are defined by the specified vertex size function (specified + * by a {@code Function}) and vertex aspect ratio function (specified by a + * {@code Function}) implementations: the width of the bounding box of the shape + * is given by the vertex size, and the height is given by the size multiplied by the vertex's + * aspect ratio. + * * @author Joshua O'Madadhain */ -public class VertexShapeFactory -{ - protected Function vsf; - protected Function varf; - - /** - * Creates an instance with the specified vertex size and aspect ratio functions. - * - * @param vsf provides a size (width) for each vertex - * @param varf provides a height/width ratio for each vertex - */ - public VertexShapeFactory(Function vsf, Function varf) - { - this.vsf = vsf; - this.varf = varf; - } - - /** - * Creates a VertexShapeFactory with a constant size of - * 10 and a constant aspect ratio of 1. - */ - public VertexShapeFactory() - { - this(Functions.constant(10), - Functions.constant(1.0f)); - } - - private static final Rectangle2D theRectangle = new Rectangle2D.Float(); - - /** - * Returns a Rectangle2D whose width and - * height are defined by this instance's size and - * aspect ratio functions for this vertex. - * - * @param v the vertex for which the shape will be drawn - * @return a rectangle for this vertex - */ - public Rectangle2D getRectangle(V v) - { - float width = vsf.apply(v); - float height = width * varf.apply(v); - float h_offset = -(width / 2); - float v_offset = -(height / 2); - theRectangle.setFrame(h_offset, v_offset, width, height); - return theRectangle; - } +public class VertexShapeFactory { + protected Function vsf; + protected Function varf; - private static final Ellipse2D theEllipse = new Ellipse2D.Float(); - - /** - * Returns a Ellipse2D whose width and - * height are defined by this instance's size and - * aspect ratio functions for this vertex. - * - * @param v the vertex for which the shape will be drawn - * @return an ellipse for this vertex - */ - public Ellipse2D getEllipse(V v) - { - theEllipse.setFrame(getRectangle(v)); - return theEllipse; - } - - private static final RoundRectangle2D theRoundRectangle = - new RoundRectangle2D.Float(); - /** - * Returns a RoundRectangle2D whose width and - * height are defined by this instance's size and - * aspect ratio functions for this vertex. The arc size is - * set to be half the minimum of the height and width of the frame. - * - * @param v the vertex for which the shape will be drawn - * @return an round rectangle for this vertex - */ - public RoundRectangle2D getRoundRectangle(V v) - { - Rectangle2D frame = getRectangle(v); - float arc_size = (float)Math.min(frame.getHeight(), frame.getWidth()) / 2; - theRoundRectangle.setRoundRect(frame.getX(), frame.getY(), - frame.getWidth(), frame.getHeight(), arc_size, arc_size); - return theRoundRectangle; - } - - private static final GeneralPath thePolygon = new GeneralPath(); - - /** - * Returns a regular num_sides-sided - * Polygon whose bounding - * box's width and height are defined by this instance's size and - * aspect ratio functions for this vertex. - * - * @param v the vertex for which the shape will be drawn - * @param num_sides the number of sides of the polygon; must be ≥ 3. - * @return a regular polygon for this vertex - */ - public Shape getRegularPolygon(V v, int num_sides) - { - if (num_sides < 3) - throw new IllegalArgumentException("Number of sides must be >= 3"); - Rectangle2D frame = getRectangle(v); - float width = (float)frame.getWidth(); - float height = (float)frame.getHeight(); - - // generate coordinates - double angle = 0; - thePolygon.reset(); - thePolygon.moveTo(0,0); - thePolygon.lineTo(width, 0); - double theta = (2 * Math.PI) / num_sides; - for (int i = 2; i < num_sides; i++) - { - angle -= theta; - float delta_x = (float) (width * Math.cos(angle)); - float delta_y = (float) (width * Math.sin(angle)); - Point2D prev = thePolygon.getCurrentPoint(); - thePolygon.lineTo((float)prev.getX() + delta_x, (float)prev.getY() + delta_y); - } - thePolygon.closePath(); - - // scale polygon to be right size, translate to center at (0,0) - Rectangle2D r = thePolygon.getBounds2D(); - double scale_x = width / r.getWidth(); - double scale_y = height / r.getHeight(); - float translationX = (float) (r.getMinX() + r.getWidth()/2); - float translationY = (float) (r.getMinY() + r.getHeight()/2); - - AffineTransform at = AffineTransform.getScaleInstance(scale_x, scale_y); - at.translate(-translationX, -translationY); - - Shape shape = at.createTransformedShape(thePolygon); - return shape; + /** + * Creates an instance with the specified vertex size and aspect ratio functions. + * + * @param vsf provides a size (width) for each vertex + * @param varf provides a height/width ratio for each vertex + */ + public VertexShapeFactory(Function vsf, Function varf) { + this.vsf = vsf; + this.varf = varf; + } + + /** + * Creates a VertexShapeFactory with a constant size of 10 and a constant aspect + * ratio of 1. + */ + public VertexShapeFactory() { + this(Functions.constant(10), Functions.constant(1.0f)); + } + + private static final Rectangle2D theRectangle = new Rectangle2D.Float(); + + /** + * Returns a Rectangle2D whose width and height are defined by this instance's size + * and aspect ratio functions for this vertex. + * + * @param v the vertex for which the shape will be drawn + * @return a rectangle for this vertex + */ + public Rectangle2D getRectangle(V v) { + float width = vsf.apply(v); + float height = width * varf.apply(v); + float h_offset = -(width / 2); + float v_offset = -(height / 2); + theRectangle.setFrame(h_offset, v_offset, width, height); + return theRectangle; + } + + private static final Ellipse2D theEllipse = new Ellipse2D.Float(); + + /** + * Returns a Ellipse2D whose width and height are defined by this instance's size and + * aspect ratio functions for this vertex. + * + * @param v the vertex for which the shape will be drawn + * @return an ellipse for this vertex + */ + public Ellipse2D getEllipse(V v) { + theEllipse.setFrame(getRectangle(v)); + return theEllipse; + } + + private static final RoundRectangle2D theRoundRectangle = new RoundRectangle2D.Float(); + /** + * Returns a RoundRectangle2D whose width and height are defined by this instance's + * size and aspect ratio functions for this vertex. The arc size is set to be half the minimum of + * the height and width of the frame. + * + * @param v the vertex for which the shape will be drawn + * @return an round rectangle for this vertex + */ + public RoundRectangle2D getRoundRectangle(V v) { + Rectangle2D frame = getRectangle(v); + float arc_size = (float) Math.min(frame.getHeight(), frame.getWidth()) / 2; + theRoundRectangle.setRoundRect( + frame.getX(), frame.getY(), frame.getWidth(), frame.getHeight(), arc_size, arc_size); + return theRoundRectangle; + } + + private static final GeneralPath thePolygon = new GeneralPath(); + + /** + * Returns a regular num_sides-sided Polygon whose bounding box's width + * and height are defined by this instance's size and aspect ratio functions for this vertex. + * + * @param v the vertex for which the shape will be drawn + * @param num_sides the number of sides of the polygon; must be ≥ 3. + * @return a regular polygon for this vertex + */ + public Shape getRegularPolygon(V v, int num_sides) { + if (num_sides < 3) throw new IllegalArgumentException("Number of sides must be >= 3"); + Rectangle2D frame = getRectangle(v); + float width = (float) frame.getWidth(); + float height = (float) frame.getHeight(); + + // generate coordinates + double angle = 0; + thePolygon.reset(); + thePolygon.moveTo(0, 0); + thePolygon.lineTo(width, 0); + double theta = (2 * Math.PI) / num_sides; + for (int i = 2; i < num_sides; i++) { + angle -= theta; + float delta_x = (float) (width * Math.cos(angle)); + float delta_y = (float) (width * Math.sin(angle)); + Point2D prev = thePolygon.getCurrentPoint(); + thePolygon.lineTo((float) prev.getX() + delta_x, (float) prev.getY() + delta_y); } - - /** - * Returns a regular Polygon of num_points - * points whose bounding - * box's width and height are defined by this instance's size and - * aspect ratio functions for this vertex. - * - * @param v the vertex for which the shape will be drawn - * @param num_points the number of points of the polygon; must be ≥ 5. - * @return an star shape for this vertex - */ - public Shape getRegularStar(V v, int num_points) - { - if (num_points < 5) - throw new IllegalArgumentException("Number of sides must be >= 5"); - Rectangle2D frame = getRectangle(v); - float width = (float) frame.getWidth(); - float height = (float) frame.getHeight(); - - // generate coordinates - double theta = (2 * Math.PI) / num_points; - double angle = -theta/2; - thePolygon.reset(); - thePolygon.moveTo(0,0); - float delta_x = width * (float)Math.cos(angle); - float delta_y = width * (float)Math.sin(angle); - Point2D prev = thePolygon.getCurrentPoint(); - thePolygon.lineTo((float)prev.getX() + delta_x, (float)prev.getY() + delta_y); - for (int i = 1; i < num_points; i++) - { - angle += theta; - delta_x = width * (float)Math.cos(angle); - delta_y = width * (float)Math.sin(angle); - prev = thePolygon.getCurrentPoint(); - thePolygon.lineTo((float)prev.getX() + delta_x, (float)prev.getY() + delta_y); - angle -= theta*2; - delta_x = width * (float)Math.cos(angle); - delta_y = width * (float)Math.sin(angle); - prev = thePolygon.getCurrentPoint(); - thePolygon.lineTo((float)prev.getX() + delta_x, (float)prev.getY() + delta_y); - } - thePolygon.closePath(); - - // scale polygon to be right size, translate to center at (0,0) - Rectangle2D r = thePolygon.getBounds2D(); - double scale_x = width / r.getWidth(); - double scale_y = height / r.getHeight(); - - float translationX = (float) (r.getMinX() + r.getWidth()/2); - float translationY = (float) (r.getMinY() + r.getHeight()/2); - - AffineTransform at = AffineTransform.getScaleInstance(scale_x, scale_y); - at.translate(-translationX, -translationY); - - Shape shape = at.createTransformedShape(thePolygon); - return shape; + thePolygon.closePath(); + + // scale polygon to be right size, translate to center at (0,0) + Rectangle2D r = thePolygon.getBounds2D(); + double scale_x = width / r.getWidth(); + double scale_y = height / r.getHeight(); + float translationX = (float) (r.getMinX() + r.getWidth() / 2); + float translationY = (float) (r.getMinY() + r.getHeight() / 2); + + AffineTransform at = AffineTransform.getScaleInstance(scale_x, scale_y); + at.translate(-translationX, -translationY); + + Shape shape = at.createTransformedShape(thePolygon); + return shape; + } + + /** + * Returns a regular Polygon of num_points points whose bounding box's + * width and height are defined by this instance's size and aspect ratio functions for this + * vertex. + * + * @param v the vertex for which the shape will be drawn + * @param num_points the number of points of the polygon; must be ≥ 5. + * @return an star shape for this vertex + */ + public Shape getRegularStar(V v, int num_points) { + if (num_points < 5) throw new IllegalArgumentException("Number of sides must be >= 5"); + Rectangle2D frame = getRectangle(v); + float width = (float) frame.getWidth(); + float height = (float) frame.getHeight(); + + // generate coordinates + double theta = (2 * Math.PI) / num_points; + double angle = -theta / 2; + thePolygon.reset(); + thePolygon.moveTo(0, 0); + float delta_x = width * (float) Math.cos(angle); + float delta_y = width * (float) Math.sin(angle); + Point2D prev = thePolygon.getCurrentPoint(); + thePolygon.lineTo((float) prev.getX() + delta_x, (float) prev.getY() + delta_y); + for (int i = 1; i < num_points; i++) { + angle += theta; + delta_x = width * (float) Math.cos(angle); + delta_y = width * (float) Math.sin(angle); + prev = thePolygon.getCurrentPoint(); + thePolygon.lineTo((float) prev.getX() + delta_x, (float) prev.getY() + delta_y); + angle -= theta * 2; + delta_x = width * (float) Math.cos(angle); + delta_y = width * (float) Math.sin(angle); + prev = thePolygon.getCurrentPoint(); + thePolygon.lineTo((float) prev.getX() + delta_x, (float) prev.getY() + delta_y); } + thePolygon.closePath(); + + // scale polygon to be right size, translate to center at (0,0) + Rectangle2D r = thePolygon.getBounds2D(); + double scale_x = width / r.getWidth(); + double scale_y = height / r.getHeight(); + + float translationX = (float) (r.getMinX() + r.getWidth() / 2); + float translationY = (float) (r.getMinY() + r.getHeight() / 2); + + AffineTransform at = AffineTransform.getScaleInstance(scale_x, scale_y); + at.translate(-translationX, -translationY); + + Shape shape = at.createTransformedShape(thePolygon); + return shape; + } } diff --git a/jung-visualization/src/test/java/edu/uci/ics/jung/visualization/BasicVisualizationServerTest.java b/jung-visualization/src/test/java/edu/uci/ics/jung/visualization/BasicVisualizationServerTest.java index 38ff92e0..28d22622 100644 --- a/jung-visualization/src/test/java/edu/uci/ics/jung/visualization/BasicVisualizationServerTest.java +++ b/jung-visualization/src/test/java/edu/uci/ics/jung/visualization/BasicVisualizationServerTest.java @@ -2,7 +2,6 @@ import com.google.common.graph.Network; import com.google.common.graph.NetworkBuilder; - import edu.uci.ics.jung.algorithms.layout.CircleLayout; import edu.uci.ics.jung.visualization.picking.PickedState; import junit.framework.TestCase; @@ -14,11 +13,11 @@ public class BasicVisualizationServerTest extends TestCase { * in data like pickedVertexState to be lost. */ public void testRenderContextNotOverridden() { - Network graph = NetworkBuilder.directed().build(); + Network graph = NetworkBuilder.directed().build(); CircleLayout layout = new CircleLayout(graph.asGraph()); - BasicVisualizationServer server - = new BasicVisualizationServer(graph, layout); + BasicVisualizationServer server = + new BasicVisualizationServer(graph, layout); PickedState pickedVertexState = server.getRenderContext().getPickedVertexState(); assertNotNull(pickedVertexState); diff --git a/jung-visualization/src/test/java/edu/uci/ics/jung/visualization/TestImageShaper.java b/jung-visualization/src/test/java/edu/uci/ics/jung/visualization/TestImageShaper.java index 0b82411a..8c68338c 100644 --- a/jung-visualization/src/test/java/edu/uci/ics/jung/visualization/TestImageShaper.java +++ b/jung-visualization/src/test/java/edu/uci/ics/jung/visualization/TestImageShaper.java @@ -1,55 +1,53 @@ package edu.uci.ics.jung.visualization; +import edu.uci.ics.jung.visualization.util.ImageShapeUtils; import java.awt.Shape; import java.awt.geom.PathIterator; import java.awt.image.BufferedImage; - -import edu.uci.ics.jung.visualization.util.ImageShapeUtils; import junit.framework.TestCase; public class TestImageShaper extends TestCase { - - BufferedImage image; - - @Override - protected void setUp() throws Exception { - super.setUp(); - int width = 6; - int height = 5; - image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - for(int i=0; i vv; - - float crossover; - float scale; - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public void setUp() { - sc = new CrossoverScalingControl(); - Network network = NetworkBuilder.directed().build(); - vv = new BasicVisualizationServer(network, new FRLayout(network.asGraph())); - } - public void testCrossover() { - crossover = 2.0f; - scale = .5f; - sc.setCrossover(crossover); - sc.scale(vv, scale, new Point2D.Double()); -// System.err.println("crossover="+crossover); -// System.err.println("scale="+scale); -// System.err.println("layout scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getScale()); -// System.err.println("view scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getScale()); - } - public void testCrossover2() { - crossover = 2.0f; - scale = 1.5f; - sc.setCrossover(crossover); - sc.scale(vv, scale, new Point2D.Double()); -// System.err.println("crossover="+crossover); -// System.err.println("scale="+scale); -// System.err.println("layout scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getScale()); -// System.err.println("view scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getScale()); - - } - public void testCrossover3() { - crossover = 2.0f; - scale = 2.5f; - sc.setCrossover(crossover); - sc.scale(vv, scale, new Point2D.Double()); -// System.err.println("crossover="+crossover); -// System.err.println("scale="+scale); -// System.err.println("layout scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getScale()); -// System.err.println("view scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getScale()); - } - public void testCrossover4() { - crossover = 0.5f; - scale = 2.5f; - sc.setCrossover(crossover); - sc.scale(vv, scale, new Point2D.Double()); -// System.err.println("crossover="+crossover); -// System.err.println("scale="+scale); -// System.err.println("layout scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getScale()); -// System.err.println("view scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getScale()); - } - public void testCrossover5() { - crossover = 0.5f; - scale = .3f; - sc.setCrossover(crossover); - sc.scale(vv, scale, new Point2D.Double()); -// System.err.println("crossover="+crossover); -// System.err.println("scale="+scale); -// System.err.println("layout scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getScale()); -// System.err.println("view scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getScale()); - } + CrossoverScalingControl sc; + VisualizationServer vv; + + float crossover; + float scale; + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + public void setUp() { + sc = new CrossoverScalingControl(); + Network network = NetworkBuilder.directed().build(); + vv = new BasicVisualizationServer(network, new FRLayout(network.asGraph())); + } + + public void testCrossover() { + crossover = 2.0f; + scale = .5f; + sc.setCrossover(crossover); + sc.scale(vv, scale, new Point2D.Double()); + // System.err.println("crossover="+crossover); + // System.err.println("scale="+scale); + // System.err.println("layout scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getScale()); + // System.err.println("view scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getScale()); + } + + public void testCrossover2() { + crossover = 2.0f; + scale = 1.5f; + sc.setCrossover(crossover); + sc.scale(vv, scale, new Point2D.Double()); + // System.err.println("crossover="+crossover); + // System.err.println("scale="+scale); + // System.err.println("layout scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getScale()); + // System.err.println("view scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getScale()); + + } + + public void testCrossover3() { + crossover = 2.0f; + scale = 2.5f; + sc.setCrossover(crossover); + sc.scale(vv, scale, new Point2D.Double()); + // System.err.println("crossover="+crossover); + // System.err.println("scale="+scale); + // System.err.println("layout scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getScale()); + // System.err.println("view scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getScale()); + } + + public void testCrossover4() { + crossover = 0.5f; + scale = 2.5f; + sc.setCrossover(crossover); + sc.scale(vv, scale, new Point2D.Double()); + // System.err.println("crossover="+crossover); + // System.err.println("scale="+scale); + // System.err.println("layout scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getScale()); + // System.err.println("view scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getScale()); + } + + public void testCrossover5() { + crossover = 0.5f; + scale = .3f; + sc.setCrossover(crossover); + sc.scale(vv, scale, new Point2D.Double()); + // System.err.println("crossover="+crossover); + // System.err.println("scale="+scale); + // System.err.println("layout scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getScale()); + // System.err.println("view scale = "+vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getScale()); + } }