diff --git a/mastfrog-graph-adapter/src/main/java/com/mastfrog/graph/jung/adapter/GraphAdapter.java b/mastfrog-graph-adapter/src/main/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapter.java similarity index 99% rename from mastfrog-graph-adapter/src/main/java/com/mastfrog/graph/jung/adapter/GraphAdapter.java rename to mastfrog-graph-adapter/src/main/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapter.java index 8c7e8be..a40d9c8 100644 --- a/mastfrog-graph-adapter/src/main/java/com/mastfrog/graph/jung/adapter/GraphAdapter.java +++ b/mastfrog-graph-adapter/src/main/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapter.java @@ -23,7 +23,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -package com.mastfrog.graph.jung.adapter; +package com.mastfrog.graph.jgrapht.adapter; import com.mastfrog.abstractions.Wrapper; import com.mastfrog.abstractions.list.IndexedResolvable; diff --git a/mastfrog-graph-adapter/src/test/java/com/mastfrog/graph/jung/adapter/GraphAdapterTest.java b/mastfrog-graph-adapter/src/test/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapterTest.java similarity index 99% rename from mastfrog-graph-adapter/src/test/java/com/mastfrog/graph/jung/adapter/GraphAdapterTest.java rename to mastfrog-graph-adapter/src/test/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapterTest.java index e2a7ff3..fcb000b 100644 --- a/mastfrog-graph-adapter/src/test/java/com/mastfrog/graph/jung/adapter/GraphAdapterTest.java +++ b/mastfrog-graph-adapter/src/test/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapterTest.java @@ -23,7 +23,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -package com.mastfrog.graph.jung.adapter; +package com.mastfrog.graph.jgrapht.adapter; import com.mastfrog.abstractions.list.IndexedResolvable; import com.mastfrog.graph.IntGraph; diff --git a/mastfrog-jgrapht-adapter/pom.xml b/mastfrog-jgrapht-adapter/pom.xml new file mode 100644 index 0000000..b69beea --- /dev/null +++ b/mastfrog-jgrapht-adapter/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + com.mastfrog + visual-library-jung + 2.3 + + mastfrog-jgrapht-adapter + Adapters to wrapper com.mastfrog:graph bit-set based graphs as JUNG graphs + https://github.com:timboudreau/vl-jung + Visual Library + Mastfrog Graph Adapters + + git@github.com:timboudreau/vl-jung.git + scm:git:https://github.com:timboudreau/vl-jung.git + git@github.com:timboudreau/vl-jung.git + + + Github + https://github.com/timboudreau/vl-jung/issues + + + Mastfrog Technologies + https://mastfrog.com + + + + BSD-2-Clause + https://opensource.org/licenses/BSD-2-Clause + repo + + + + + timboudreau + Tim Boudreau + https://timboudreau.com + tim+github@timboudreau.com + + + + + com.mastfrog + abstractions + + + com.mastfrog + graph + + + org.jgrapht + jgrapht-core + + + junit + junit + test + + + ${project.groupId} + vl-jungrapht-demo + test + + + org.hamcrest + hamcrest-core + test + + + diff --git a/mastfrog-jgrapht-adapter/src/main/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapter.java b/mastfrog-jgrapht-adapter/src/main/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapter.java new file mode 100644 index 0000000..3556316 --- /dev/null +++ b/mastfrog-jgrapht-adapter/src/main/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapter.java @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2020, Mastfrog Technologies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.mastfrog.graph.jgrapht.adapter; + +import com.mastfrog.abstractions.Wrapper; +import com.mastfrog.abstractions.list.IndexedResolvable; +import com.mastfrog.graph.IntGraph; +import com.mastfrog.graph.ObjectGraph; +import com.mastfrog.graph.ObjectGraphVisitor; +import org.jgrapht.Graph; +import org.jgrapht.GraphType; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Supplier; + +/** + * Adapts com.mastfrog:graph's super-lightweight bit-set based graphs as JGraphT + * graphs. + * + * @author Tim Boudreau + */ +public final class GraphAdapter { + + /** + * Create a JGraphT graph for a Mastfrog graph. + * + * @param The vertex type + * @param The edge type + * @param graph The original graph + * @param edgeFactory The factory for edge objects + * @return A graph + */ + public static Graph wrap(ObjectGraph graph, EdgeFactory edgeFactory) { + return new WrappedObjectGraph<>(graph, edgeFactory); + } + + /** + * Create a JGraphT graph for a Mastfrog graph, using the built-in edge type, + * which unwraps the underlying IntGraph and represents edges internally as + * a pair of ints, for the most lightweight possible edge objects. + * + * @param The vertex type + * @param graph The original graph + * @param directed Whether or not to return a graph that represents itself + * to JGraphT as being a directed graph (be very sure!) + * @return A graph + */ + public static Graph> wrap(ObjectGraph graph, + boolean directed) { + DefaultEdgeFactory def = new DefaultEdgeFactory(graph, directed); + return new WrappedObjectGraph<>(graph, def); + } + + /** + * Create a JGraphT graph for a Mastfrog graph, using the built-in edge type, + * which unwraps the underlying IntGraph and represents edges internally as + * a pair of ints, for the most lightweight possible edge objects. + *

+ * This method will probe the graph to determine if it is directed or + * undirected; if the graph is very large and has very few cycles, this may + * be expensive - if you know for sure the graph can or cannot be directed, + * you should use one of the other methods that lets you pass that + * information. + *

+ * + * @param The vertex type + * @param graph The original graph + * @return A graph + */ + public static Graph> wrap(ObjectGraph graph) { + DefaultEdgeFactory def = new DefaultEdgeFactory(graph, null); + return new WrappedObjectGraph<>(graph, def); + } + + private GraphAdapter() { + throw new AssertionError(); + } + + /** + * A factory for edges which can provide information JGraphT graphs need. + * Resulting edge objects must have the following characteristics: + *
    + *
  • equals() must return true for identical edges (same + * source, same destination)
  • + *
  • hashCode() must return the same value for identical + * edges
  • + *
  • If the graph is undirected, equals() must return true + * for identical edges (same source, same destination) + * and for mirror-image edges (source=dest, dest=source)
  • + *
  • If the graph is undirected hashCode() must return the + * same value for identical edges + * and for mirror-image edges (hint, before hashing, sort on + * hashCode() or some similar means of achieving consistency) + *
  • + *
+ * + * @param The vertex type + * @param The edge type + */ + public interface EdgeFactory extends BiFunction { + + V sourceOf(E e); + + V destOf(E e); + } + + private static final class DefaultEdgeFactory implements + EdgeFactory.Edg> { + + private final ObjectGraph graph; + private final IntGraph ig; + private final IndexedResolvable contents; + private boolean undirected; + + DefaultEdgeFactory(ObjectGraph graph, Boolean directed) { + this.graph = graph; + Object[] stuff = dissect(graph); + contents = (IndexedResolvable) stuff[0]; + ig = (IntGraph) stuff[1]; + if (directed == null) { + ig.connectors().forEachSetBitAscending(id -> { + if (ig.isRecursive(id)) { + undirected = true; + return false; + } + return true; + }); + } else { + undirected = !directed.booleanValue(); + } + } + + private static Object[] dissect(ObjectGraph graph) { + Object[] result = new Object[2]; + graph.toIntGraph((contents, intGraph) -> { + result[0] = contents; + result[1] = intGraph; + }); + return result; + } + + @Override + public V sourceOf(Edg e) { + return e.source(); + } + + @Override + public V destOf(Edg e) { + return e.dest(); + } + + @Override + public Edg apply(V t, V u) { + return new Edg(contents.indexOf(t), contents.indexOf(u)); + } + + class Edg extends Edge { + + private final int src; + private final int dest; + + public Edg(int src, int dest) { + this.src = src; + this.dest = dest; + } + + @Override + public V source() { + return contents.forIndex(src); + } + + @Override + public V dest() { + return contents.forIndex(dest); + } + + @Override + public boolean equalsDirected(Edge other) { + if (!(other.getClass() == Edg.class)) { + return false; + } + Edg o = (Edg) other; + return o.src == src && o.dest == dest; + } + + @Override + public boolean equalsUndirected(Edge other) { + if (!(other.getClass() == Edg.class)) { + return false; + } + Edg o = (Edg) other; + return (o.src == src && o.dest == dest) + || (o.src == dest && o.dest == src); + } + + public String toString() { + return source() + ":" + dest(); + } + + @Override + public int hashCode() { + if (undirected && dest < src) { + return (7 * dest) + (41131 * src); + } + return (7 * src) + (41131 * dest); + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } else if (o == null || o.getClass() != Edg.class) { + return false; + } + Edg other = (Edg) o; + if (undirected) { + return equalsUndirected(other); + } else { + return equalsDirected(other); + } + } + } + } + + public static Edge create(V a, V b, boolean directed) { + return new DefaultEdge(a, b, directed); + } + + public static abstract class Edge { + + public abstract V source(); + + public abstract V dest(); + + public boolean equalsDirected(Edge other) { + return source().equals(other.source()) && dest().equals(other.dest()); + } + + public boolean equalsUndirected(Edge other) { + return equalsDirected(other) + || (source().equals(other.dest()) && dest().equals(other.source())); + } + } + + private static final class DefaultEdge extends Edge { + + private final V source; + private final V dest; + private final boolean directed; + + public DefaultEdge(V source, V dest, boolean directed) { + this.source = source; + this.dest = dest; + this.directed = directed; + } + + public V source() { + return source; + } + + public V dest() { + return dest; + } + + @Override + public String toString() { + if (directed) { + return source + ":" + dest; + } else { + if (source.hashCode() > dest.hashCode()) { + return dest + ":" + source; + } else { + return source + ":" + dest; + } + } + } + + @Override + public int hashCode() { + if (directed || source.hashCode() < dest.hashCode()) { + return source.hashCode() + (24133 * dest.hashCode()); + } + return dest.hashCode() + (24133 * source.hashCode()); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } else if (o == null || o.getClass() != DefaultEdge.class) { + return false; + } + DefaultEdge other = (DefaultEdge) o; + boolean dir = directed || other.directed; + if (dir) { + return equalsDirected(other); + } else { + return equalsUndirected(other); + } + } + } + + static class WrappedObjectGraph implements Graph, Wrapper> { + + private final ObjectGraph graph; + private final EdgeFactory edgeFactory; + + public WrappedObjectGraph(ObjectGraph graph, EdgeFactory edgeFactory) { + this.graph = graph; + this.edgeFactory = edgeFactory; + } + + @Override + public ObjectGraph wrapped() { + return graph; + } + + @Override + public F find(Class what) { + if (IntGraph.class == what) { + IntGraph[] ig = new IntGraph[1]; + graph.toIntGraph((ir, g) -> ig[0] = g); + return (F) what.cast(ig[0]); + } + return Wrapper.super.find(what); + } + + @Override + public boolean has(Class what) { + if (IntGraph.class == what) { + return true; + } + return Wrapper.super.has(what); + } + + @Override + public ObjectGraph root() { + return graph; + } + + private Set edges; + + private Set allVertices; + + + @Override + public boolean containsVertex(V vertex) { + return vertexSet().contains(vertex); + } + + @Override + public Set edgeSet() { + if (edges != null) { + return edges; + } + Set result = new HashSet<>(); + graph.walk(new ObjectGraphVisitor() { + LinkedList stack = new LinkedList<>(); + + @Override + public void enterNode(V node, int depth) { + if (stack.size() > 0) { + V curr = stack.peek(); + result.add(edgeFactory.apply(curr, node)); + } + stack.push(node); + } + + @Override + public void exitNode(V node, int depth) { + stack.pop(); + } + }); + return edges = Collections.unmodifiableSet(result); + } + + @Override + public int degreeOf(V vertex) { + return inDegreeOf(vertex) + outDegreeOf(vertex); + } + + @Override + public Set edgesOf(V vertex) { + return null; + } + + @Override + public int inDegreeOf(V vertex) { + return graph.inboundReferenceCount(vertex); + } + + @Override + public Set incomingEdgesOf(V vertex) { + Set result = new HashSet<>(); + graph.parents(vertex).forEach(o -> result.add(edgeFactory.apply(vertex, o))); + return result; + } + + @Override + public int outDegreeOf(V vertex) { + return graph.outboundReferenceCount(vertex); + } + + @Override + public Set outgoingEdgesOf(V vertex) { + Set result = new HashSet<>(); + graph.children(vertex).forEach(o -> result.add(edgeFactory.apply(vertex, o))); + return result; + } + + @Override + public boolean removeAllEdges(Collection edges) { + throw new UnsupportedOperationException("immutable"); } + + @Override + public Set removeAllEdges(V sourceVertex, V targetVertex) { + throw new UnsupportedOperationException("immutable"); } + + @Override + public boolean removeAllVertices(Collection vertices) { + throw new UnsupportedOperationException("immutable"); } + + @Override + public E removeEdge(V sourceVertex, V targetVertex) { + throw new UnsupportedOperationException("immutable"); } + + @Override + public boolean containsEdge(E edge) { + V src = edgeFactory.sourceOf(edge); + V dest = edgeFactory.destOf(edge); + return graph.children(src).contains(dest); + } + + @Override + public Set getAllEdges(V v1, V v2) { + Set edges = new HashSet<>(); + if (graph.parents(v2).contains(v1)) { + edges.add(edgeFactory.apply(v1, v2)); + } + if (graph.parents(v2).contains(v1)) { + edges.add(edgeFactory.apply(v2, v1)); + } + return edges; + } + + @Override + public E getEdge(V v1, V v2) { + if (graph.parents(v2).contains(v1)) { + return edgeFactory.apply(v1, v2); + } else if (graph.parents(v2).contains(v1)) { + return edgeFactory.apply(v2, v1); + } + return null; + } + + @Override + public Supplier getVertexSupplier() { + return null; + } + + @Override + public Supplier getEdgeSupplier() { + return null; + } + + @Override + public E addEdge(V sourceVertex, V targetVertex) { + throw new UnsupportedOperationException("Immutable."); } + + @Override + public boolean addEdge(V sourceVertex, V targetVertex, E e) { + throw new UnsupportedOperationException("Immutable."); } + + @Override + public V addVertex() { + throw new UnsupportedOperationException("Immutable."); } + + @Override + public boolean addVertex(V vertex) { + throw new UnsupportedOperationException("Immutable."); + } + + @Override + public boolean containsEdge(V sourceVertex, V targetVertex) { + return false; + } + + @Override + public boolean removeVertex(V vertex) { + throw new UnsupportedOperationException("immutable"); + } + + @Override + public Set vertexSet() { + if (allVertices != null) { + return allVertices; + } + Set items = new HashSet<>(); + for (int i = 0; i < graph.size(); i++) { + items.add(graph.toNode(i)); + } + return allVertices = Collections.unmodifiableSet(items); + } + + @Override + public V getEdgeSource(E e) { + return null; + } + + @Override + public V getEdgeTarget(E e) { + return null; + } + + @Override + public GraphType getType() { + return null; + } + + @Override + public double getEdgeWeight(E e) { + return 0; + } + + @Override + public void setEdgeWeight(E e, double weight) { + + } + + @Override + public boolean removeEdge(E edge) { + throw new UnsupportedOperationException("immutable"); + } + + } +} diff --git a/mastfrog-jgrapht-adapter/src/test/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapterTest.java b/mastfrog-jgrapht-adapter/src/test/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapterTest.java new file mode 100644 index 0000000..f1c734d --- /dev/null +++ b/mastfrog-jgrapht-adapter/src/test/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapterTest.java @@ -0,0 +1,1923 @@ +/* + * Copyright (c) 2020, Mastfrog Technologies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.mastfrog.graph.jgrapht.adapter; + +import com.mastfrog.abstractions.list.IndexedResolvable; +import com.mastfrog.graph.IntGraph; +import com.mastfrog.graph.ObjectGraph; +import java.awt.EventQueue; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; + +import com.timboudreau.vl.jungrapht.demo.App; +import org.jgrapht.Graph; +import org.junit.Test; +import org.openide.util.Exceptions; + +/** + * + * @author Tim Boudreau + */ +public class GraphAdapterTest { + + @Test + public void testSomeMethod() throws IOException, InterruptedException, InvocationTargetException { + if (true) { + // for manual testing + return; + } + IntGraph ig = sensorConfigLanguageRules(); + IXA ixa = new IXA(ig.size()); + List ixs = new ArrayList<>(); + for (int i = 0; i < ixa.size; i++) { + ixs.add(i); + } + ObjectGraph og = ig.toObjectGraph(ixs); + Graph> g = GraphAdapter.wrap(og, true); + + EventQueue.invokeAndWait(() -> { + try { + App.showDemo(g); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); +// System.exit(1); + } + }); + Thread.currentThread().join(); + } + + static class IXA implements IndexedResolvable { + + private final int size; + + public IXA(int size) { + this.size = size; + } + + @Override + public Integer forIndex(int index) { + return index; + } + + @Override + public int size() { + return size; + } + + @Override + public int indexOf(Object obj) { + return (Integer) obj; + } + + } + + static IntGraph sensorConfigLanguageRules() { + BitSet[] parents = new BitSet[67]; + BitSet[] children = new BitSet[67]; + parents[0] = new BitSet(); + parents[0].set(54); + parents[0].set(66); + children[0] = new BitSet(); + parents[1] = new BitSet(); + children[1] = new BitSet(); + children[1].set(30); + parents[2] = new BitSet(); + parents[2].set(38); + children[2] = new BitSet(); + parents[3] = new BitSet(); + parents[3].set(4); + parents[3].set(7); + children[3] = new BitSet(); + parents[4] = new BitSet(); + parents[4].set(36); + children[4] = new BitSet(); + children[4].set(3); + parents[5] = new BitSet(); + parents[5].set(24); + children[5] = new BitSet(); + parents[6] = new BitSet(); + parents[6].set(25); + children[6] = new BitSet(); + parents[7] = new BitSet(); + parents[7].set(38); + children[7] = new BitSet(); + children[7].set(3); + children[7].set(8); + parents[8] = new BitSet(); + parents[8].set(7); + children[8] = new BitSet(); + parents[9] = new BitSet(); + parents[9].set(49); + children[9] = new BitSet(); + parents[10] = new BitSet(); + parents[10].set(57); + children[10] = new BitSet(); + parents[11] = new BitSet(); + parents[11].set(41); + children[11] = new BitSet(); + parents[12] = new BitSet(); + parents[12].set(43); + children[12] = new BitSet(); + parents[13] = new BitSet(); + parents[13].set(49); + children[13] = new BitSet(); + parents[14] = new BitSet(); + parents[14].set(45); + children[14] = new BitSet(); + parents[15] = new BitSet(); + parents[15].set(47); + children[15] = new BitSet(); + parents[16] = new BitSet(); + parents[16].set(48); + children[16] = new BitSet(); + parents[17] = new BitSet(); + parents[17].set(52); + children[17] = new BitSet(); + parents[18] = new BitSet(); + parents[18].set(53); + children[18] = new BitSet(); + parents[19] = new BitSet(); + parents[19].set(57); + children[19] = new BitSet(); + parents[20] = new BitSet(); + parents[20].set(58); + children[20] = new BitSet(); + parents[21] = new BitSet(); + parents[21].set(59); + children[21] = new BitSet(); + parents[22] = new BitSet(); + parents[22].set(65); + children[22] = new BitSet(); + parents[23] = new BitSet(); + parents[23].set(60); + parents[23].set(66); + children[23] = new BitSet(); + children[23].set(24); + children[23].set(25); + parents[24] = new BitSet(); + parents[24].set(23); + children[24] = new BitSet(); + children[24].set(5); + parents[25] = new BitSet(); + parents[25].set(23); + children[25] = new BitSet(); + children[25].set(6); + parents[26] = new BitSet(); + parents[26].set(52); + children[26] = new BitSet(); + parents[27] = new BitSet(); + parents[27].set(50); + children[27] = new BitSet(); + parents[28] = new BitSet(); + parents[28].set(52); + parents[28].set(58); + parents[28].set(62); + children[28] = new BitSet(); + parents[29] = new BitSet(); + parents[29].set(56); + children[29] = new BitSet(); + parents[30] = new BitSet(); + parents[30].set(1); + children[30] = new BitSet(); + parents[31] = new BitSet(); + parents[31].set(52); + children[31] = new BitSet(); + parents[32] = new BitSet(); + parents[32].set(39); + parents[32].set(43); + parents[32].set(53); + parents[32].set(59); + children[32] = new BitSet(); + parents[33] = new BitSet(); + parents[33].set(50); + children[33] = new BitSet(); + parents[34] = new BitSet(); + children[34] = new BitSet(); + parents[35] = new BitSet(); + children[35] = new BitSet(); + parents[36] = new BitSet(); + parents[36].set(43); + parents[36].set(46); + parents[36].set(47); + parents[36].set(50); + parents[36].set(56); + parents[36].set(57); + parents[36].set(59); + parents[36].set(66); + children[36] = new BitSet(); + children[36].set(4); + parents[37] = new BitSet(); + parents[37].set(64); + parents[37].set(66); + children[37] = new BitSet(); + parents[38] = new BitSet(); + children[38] = new BitSet(); + children[38].set(2); + children[38].set(7); + parents[39] = new BitSet(); + parents[39].set(40); + children[39] = new BitSet(); + children[39].set(32); + children[39].set(45); + children[39].set(57); + children[39].set(58); + parents[40] = new BitSet(); + parents[40].set(42); + children[40] = new BitSet(); + children[40].set(39); + parents[41] = new BitSet(); + parents[41].set(61); + children[41] = new BitSet(); + children[41].set(11); + children[41].set(63); + parents[42] = new BitSet(); + children[42] = new BitSet(); + children[42].set(40); + children[42].set(44); + children[42].set(52); + children[42].set(55); + children[42].set(59); + parents[43] = new BitSet(); + parents[43].set(44); + children[43] = new BitSet(); + children[43].set(12); + children[43].set(32); + children[43].set(36); + children[43].set(49); + children[43].set(64); + parents[44] = new BitSet(); + parents[44].set(42); + children[44] = new BitSet(); + children[44].set(43); + parents[45] = new BitSet(); + parents[45].set(39); + children[45] = new BitSet(); + children[45].set(14); + children[45].set(60); + parents[46] = new BitSet(); + parents[46].set(63); + children[46] = new BitSet(); + children[46].set(36); + parents[47] = new BitSet(); + parents[47].set(51); + children[47] = new BitSet(); + children[47].set(15); + children[47].set(36); + parents[48] = new BitSet(); + parents[48].set(61); + children[48] = new BitSet(); + children[48].set(16); + children[48].set(63); + parents[49] = new BitSet(); + parents[49].set(43); + children[49] = new BitSet(); + children[49].set(9); + children[49].set(13); + parents[50] = new BitSet(); + parents[50].set(52); + children[50] = new BitSet(); + children[50].set(27); + children[50].set(33); + children[50].set(36); + children[50].set(63); + parents[51] = new BitSet(); + parents[51].set(58); + children[51] = new BitSet(); + children[51].set(47); + children[51].set(54); + parents[52] = new BitSet(); + parents[52].set(42); + children[52] = new BitSet(); + children[52].set(17); + children[52].set(26); + children[52].set(28); + children[52].set(31); + children[52].set(50); + children[52].set(64); + parents[53] = new BitSet(); + parents[53].set(55); + children[53] = new BitSet(); + children[53].set(18); + children[53].set(32); + children[53].set(54); + children[53].set(62); + parents[54] = new BitSet(); + parents[54].set(51); + parents[54].set(53); + parents[54].set(57); + children[54] = new BitSet(); + children[54].set(0); + parents[55] = new BitSet(); + parents[55].set(42); + children[55] = new BitSet(); + children[55].set(53); + parents[56] = new BitSet(); + parents[56].set(63); + children[56] = new BitSet(); + children[56].set(29); + children[56].set(36); + parents[57] = new BitSet(); + parents[57].set(39); + children[57] = new BitSet(); + children[57].set(10); + children[57].set(19); + children[57].set(36); + children[57].set(54); + children[57].set(63); + parents[58] = new BitSet(); + parents[58].set(39); + children[58] = new BitSet(); + children[58].set(20); + children[58].set(28); + children[58].set(51); + children[58].set(64); + parents[59] = new BitSet(); + parents[59].set(42); + children[59] = new BitSet(); + children[59].set(21); + children[59].set(32); + children[59].set(36); + parents[60] = new BitSet(); + parents[60].set(45); + children[60] = new BitSet(); + children[60].set(23); + parents[61] = new BitSet(); + parents[61].set(62); + children[61] = new BitSet(); + children[61].set(41); + children[61].set(48); + children[61].set(65); + parents[62] = new BitSet(); + parents[62].set(53); + children[62] = new BitSet(); + children[62].set(28); + children[62].set(61); + parents[63] = new BitSet(); + parents[63].set(41); + parents[63].set(48); + parents[63].set(50); + parents[63].set(57); + parents[63].set(65); + children[63] = new BitSet(); + children[63].set(46); + children[63].set(56); + children[63].set(64); + parents[64] = new BitSet(); + parents[64].set(43); + parents[64].set(52); + parents[64].set(58); + parents[64].set(63); + children[64] = new BitSet(); + children[64].set(37); + parents[65] = new BitSet(); + parents[65].set(61); + children[65] = new BitSet(); + children[65].set(22); + children[65].set(63); + parents[66] = new BitSet(); + children[66] = new BitSet(); + children[66].set(0); + children[66].set(23); + children[66].set(36); + children[66].set(37); + return IntGraph.create(parents, children); + } + + static IntGraph rustGrammarRules() { + // Rule graph of Rust.g4 + BitSet[] parents = new BitSet[252]; + BitSet[] children = new BitSet[252]; + parents[0] = new BitSet(); + parents[0].set(166); + parents[0].set(213); + parents[0].set(214); + parents[0].set(215); + parents[0].set(242); + children[0] = new BitSet(); + parents[1] = new BitSet(); + parents[1].set(220); + children[1] = new BitSet(); + parents[2] = new BitSet(); + parents[2].set(185); + parents[2].set(196); + parents[2].set(231); + parents[2].set(238); + parents[2].set(241); + children[2] = new BitSet(); + parents[3] = new BitSet(); + parents[3].set(168); + children[3] = new BitSet(); + parents[4] = new BitSet(); + parents[4].set(168); + children[4] = new BitSet(); + parents[5] = new BitSet(); + parents[5].set(168); + children[5] = new BitSet(); + parents[6] = new BitSet(); + parents[6].set(168); + children[6] = new BitSet(); + parents[7] = new BitSet(); + parents[7].set(168); + children[7] = new BitSet(); + parents[8] = new BitSet(); + parents[8].set(168); + children[8] = new BitSet(); + parents[9] = new BitSet(); + parents[9].set(168); + children[9] = new BitSet(); + parents[10] = new BitSet(); + parents[10].set(168); + children[10] = new BitSet(); + parents[11] = new BitSet(); + parents[11].set(168); + children[11] = new BitSet(); + parents[12] = new BitSet(); + children[12] = new BitSet(); + parents[13] = new BitSet(); + parents[13].set(23); + parents[13].set(164); + parents[13].set(207); + parents[13].set(242); + children[13] = new BitSet(); + parents[14] = new BitSet(); + children[14] = new BitSet(); + parents[15] = new BitSet(); + parents[15].set(212); + children[15] = new BitSet(); + parents[16] = new BitSet(); + children[16] = new BitSet(); + parents[17] = new BitSet(); + parents[17].set(18); + parents[17].set(29); + children[17] = new BitSet(); + children[17].set(20); + children[17].set(37); + children[17].set(126); + children[17].set(128); + children[17].set(139); + parents[18] = new BitSet(); + parents[18].set(31); + children[18] = new BitSet(); + children[18].set(17); + children[18].set(107); + parents[19] = new BitSet(); + parents[19].set(107); + parents[19].set(128); + children[19] = new BitSet(); + parents[20] = new BitSet(); + parents[20].set(17); + parents[20].set(171); + parents[20].set(179); + parents[20].set(206); + children[20] = new BitSet(); + parents[21] = new BitSet(); + parents[21].set(179); + parents[21].set(197); + children[21] = new BitSet(); + children[21].set(43); + parents[22] = new BitSet(); + parents[22].set(72); + children[22] = new BitSet(); + parents[23] = new BitSet(); + parents[23].set(23); + children[23] = new BitSet(); + children[23].set(13); + children[23].set(23); + children[23].set(24); + children[23].set(138); + parents[24] = new BitSet(); + parents[24].set(23); + children[24] = new BitSet(); + parents[25] = new BitSet(); + parents[25].set(198); + children[25] = new BitSet(); + parents[26] = new BitSet(); + parents[26].set(165); + parents[26].set(171); + parents[26].set(203); + children[26] = new BitSet(); + children[26].set(67); + children[26].set(147); + parents[27] = new BitSet(); + children[27] = new BitSet(); + parents[28] = new BitSet(); + children[28] = new BitSet(); + parents[29] = new BitSet(); + parents[29].set(165); + parents[29].set(203); + children[29] = new BitSet(); + children[29].set(17); + children[29].set(30); + children[29].set(114); + children[29].set(137); + parents[30] = new BitSet(); + parents[30].set(29); + children[30] = new BitSet(); + parents[31] = new BitSet(); + children[31] = new BitSet(); + children[31].set(18); + children[31].set(32); + children[31].set(114); + children[31].set(115); + parents[32] = new BitSet(); + parents[32].set(31); + children[32] = new BitSet(); + parents[33] = new BitSet(); + parents[33].set(36); + parents[33].set(129); + children[33] = new BitSet(); + children[33].set(125); + children[33].set(128); + parents[34] = new BitSet(); + parents[34].set(107); + children[34] = new BitSet(); + parents[35] = new BitSet(); + parents[35].set(198); + children[35] = new BitSet(); + parents[36] = new BitSet(); + children[36] = new BitSet(); + children[36].set(33); + children[36].set(114); + children[36].set(137); + parents[37] = new BitSet(); + parents[37].set(17); + parents[37].set(204); + children[37] = new BitSet(); + parents[38] = new BitSet(); + parents[38].set(215); + parents[38].set(228); + parents[38].set(229); + parents[38].set(248); + children[38] = new BitSet(); + parents[39] = new BitSet(); + parents[39].set(165); + parents[39].set(175); + parents[39].set(180); + parents[39].set(181); + parents[39].set(199); + parents[39].set(202); + parents[39].set(208); + parents[39].set(210); + parents[39].set(214); + parents[39].set(226); + parents[39].set(227); + parents[39].set(230); + parents[39].set(232); + parents[39].set(238); + parents[39].set(244); + parents[39].set(245); + children[39] = new BitSet(); + parents[40] = new BitSet(); + children[40] = new BitSet(); + parents[41] = new BitSet(); + children[41] = new BitSet(); + parents[42] = new BitSet(); + parents[42].set(183); + parents[42].set(250); + children[42] = new BitSet(); + parents[43] = new BitSet(); + parents[43].set(21); + parents[43].set(69); + parents[43].set(72); + parents[43].set(100); + parents[43].set(118); + parents[43].set(119); + children[43] = new BitSet(); + parents[44] = new BitSet(); + children[44] = new BitSet(); + parents[45] = new BitSet(); + parents[45].set(176); + children[45] = new BitSet(); + children[45].set(46); + parents[46] = new BitSet(); + parents[46].set(45); + children[46] = new BitSet(); + parents[47] = new BitSet(); + children[47] = new BitSet(); + parents[48] = new BitSet(); + parents[48].set(69); + parents[48].set(179); + parents[48].set(247); + children[48] = new BitSet(); + parents[49] = new BitSet(); + parents[49].set(118); + parents[49].set(228); + children[49] = new BitSet(); + parents[50] = new BitSet(); + parents[50].set(119); + children[50] = new BitSet(); + parents[51] = new BitSet(); + parents[51].set(204); + children[51] = new BitSet(); + parents[52] = new BitSet(); + children[52] = new BitSet(); + parents[53] = new BitSet(); + parents[53].set(190); + parents[53].set(217); + parents[53].set(218); + children[53] = new BitSet(); + parents[54] = new BitSet(); + parents[54].set(173); + children[54] = new BitSet(); + parents[55] = new BitSet(); + parents[55].set(172); + parents[55].set(204); + children[55] = new BitSet(); + parents[56] = new BitSet(); + parents[56].set(213); + children[56] = new BitSet(); + parents[57] = new BitSet(); + parents[57].set(69); + children[57] = new BitSet(); + children[57].set(155); + parents[58] = new BitSet(); + parents[58].set(194); + children[58] = new BitSet(); + parents[59] = new BitSet(); + parents[59].set(177); + children[59] = new BitSet(); + children[59].set(60); + parents[60] = new BitSet(); + parents[60].set(59); + children[60] = new BitSet(); + parents[61] = new BitSet(); + children[61] = new BitSet(); + parents[62] = new BitSet(); + parents[62].set(168); + parents[62].set(193); + parents[62].set(210); + parents[62].set(240); + children[62] = new BitSet(); + parents[63] = new BitSet(); + parents[63].set(181); + parents[63].set(183); + children[63] = new BitSet(); + parents[64] = new BitSet(); + parents[64].set(66); + parents[64].set(187); + parents[64].set(198); + children[64] = new BitSet(); + parents[65] = new BitSet(); + parents[65].set(66); + parents[65].set(187); + parents[65].set(198); + children[65] = new BitSet(); + parents[66] = new BitSet(); + children[66] = new BitSet(); + children[66].set(64); + children[66].set(65); + parents[67] = new BitSet(); + parents[67].set(26); + children[67] = new BitSet(); + parents[68] = new BitSet(); + parents[68].set(175); + parents[68].set(209); + children[68] = new BitSet(); + parents[69] = new BitSet(); + parents[69].set(186); + children[69] = new BitSet(); + children[69].set(43); + children[69].set(48); + children[69].set(57); + parents[70] = new BitSet(); + parents[70].set(192); + children[70] = new BitSet(); + parents[71] = new BitSet(); + parents[71].set(188); + children[71] = new BitSet(); + parents[72] = new BitSet(); + parents[72].set(197); + children[72] = new BitSet(); + children[72].set(22); + children[72].set(43); + children[72].set(75); + children[72].set(82); + children[72].set(108); + children[72].set(155); + parents[73] = new BitSet(); + parents[73].set(173); + children[73] = new BitSet(); + parents[74] = new BitSet(); + parents[74].set(115); + parents[74].set(117); + children[74] = new BitSet(); + parents[75] = new BitSet(); + parents[75].set(72); + children[75] = new BitSet(); + parents[76] = new BitSet(); + parents[76].set(198); + parents[76].set(222); + children[76] = new BitSet(); + parents[77] = new BitSet(); + parents[77].set(198); + parents[77].set(222); + children[77] = new BitSet(); + parents[78] = new BitSet(); + parents[78].set(198); + parents[78].set(222); + children[78] = new BitSet(); + parents[79] = new BitSet(); + parents[79].set(198); + parents[79].set(222); + children[79] = new BitSet(); + parents[80] = new BitSet(); + parents[80].set(198); + parents[80].set(222); + children[80] = new BitSet(); + parents[81] = new BitSet(); + parents[81].set(84); + parents[81].set(95); + children[81] = new BitSet(); + children[81].set(162); + children[81].set(163); + parents[82] = new BitSet(); + parents[82].set(72); + children[82] = new BitSet(); + parents[83] = new BitSet(); + parents[83].set(198); + parents[83].set(222); + children[83] = new BitSet(); + parents[84] = new BitSet(); + parents[84].set(178); + parents[84].set(183); + parents[84].set(190); + parents[84].set(191); + parents[84].set(206); + parents[84].set(210); + parents[84].set(211); + parents[84].set(217); + parents[84].set(218); + parents[84].set(226); + parents[84].set(228); + parents[84].set(229); + parents[84].set(233); + parents[84].set(238); + parents[84].set(243); + parents[84].set(250); + children[84] = new BitSet(); + children[84].set(81); + parents[85] = new BitSet(); + parents[85].set(193); + parents[85].set(194); + parents[85].set(209); + children[85] = new BitSet(); + parents[86] = new BitSet(); + children[86] = new BitSet(); + parents[87] = new BitSet(); + parents[87].set(188); + parents[87].set(250); + children[87] = new BitSet(); + parents[88] = new BitSet(); + parents[88].set(195); + children[88] = new BitSet(); + parents[89] = new BitSet(); + parents[89].set(173); + parents[89].set(202); + parents[89].set(231); + children[89] = new BitSet(); + parents[90] = new BitSet(); + parents[90].set(169); + parents[90].set(181); + parents[90].set(208); + parents[90].set(211); + parents[90].set(226); + parents[90].set(227); + parents[90].set(235); + parents[90].set(238); + children[90] = new BitSet(); + parents[91] = new BitSet(); + parents[91].set(165); + parents[91].set(242); + children[91] = new BitSet(); + parents[92] = new BitSet(); + parents[92].set(171); + parents[92].set(179); + parents[92].set(180); + parents[92].set(190); + parents[92].set(192); + parents[92].set(206); + parents[92].set(210); + parents[92].set(226); + parents[92].set(230); + parents[92].set(232); + parents[92].set(233); + parents[92].set(244); + parents[92].set(245); + parents[92].set(250); + children[92] = new BitSet(); + parents[93] = new BitSet(); + parents[93].set(173); + children[93] = new BitSet(); + parents[94] = new BitSet(); + parents[94].set(193); + parents[94].set(240); + children[94] = new BitSet(); + parents[95] = new BitSet(); + parents[95].set(201); + children[95] = new BitSet(); + children[95].set(81); + children[95].set(137); + children[95].set(140); + parents[96] = new BitSet(); + children[96] = new BitSet(); + children[96].set(97); + parents[97] = new BitSet(); + parents[97].set(96); + children[97] = new BitSet(); + parents[98] = new BitSet(); + parents[98].set(205); + children[98] = new BitSet(); + parents[99] = new BitSet(); + parents[99].set(207); + children[99] = new BitSet(); + parents[100] = new BitSet(); + parents[100].set(209); + children[100] = new BitSet(); + children[100].set(43); + children[100].set(146); + parents[101] = new BitSet(); + parents[101].set(164); + parents[101].set(179); + parents[101].set(197); + children[101] = new BitSet(); + parents[102] = new BitSet(); + parents[102].set(211); + children[102] = new BitSet(); + parents[103] = new BitSet(); + parents[103].set(172); + children[103] = new BitSet(); + parents[104] = new BitSet(); + parents[104].set(166); + parents[104].set(213); + parents[104].set(246); + children[104] = new BitSet(); + parents[105] = new BitSet(); + parents[105].set(107); + children[105] = new BitSet(); + parents[106] = new BitSet(); + parents[106].set(173); + children[106] = new BitSet(); + parents[107] = new BitSet(); + parents[107].set(18); + parents[107].set(129); + children[107] = new BitSet(); + children[107].set(19); + children[107].set(34); + children[107].set(105); + children[107].set(137); + parents[108] = new BitSet(); + parents[108].set(72); + children[108] = new BitSet(); + parents[109] = new BitSet(); + parents[109].set(164); + children[109] = new BitSet(); + parents[110] = new BitSet(); + parents[110].set(172); + parents[110].set(209); + children[110] = new BitSet(); + parents[111] = new BitSet(); + parents[111].set(164); + children[111] = new BitSet(); + parents[112] = new BitSet(); + parents[112].set(211); + parents[112].set(239); + parents[112].set(249); + children[112] = new BitSet(); + parents[113] = new BitSet(); + children[113] = new BitSet(); + parents[114] = new BitSet(); + parents[114].set(29); + parents[114].set(31); + parents[114].set(36); + parents[114].set(115); + parents[114].set(117); + parents[114].set(141); + children[114] = new BitSet(); + parents[115] = new BitSet(); + parents[115].set(31); + parents[115].set(115); + children[115] = new BitSet(); + children[115].set(74); + children[115].set(114); + children[115].set(115); + parents[116] = new BitSet(); + parents[116].set(117); + children[116] = new BitSet(); + parents[117] = new BitSet(); + parents[117].set(117); + parents[117].set(141); + children[117] = new BitSet(); + children[117].set(74); + children[117].set(114); + children[117].set(116); + children[117].set(117); + parents[118] = new BitSet(); + parents[118].set(188); + parents[118].set(209); + children[118] = new BitSet(); + children[118].set(43); + children[118].set(49); + parents[119] = new BitSet(); + parents[119].set(188); + children[119] = new BitSet(); + children[119].set(43); + children[119].set(50); + parents[120] = new BitSet(); + parents[120].set(241); + children[120] = new BitSet(); + parents[121] = new BitSet(); + parents[121].set(241); + children[121] = new BitSet(); + parents[122] = new BitSet(); + parents[122].set(246); + children[122] = new BitSet(); + parents[123] = new BitSet(); + parents[123].set(219); + children[123] = new BitSet(); + parents[124] = new BitSet(); + parents[124].set(173); + parents[124].set(202); + parents[124].set(231); + children[124] = new BitSet(); + parents[125] = new BitSet(); + parents[125].set(33); + parents[125].set(169); + parents[125].set(208); + parents[125].set(211); + parents[125].set(226); + parents[125].set(227); + parents[125].set(235); + parents[125].set(238); + children[125] = new BitSet(); + parents[126] = new BitSet(); + parents[126].set(17); + parents[126].set(165); + parents[126].set(195); + parents[126].set(212); + parents[126].set(242); + children[126] = new BitSet(); + parents[127] = new BitSet(); + parents[127].set(171); + parents[127].set(179); + parents[127].set(180); + parents[127].set(190); + parents[127].set(192); + parents[127].set(206); + parents[127].set(210); + parents[127].set(226); + parents[127].set(230); + parents[127].set(232); + parents[127].set(233); + parents[127].set(244); + parents[127].set(245); + parents[127].set(250); + children[127] = new BitSet(); + parents[128] = new BitSet(); + parents[128].set(17); + parents[128].set(33); + children[128] = new BitSet(); + children[128].set(19); + parents[129] = new BitSet(); + parents[129].set(141); + children[129] = new BitSet(); + children[129].set(33); + children[129].set(107); + parents[130] = new BitSet(); + children[130] = new BitSet(); + parents[131] = new BitSet(); + children[131] = new BitSet(); + parents[132] = new BitSet(); + parents[132].set(218); + children[132] = new BitSet(); + parents[133] = new BitSet(); + parents[133].set(214); + children[133] = new BitSet(); + parents[134] = new BitSet(); + parents[134].set(182); + parents[134].set(184); + parents[134].set(223); + parents[134].set(226); + parents[134].set(239); + children[134] = new BitSet(); + parents[135] = new BitSet(); + parents[135].set(221); + children[135] = new BitSet(); + parents[136] = new BitSet(); + parents[136].set(221); + children[136] = new BitSet(); + parents[137] = new BitSet(); + parents[137].set(29); + parents[137].set(36); + parents[137].set(95); + parents[137].set(107); + children[137] = new BitSet(); + parents[138] = new BitSet(); + parents[138].set(23); + parents[138].set(164); + children[138] = new BitSet(); + parents[139] = new BitSet(); + parents[139].set(17); + children[139] = new BitSet(); + parents[140] = new BitSet(); + parents[140].set(95); + children[140] = new BitSet(); + parents[141] = new BitSet(); + parents[141].set(165); + parents[141].set(203); + children[141] = new BitSet(); + children[141].set(114); + children[141].set(117); + children[141].set(129); + parents[142] = new BitSet(); + parents[142].set(226); + children[142] = new BitSet(); + parents[143] = new BitSet(); + parents[143].set(250); + children[143] = new BitSet(); + parents[144] = new BitSet(); + parents[144].set(218); + children[144] = new BitSet(); + parents[145] = new BitSet(); + children[145] = new BitSet(); + parents[146] = new BitSet(); + parents[146].set(100); + children[146] = new BitSet(); + parents[147] = new BitSet(); + parents[147].set(26); + children[147] = new BitSet(); + parents[148] = new BitSet(); + children[148] = new BitSet(); + parents[149] = new BitSet(); + parents[149].set(198); + parents[149].set(236); + children[149] = new BitSet(); + parents[150] = new BitSet(); + parents[150].set(198); + parents[150].set(236); + children[150] = new BitSet(); + parents[151] = new BitSet(); + parents[151].set(198); + parents[151].set(236); + children[151] = new BitSet(); + parents[152] = new BitSet(); + parents[152].set(198); + parents[152].set(236); + children[152] = new BitSet(); + parents[153] = new BitSet(); + parents[153].set(198); + parents[153].set(236); + children[153] = new BitSet(); + parents[154] = new BitSet(); + parents[154].set(198); + parents[154].set(236); + children[154] = new BitSet(); + parents[155] = new BitSet(); + parents[155].set(57); + parents[155].set(72); + parents[155].set(175); + parents[155].set(244); + parents[155].set(248); + children[155] = new BitSet(); + parents[156] = new BitSet(); + children[156] = new BitSet(); + parents[157] = new BitSet(); + parents[157].set(234); + parents[157].set(235); + children[157] = new BitSet(); + parents[158] = new BitSet(); + parents[158].set(239); + children[158] = new BitSet(); + parents[159] = new BitSet(); + children[159] = new BitSet(); + parents[160] = new BitSet(); + parents[160].set(251); + children[160] = new BitSet(); + parents[161] = new BitSet(); + children[161] = new BitSet(); + parents[162] = new BitSet(); + parents[162].set(81); + children[162] = new BitSet(); + parents[163] = new BitSet(); + parents[163].set(81); + children[163] = new BitSet(); + parents[164] = new BitSet(); + parents[164].set(179); + children[164] = new BitSet(); + children[164].set(13); + children[164].set(101); + children[164].set(109); + children[164].set(111); + children[164].set(138); + parents[165] = new BitSet(); + parents[165].set(203); + children[165] = new BitSet(); + children[165].set(26); + children[165].set(29); + children[165].set(39); + children[165].set(91); + children[165].set(126); + children[165].set(141); + children[165].set(179); + children[165].set(186); + children[165].set(197); + parents[166] = new BitSet(); + parents[166].set(240); + children[166] = new BitSet(); + children[166].set(0); + children[166].set(104); + parents[167] = new BitSet(); + parents[167].set(179); + parents[167].set(225); + children[167] = new BitSet(); + children[167].set(168); + children[167].set(179); + children[167].set(242); + parents[168] = new BitSet(); + parents[168].set(167); + children[168] = new BitSet(); + children[168].set(3); + children[168].set(4); + children[168].set(5); + children[168].set(6); + children[168].set(7); + children[168].set(8); + children[168].set(9); + children[168].set(10); + children[168].set(11); + children[168].set(62); + parents[169] = new BitSet(); + parents[169].set(170); + parents[169].set(172); + parents[169].set(175); + parents[169].set(188); + parents[169].set(189); + parents[169].set(193); + parents[169].set(194); + parents[169].set(205); + parents[169].set(209); + parents[169].set(224); + parents[169].set(234); + parents[169].set(251); + children[169] = new BitSet(); + children[169].set(90); + children[169].set(125); + children[169].set(177); + children[169].set(179); + children[169].set(180); + children[169].set(195); + children[169].set(224); + parents[170] = new BitSet(); + parents[170].set(172); + children[170] = new BitSet(); + children[170].set(169); + children[170].set(179); + parents[171] = new BitSet(); + parents[171].set(171); + parents[171].set(194); + parents[171].set(209); + parents[171].set(251); + children[171] = new BitSet(); + children[171].set(20); + children[171].set(26); + children[171].set(92); + children[171].set(127); + children[171].set(171); + children[171].set(173); + children[171].set(179); + parents[172] = new BitSet(); + parents[172].set(179); + children[172] = new BitSet(); + children[172].set(55); + children[172].set(103); + children[172].set(110); + children[172].set(169); + children[172].set(170); + children[172].set(179); + children[172].set(202); + children[172].set(212); + children[172].set(214); + children[172].set(220); + parents[173] = new BitSet(); + parents[173].set(171); + parents[173].set(179); + children[173] = new BitSet(); + children[173].set(54); + children[173].set(73); + children[173].set(89); + children[173].set(93); + children[173].set(106); + children[173].set(124); + parents[174] = new BitSet(); + children[174] = new BitSet(); + children[174].set(200); + parents[175] = new BitSet(); + parents[175].set(208); + children[175] = new BitSet(); + children[175].set(39); + children[175].set(68); + children[175].set(155); + children[175].set(169); + children[175].set(179); + children[175].set(225); + parents[176] = new BitSet(); + parents[176].set(192); + parents[176].set(211); + parents[176].set(226); + parents[176].set(229); + children[176] = new BitSet(); + children[176].set(45); + parents[177] = new BitSet(); + parents[177].set(169); + children[177] = new BitSet(); + children[177].set(59); + parents[178] = new BitSet(); + children[178] = new BitSet(); + children[178].set(84); + children[178].set(216); + parents[179] = new BitSet(); + parents[179].set(165); + parents[179].set(167); + parents[179].set(169); + parents[179].set(170); + parents[179].set(171); + parents[179].set(172); + parents[179].set(175); + parents[179].set(179); + parents[179].set(180); + parents[179].set(188); + parents[179].set(193); + parents[179].set(199); + parents[179].set(207); + parents[179].set(209); + parents[179].set(219); + parents[179].set(228); + parents[179].set(230); + parents[179].set(235); + parents[179].set(240); + parents[179].set(242); + children[179] = new BitSet(); + children[179].set(20); + children[179].set(21); + children[179].set(48); + children[179].set(92); + children[179].set(101); + children[179].set(127); + children[179].set(164); + children[179].set(167); + children[179].set(172); + children[179].set(173); + children[179].set(179); + children[179].set(190); + children[179].set(203); + children[179].set(207); + children[179].set(221); + children[179].set(227); + children[179].set(230); + children[179].set(234); + children[179].set(235); + children[179].set(241); + children[179].set(242); + parents[180] = new BitSet(); + parents[180].set(169); + parents[180].set(240); + children[180] = new BitSet(); + children[180].set(39); + children[180].set(92); + children[180].set(127); + children[180].set(179); + parents[181] = new BitSet(); + parents[181].set(200); + children[181] = new BitSet(); + children[181].set(39); + children[181].set(63); + children[181].set(90); + children[181].set(182); + children[181].set(212); + parents[182] = new BitSet(); + parents[182].set(181); + children[182] = new BitSet(); + children[182].set(134); + children[182].set(192); + children[182].set(195); + children[182].set(212); + parents[183] = new BitSet(); + parents[183].set(184); + children[183] = new BitSet(); + children[183].set(42); + children[183].set(63); + children[183].set(84); + parents[184] = new BitSet(); + parents[184].set(200); + children[184] = new BitSet(); + children[184].set(134); + children[184].set(183); + parents[185] = new BitSet(); + parents[185].set(186); + children[185] = new BitSet(); + children[185].set(2); + children[185].set(187); + parents[186] = new BitSet(); + parents[186].set(165); + parents[186].set(203); + children[186] = new BitSet(); + children[186].set(69); + children[186].set(185); + children[186].set(187); + parents[187] = new BitSet(); + parents[187].set(185); + parents[187].set(186); + children[187] = new BitSet(); + children[187].set(64); + children[187].set(65); + parents[188] = new BitSet(); + parents[188].set(224); + children[188] = new BitSet(); + children[188].set(71); + children[188].set(87); + children[188].set(118); + children[188].set(119); + children[188].set(169); + children[188].set(179); + children[188].set(224); + children[188].set(243); + parents[189] = new BitSet(); + parents[189].set(200); + parents[189].set(211); + parents[189].set(224); + children[189] = new BitSet(); + children[189].set(169); + children[189].set(192); + parents[190] = new BitSet(); + parents[190].set(179); + parents[190].set(225); + children[190] = new BitSet(); + children[190].set(53); + children[190].set(84); + children[190].set(92); + children[190].set(127); + children[190].set(199); + children[190].set(216); + children[190].set(231); + parents[191] = new BitSet(); + parents[191].set(192); + children[191] = new BitSet(); + children[191].set(84); + parents[192] = new BitSet(); + parents[192].set(182); + parents[192].set(189); + children[192] = new BitSet(); + children[192].set(70); + children[192].set(92); + children[192].set(127); + children[192].set(176); + children[192].set(191); + children[192].set(202); + children[192].set(212); + children[192].set(214); + children[192].set(220); + children[192].set(249); + parents[193] = new BitSet(); + parents[193].set(224); + children[193] = new BitSet(); + children[193].set(62); + children[193].set(85); + children[193].set(94); + children[193].set(169); + children[193].set(179); + children[193].set(224); + children[193].set(243); + parents[194] = new BitSet(); + parents[194].set(224); + children[194] = new BitSet(); + children[194].set(58); + children[194].set(85); + children[194].set(169); + children[194].set(171); + parents[195] = new BitSet(); + parents[195].set(169); + parents[195].set(182); + parents[195].set(200); + parents[195].set(229); + children[195] = new BitSet(); + children[195].set(88); + children[195].set(126); + children[195].set(210); + parents[196] = new BitSet(); + parents[196].set(197); + children[196] = new BitSet(); + children[196].set(2); + children[196].set(222); + children[196].set(236); + parents[197] = new BitSet(); + parents[197].set(165); + parents[197].set(203); + children[197] = new BitSet(); + children[197].set(21); + children[197].set(72); + children[197].set(101); + children[197].set(196); + children[197].set(222); + children[197].set(236); + parents[198] = new BitSet(); + parents[198].set(233); + children[198] = new BitSet(); + children[198].set(25); + children[198].set(35); + children[198].set(64); + children[198].set(65); + children[198].set(76); + children[198].set(77); + children[198].set(78); + children[198].set(79); + children[198].set(80); + children[198].set(83); + children[198].set(149); + children[198].set(150); + children[198].set(151); + children[198].set(152); + children[198].set(153); + children[198].set(154); + parents[199] = new BitSet(); + parents[199].set(190); + parents[199].set(206); + children[199] = new BitSet(); + children[199].set(39); + children[199].set(179); + parents[200] = new BitSet(); + parents[200].set(174); + children[200] = new BitSet(); + children[200].set(181); + children[200].set(184); + children[200].set(189); + children[200].set(195); + children[200].set(211); + children[200].set(226); + children[200].set(234); + children[200].set(239); + parents[201] = new BitSet(); + parents[201].set(202); + parents[201].set(213); + parents[201].set(215); + children[201] = new BitSet(); + children[201].set(95); + parents[202] = new BitSet(); + parents[202].set(172); + parents[202].set(192); + parents[202].set(226); + children[202] = new BitSet(); + children[202].set(39); + children[202].set(89); + children[202].set(124); + children[202].set(201); + parents[203] = new BitSet(); + parents[203].set(179); + parents[203].set(209); + parents[203].set(210); + children[203] = new BitSet(); + children[203].set(26); + children[203].set(29); + children[203].set(141); + children[203].set(165); + children[203].set(186); + children[203].set(197); + parents[204] = new BitSet(); + children[204] = new BitSet(); + children[204].set(37); + children[204].set(51); + children[204].set(55); + parents[205] = new BitSet(); + parents[205].set(224); + children[205] = new BitSet(); + children[205].set(98); + children[205].set(169); + parents[206] = new BitSet(); + parents[206].set(225); + children[206] = new BitSet(); + children[206].set(20); + children[206].set(84); + children[206].set(92); + children[206].set(127); + children[206].set(199); + parents[207] = new BitSet(); + parents[207].set(179); + parents[207].set(224); + children[207] = new BitSet(); + children[207].set(13); + children[207].set(99); + children[207].set(179); + children[207].set(208); + parents[208] = new BitSet(); + parents[208].set(207); + children[208] = new BitSet(); + children[208].set(39); + children[208].set(90); + children[208].set(125); + children[208].set(175); + children[208].set(209); + parents[209] = new BitSet(); + parents[209].set(208); + children[209] = new BitSet(); + children[209].set(68); + children[209].set(85); + children[209].set(100); + children[209].set(110); + children[209].set(118); + children[209].set(169); + children[209].set(171); + children[209].set(179); + children[209].set(203); + children[209].set(225); + children[209].set(244); + parents[210] = new BitSet(); + parents[210].set(195); + parents[210].set(210); + parents[210].set(212); + children[210] = new BitSet(); + children[210].set(39); + children[210].set(62); + children[210].set(84); + children[210].set(92); + children[210].set(127); + children[210].set(203); + children[210].set(210); + parents[211] = new BitSet(); + parents[211].set(200); + parents[211].set(211); + children[211] = new BitSet(); + children[211].set(84); + children[211].set(90); + children[211].set(102); + children[211].set(112); + children[211].set(125); + children[211].set(176); + children[211].set(189); + children[211].set(211); + children[211].set(212); + children[211].set(239); + parents[212] = new BitSet(); + parents[212].set(172); + parents[212].set(181); + parents[212].set(182); + parents[212].set(192); + parents[212].set(211); + parents[212].set(226); + parents[212].set(229); + parents[212].set(239); + children[212] = new BitSet(); + children[212].set(15); + children[212].set(126); + children[212].set(210); + parents[213] = new BitSet(); + parents[213].set(215); + parents[213].set(228); + parents[213].set(229); + children[213] = new BitSet(); + children[213].set(0); + children[213].set(56); + children[213].set(104); + children[213].set(201); + parents[214] = new BitSet(); + parents[214].set(172); + parents[214].set(192); + children[214] = new BitSet(); + children[214].set(0); + children[214].set(39); + children[214].set(133); + children[214].set(215); + parents[215] = new BitSet(); + parents[215].set(214); + children[215] = new BitSet(); + children[215].set(0); + children[215].set(38); + children[215].set(201); + children[215].set(213); + children[215].set(233); + children[215].set(243); + parents[216] = new BitSet(); + parents[216].set(178); + parents[216].set(190); + parents[216].set(233); + parents[216].set(237); + parents[216].set(242); + children[216] = new BitSet(); + children[216].set(217); + children[216].set(218); + parents[217] = new BitSet(); + parents[217].set(216); + children[217] = new BitSet(); + children[217].set(53); + children[217].set(84); + parents[218] = new BitSet(); + parents[218].set(216); + children[218] = new BitSet(); + children[218].set(53); + children[218].set(84); + children[218].set(132); + children[218].set(144); + parents[219] = new BitSet(); + parents[219].set(225); + children[219] = new BitSet(); + children[219].set(123); + children[219].set(179); + parents[220] = new BitSet(); + parents[220].set(172); + parents[220].set(192); + children[220] = new BitSet(); + children[220].set(1); + children[220].set(232); + children[220].set(233); + parents[221] = new BitSet(); + parents[221].set(179); + children[221] = new BitSet(); + children[221].set(135); + children[221].set(136); + parents[222] = new BitSet(); + parents[222].set(196); + parents[222].set(197); + children[222] = new BitSet(); + children[222].set(76); + children[222].set(77); + children[222].set(78); + children[222].set(79); + children[222].set(80); + children[222].set(83); + parents[223] = new BitSet(); + parents[223].set(224); + children[223] = new BitSet(); + children[223].set(134); + children[223].set(225); + parents[224] = new BitSet(); + parents[224].set(169); + parents[224].set(188); + parents[224].set(193); + children[224] = new BitSet(); + children[224].set(169); + children[224].set(188); + children[224].set(189); + children[224].set(193); + children[224].set(194); + children[224].set(205); + children[224].set(207); + children[224].set(223); + children[224].set(251); + parents[225] = new BitSet(); + parents[225].set(175); + parents[225].set(209); + parents[225].set(223); + children[225] = new BitSet(); + children[225].set(167); + children[225].set(190); + children[225].set(206); + children[225].set(219); + children[225].set(240); + parents[226] = new BitSet(); + parents[226].set(200); + children[226] = new BitSet(); + children[226].set(39); + children[226].set(84); + children[226].set(90); + children[226].set(92); + children[226].set(125); + children[226].set(127); + children[226].set(134); + children[226].set(142); + children[226].set(176); + children[226].set(202); + children[226].set(212); + children[226].set(229); + children[226].set(233); + parents[227] = new BitSet(); + parents[227].set(179); + children[227] = new BitSet(); + children[227].set(39); + children[227].set(90); + children[227].set(125); + children[227].set(228); + children[227].set(233); + parents[228] = new BitSet(); + parents[228].set(227); + children[228] = new BitSet(); + children[228].set(38); + children[228].set(49); + children[228].set(84); + children[228].set(179); + children[228].set(213); + parents[229] = new BitSet(); + parents[229].set(226); + children[229] = new BitSet(); + children[229].set(38); + children[229].set(84); + children[229].set(176); + children[229].set(195); + children[229].set(212); + children[229].set(213); + children[229].set(233); + parents[230] = new BitSet(); + parents[230].set(179); + children[230] = new BitSet(); + children[230].set(39); + children[230].set(92); + children[230].set(127); + children[230].set(179); + parents[231] = new BitSet(); + parents[231].set(190); + children[231] = new BitSet(); + children[231].set(2); + children[231].set(89); + children[231].set(124); + children[231].set(233); + parents[232] = new BitSet(); + parents[232].set(220); + children[232] = new BitSet(); + children[232].set(39); + children[232].set(92); + children[232].set(127); + children[232].set(233); + parents[233] = new BitSet(); + parents[233].set(215); + parents[233].set(220); + parents[233].set(226); + parents[233].set(227); + parents[233].set(229); + parents[233].set(231); + parents[233].set(232); + parents[233].set(240); + parents[233].set(241); + parents[233].set(248); + children[233] = new BitSet(); + children[233].set(84); + children[233].set(92); + children[233].set(127); + children[233].set(198); + children[233].set(216); + parents[234] = new BitSet(); + parents[234].set(179); + parents[234].set(200); + children[234] = new BitSet(); + children[234].set(157); + children[234].set(169); + parents[235] = new BitSet(); + parents[235].set(179); + children[235] = new BitSet(); + children[235].set(90); + children[235].set(125); + children[235].set(157); + children[235].set(179); + parents[236] = new BitSet(); + parents[236].set(196); + parents[236].set(197); + children[236] = new BitSet(); + children[236].set(149); + children[236].set(150); + children[236].set(151); + children[236].set(152); + children[236].set(153); + children[236].set(154); + parents[237] = new BitSet(); + parents[237].set(239); + children[237] = new BitSet(); + children[237].set(216); + children[237].set(238); + parents[238] = new BitSet(); + parents[238].set(237); + children[238] = new BitSet(); + children[238].set(2); + children[238].set(39); + children[238].set(84); + children[238].set(90); + children[238].set(125); + parents[239] = new BitSet(); + parents[239].set(200); + parents[239].set(211); + children[239] = new BitSet(); + children[239].set(112); + children[239].set(134); + children[239].set(158); + children[239].set(212); + children[239].set(237); + parents[240] = new BitSet(); + parents[240].set(225); + children[240] = new BitSet(); + children[240].set(62); + children[240].set(94); + children[240].set(166); + children[240].set(179); + children[240].set(180); + children[240].set(233); + children[240].set(241); + children[240].set(243); + children[240].set(245); + children[240].set(246); + children[240].set(248); + parents[241] = new BitSet(); + parents[241].set(179); + parents[241].set(240); + children[241] = new BitSet(); + children[241].set(2); + children[241].set(120); + children[241].set(121); + children[241].set(233); + parents[242] = new BitSet(); + parents[242].set(167); + parents[242].set(179); + children[242] = new BitSet(); + children[242].set(0); + children[242].set(13); + children[242].set(91); + children[242].set(126); + children[242].set(179); + children[242].set(216); + children[242].set(247); + parents[243] = new BitSet(); + parents[243].set(188); + parents[243].set(193); + parents[243].set(215); + parents[243].set(240); + parents[243].set(244); + parents[243].set(247); + parents[243].set(248); + children[243] = new BitSet(); + children[243].set(84); + parents[244] = new BitSet(); + parents[244].set(209); + children[244] = new BitSet(); + children[244].set(39); + children[244].set(92); + children[244].set(127); + children[244].set(155); + children[244].set(243); + parents[245] = new BitSet(); + parents[245].set(240); + children[245] = new BitSet(); + children[245].set(39); + children[245].set(92); + children[245].set(127); + children[245].set(248); + parents[246] = new BitSet(); + parents[246].set(240); + children[246] = new BitSet(); + children[246].set(104); + children[246].set(122); + parents[247] = new BitSet(); + parents[247].set(242); + children[247] = new BitSet(); + children[247].set(48); + children[247].set(243); + parents[248] = new BitSet(); + parents[248].set(240); + parents[248].set(245); + children[248] = new BitSet(); + children[248].set(38); + children[248].set(155); + children[248].set(233); + children[248].set(243); + parents[249] = new BitSet(); + parents[249].set(192); + children[249] = new BitSet(); + children[249].set(112); + children[249].set(250); + parents[250] = new BitSet(); + parents[250].set(249); + children[250] = new BitSet(); + children[250].set(42); + children[250].set(84); + children[250].set(87); + children[250].set(92); + children[250].set(127); + children[250].set(143); + parents[251] = new BitSet(); + parents[251].set(224); + children[251] = new BitSet(); + children[251].set(160); + children[251].set(169); + children[251].set(171); + return IntGraph.create(parents, children); + } + +} diff --git a/mastfrog-jgrapht-adapter/target/maven-archiver/pom.properties b/mastfrog-jgrapht-adapter/target/maven-archiver/pom.properties new file mode 100644 index 0000000..aa5721a --- /dev/null +++ b/mastfrog-jgrapht-adapter/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=mastfrog-jgrapht-adapter +groupId=com.mastfrog +version=2.3 diff --git a/mastfrog-jgrapht-adapter/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/mastfrog-jgrapht-adapter/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..55c42ad --- /dev/null +++ b/mastfrog-jgrapht-adapter/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,8 @@ +com/mastfrog/graph/jgrapht/adapter/GraphAdapter$Edge.class +com/mastfrog/graph/jgrapht/adapter/GraphAdapter.class +com/mastfrog/graph/jgrapht/adapter/GraphAdapter$WrappedObjectGraph.class +com/mastfrog/graph/jgrapht/adapter/GraphAdapter$DefaultEdgeFactory.class +com/mastfrog/graph/jgrapht/adapter/GraphAdapter$WrappedObjectGraph$1.class +com/mastfrog/graph/jgrapht/adapter/GraphAdapter$DefaultEdgeFactory$Edg.class +com/mastfrog/graph/jgrapht/adapter/GraphAdapter$EdgeFactory.class +com/mastfrog/graph/jgrapht/adapter/GraphAdapter$DefaultEdge.class diff --git a/mastfrog-jgrapht-adapter/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/mastfrog-jgrapht-adapter/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..de8b002 --- /dev/null +++ b/mastfrog-jgrapht-adapter/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1 @@ +/home/tom/projects/forkvl-jung/vl-jung/mastfrog-jgrapht-adapter/src/main/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapter.java diff --git a/mastfrog-jgrapht-adapter/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst b/mastfrog-jgrapht-adapter/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst new file mode 100644 index 0000000..107f54d --- /dev/null +++ b/mastfrog-jgrapht-adapter/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst @@ -0,0 +1,2 @@ +com/mastfrog/graph/jgrapht/adapter/GraphAdapterTest.class +com/mastfrog/graph/jgrapht/adapter/GraphAdapterTest$IXA.class diff --git a/mastfrog-jgrapht-adapter/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst b/mastfrog-jgrapht-adapter/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst new file mode 100644 index 0000000..f080dfb --- /dev/null +++ b/mastfrog-jgrapht-adapter/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst @@ -0,0 +1 @@ +/home/tom/projects/forkvl-jung/vl-jung/mastfrog-jgrapht-adapter/src/test/java/com/mastfrog/graph/jgrapht/adapter/GraphAdapterTest.java diff --git a/mastfrog-jgrapht-adapter/target/surefire-reports/TEST-com.mastfrog.graph.jgrapht.adapter.GraphAdapterTest.xml b/mastfrog-jgrapht-adapter/target/surefire-reports/TEST-com.mastfrog.graph.jgrapht.adapter.GraphAdapterTest.xml new file mode 100644 index 0000000..fa450a6 --- /dev/null +++ b/mastfrog-jgrapht-adapter/target/surefire-reports/TEST-com.mastfrog.graph.jgrapht.adapter.GraphAdapterTest.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mastfrog-jgrapht-adapter/target/surefire-reports/com.mastfrog.graph.jgrapht.adapter.GraphAdapterTest.txt b/mastfrog-jgrapht-adapter/target/surefire-reports/com.mastfrog.graph.jgrapht.adapter.GraphAdapterTest.txt new file mode 100644 index 0000000..e505739 --- /dev/null +++ b/mastfrog-jgrapht-adapter/target/surefire-reports/com.mastfrog.graph.jgrapht.adapter.GraphAdapterTest.txt @@ -0,0 +1,4 @@ +------------------------------------------------------------------------------- +Test set: com.mastfrog.graph.jgrapht.adapter.GraphAdapterTest +------------------------------------------------------------------------------- +Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.062 sec diff --git a/pom.xml b/pom.xml index 0f51489..9bf6bec 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,9 @@ https://github.com:timboudreau/vl-jung 2.1.1 + 1.7.25 + 1.5.0 + 1.0 bsd RELEASE113 UTF-8 @@ -29,6 +32,11 @@ mastfrog-graph-adapter alternate-layouts + vl-jungrapht + vl-jungrapht-extensions + vl-jungrapht-demo + vl-jungrapht-nbm + mastfrog-jgrapht-adapter git@github.com:timboudreau/vl-jung.git @@ -60,6 +68,26 @@ + + org.jgrapht + jgrapht-core + ${version.jgrapht} + + + com.github.tomnelson + jungrapht-layout + ${version.jungrapht} + + + com.github.tomnelson + jungrapht-visualization + ${version.jungrapht} + + + org.slf4j + slf4j-api + ${version.slf4j} + com.google.guava guava @@ -85,6 +113,11 @@ vl-jung-demo ${project.version} + + ${project.groupId} + vl-jungrapht-demo + ${project.version} + org.hamcrest hamcrest-core diff --git a/vl-jung-nbm/pom.xml b/vl-jung-nbm/pom.xml index b38ba9f..eb412f9 100644 --- a/vl-jung-nbm/pom.xml +++ b/vl-jung-nbm/pom.xml @@ -1,6 +1,6 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.mastfrog @@ -39,7 +39,21 @@ tim+github@timboudreau.com + + 1.7.25 + 1.5.0 + + + org.jgrapht + jgrapht-core + ${version.jgrapht} + + + org.slf4j + slf4j-api + ${version.slf4j} + org.netbeans.api org-openide-util-lookup diff --git a/vl-jungrapht-demo/nbactions.xml b/vl-jungrapht-demo/nbactions.xml new file mode 100644 index 0000000..8ba1618 --- /dev/null +++ b/vl-jungrapht-demo/nbactions.xml @@ -0,0 +1,17 @@ + + + + run + + jar + + + process-classes + org.codehaus.mojo:exec-maven-plugin:1.2.1:exec + + + -classpath %classpath com.timboudreau.vl.jungrapht.demo.App + java + + + diff --git a/vl-jungrapht-demo/pom.xml b/vl-jungrapht-demo/pom.xml new file mode 100644 index 0000000..ba95532 --- /dev/null +++ b/vl-jungrapht-demo/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + com.mastfrog + visual-library-jung + 2.3 + + vl-jungrapht-demo + Visual Library + JunGraphT Demo + + + junit + junit + 4.11 + test + + + com.mastfrog + vl-jungrapht + ${project.version} + + + com.mastfrog + vl-jungrapht-extensions + ${project.version} + + + + bsd + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.5.5 + + + jar-with-dependencies + + + true + + true + true + true + simple + com.timboudreau.vl.jungrapht.demo.App + + + + + + make-assembly + package + + single + + + + jar-with-dependencies + + vl-jungrapht-demo + true + + + + + + + diff --git a/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/App.java b/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/App.java new file mode 100644 index 0000000..ee61277 --- /dev/null +++ b/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/App.java @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2013, Tim Boudreau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.timboudreau.vl.jungrapht.demo; + +import com.timboudreau.vl.jungrapht.extensions.BaseJungraphtScene; +import org.jgrapht.Graph; +import org.jgrapht.ListenableGraph; +import org.jgrapht.graph.DefaultListenableGraph; +import org.jgrapht.graph.builder.GraphTypeBuilder; +import org.jungrapht.visualization.decorators.EdgeShape; +import org.jungrapht.visualization.layout.algorithms.BalloonLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.CircleLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.DAGLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.FRLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.ForceAtlas2LayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.GEMLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.ISOMLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.KKLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.RadialTreeLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.SpringLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.SugiyamaLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.TidierRadialTreeLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.TidierTreeLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.TreeLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.repulsion.BarnesHutFA2Repulsion; +import org.jungrapht.visualization.layout.algorithms.repulsion.BarnesHutFRRepulsion; +import org.jungrapht.visualization.layout.algorithms.repulsion.BarnesHutSpringRepulsion; +import org.jungrapht.visualization.layout.algorithms.sugiyama.Layering; +import org.jungrapht.visualization.layout.algorithms.util.EdgeArticulationFunctionSupplier; +import org.jungrapht.visualization.layout.model.Rectangle; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; +import org.openide.util.LookupEvent; +import org.openide.util.LookupListener; + +import javax.swing.*; +import javax.swing.border.BevelBorder; +import javax.swing.plaf.nimbus.NimbusLookAndFeel; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Insets; +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.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class App { + + private static final Pattern QUOTES = Pattern.compile("\"(.*?)\"\\s+\"(.*?)\"\\s+"); + private static final Pattern NO_QUOTES = Pattern.compile("(.*?)\\s+(.*)"); + + private static class GraphAndForest { + + private final ListenableGraph graph; + private final Graph forest; + + public GraphAndForest(ListenableGraph graph, Graph forest) { + this.graph = graph; + this.forest = forest; + } + } + + public static void main(String[] args) throws IOException { + try { + UIManager.setLookAndFeel(new NimbusLookAndFeel()); + } catch (Exception ex) { + Exceptions.printStackTrace(ex); + } + GraphAndForest gf = loadGraph(args); + + showDemo(gf); + } + + public static JFrame showDemo(GraphAndForest gf) throws IOException { + return showDemo(gf.graph, gf.forest); + } + + public static JFrame showDemo(Graph graph) throws IOException { + return showDemo(graph, null); + } + + public static JFrame showDemo(Graph graph, Graph forest) throws IOException { + final JFrame jf = new JFrame("Visual Library + Jungrapht Demo"); + jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); +// GraphAndForest gf = loadGraph(args); + + LayoutAlgorithm layout; + + try { + layout = forest == null ? new KKLayoutAlgorithm<>() : new TreeLayoutAlgorithm<>(); + } catch (IllegalArgumentException ex) { + layout = new KKLayoutAlgorithm<>(); + } + final BaseJungraphtScene scene = new SceneImpl((ListenableGraph)graph, layout); + jf.setLayout(new BorderLayout()); + jf.add(new JScrollPane(scene.createView()), BorderLayout.CENTER); + + JToolBar bar = new JToolBar(); + bar.setMargin(new Insets(5, 5, 5, 5)); + bar.setLayout(new FlowLayout(5)); + DefaultComboBoxModel mdl = new DefaultComboBoxModel<>(); + mdl.addElement(KKLayoutAlgorithm.builder() + .build()); + mdl.addElement(layout); + // all of the tree layout algorithms will work with an arbitrary graph + mdl.addElement(new BalloonLayoutAlgorithm()); + mdl.addElement(new RadialTreeLayoutAlgorithm()); + mdl.addElement(TidierTreeLayoutAlgorithm.edgeAwareBuilder() + .vertexBoundsFunction(v -> Rectangle.of(-10, -10, 20, 20)).build()); + mdl.addElement(TidierRadialTreeLayoutAlgorithm.edgeAwareBuilder() + .vertexBoundsFunction(v -> Rectangle.of(-10, -10, 20, 20)).build()); + mdl.addElement(CircleLayoutAlgorithm.builder().threaded(false).build()); + mdl.addElement(FRLayoutAlgorithm.builder().repulsionContractBuilder(BarnesHutFRRepulsion.barnesHutBuilder()) + .build()); + mdl.addElement(ISOMLayoutAlgorithm.builder() + .build()); + mdl.addElement(SpringLayoutAlgorithm.builder().repulsionContractBuilder(BarnesHutSpringRepulsion.barnesHutBuilder()) + .build()); + mdl.addElement(DAGLayoutAlgorithm.builder().build() + ); + mdl.addElement(GEMLayoutAlgorithm.edgeAwareBuilder() + .build()); + mdl.addElement(ForceAtlas2LayoutAlgorithm.builder() + .repulsionContractBuilder(BarnesHutFA2Repulsion.builder().repulsionK(300)) + .build()); + mdl.addElement(SugiyamaLayoutAlgorithm.edgeAwareBuilder() + .layering(Layering.COFFMAN_GRAHAM) + .threaded(false) + .build()); + + mdl.setSelectedItem(layout); + final JCheckBox checkbox = new JCheckBox("Animate iterative layouts"); + + scene.setLayoutAnimationFramesPerSecond(48); + + final JComboBox layouts = new JComboBox(mdl); + layouts.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList jlist, Object o, int i, boolean bln, boolean bln1) { + o = o.getClass().getSimpleName(); + return super.getListCellRendererComponent(jlist, o, i, bln, bln1); //To change body of generated methods, choose Tools | Templates. + } + }); + bar.add(new JLabel(" Layout Type")); + bar.add(layouts); + + bar.add(new JLabel(" Connection Shape")); + DefaultComboBoxModel, String, Shape>> shapes = new DefaultComboBoxModel<>(); + shapes.addElement(new EdgeShape.QuadCurve<>()); + shapes.addElement(new EdgeShape.CubicCurve<>()); + shapes.addElement(new EdgeShape.Line<>()); + shapes.addElement(new EdgeShape.Box<>()); + + final JComboBox, String, Shape>> shapesBox = new JComboBox<>(shapes); + shapesBox.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent ae) { + BiFunction, String, Shape> xform = + (BiFunction, String, Shape>) shapesBox.getSelectedItem(); + scene.setConnectionEdgeShape(xform); + } + }); + shapesBox.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList jlist, Object o, int i, boolean bln, boolean bln1) { + o = o.getClass().getSimpleName(); + return super.getListCellRendererComponent(jlist, o, i, bln, bln1); //To change body of generated methods, choose Tools | Templates. + } + }); + shapesBox.setSelectedItem(new EdgeShape.QuadCurve<>()); + bar.add(shapesBox); + + + layouts.addActionListener(ae -> { + LayoutAlgorithm layout1 = (LayoutAlgorithm) layouts.getSelectedItem(); + + if (layout1 instanceof EdgeArticulationFunctionSupplier) { + BiFunction, String, Shape> edgeShapeFunction = EdgeShape.articulatedLine(); + + ((EdgeShape.ArticulatedLine) edgeShapeFunction) + .setEdgeArticulationFunction( + ((EdgeArticulationFunctionSupplier) layout1) + .getEdgeArticulationFunction()); + } + + // These two layouts implement IterativeContext, but they do + // not evolve toward anything, they just randomly rearrange + // themselves. So disable animation for these. + if (layout1 instanceof ISOMLayoutAlgorithm || layout1 instanceof DAGLayoutAlgorithm) { + checkbox.setSelected(false); + } + + if (layout1 instanceof EdgeArticulationFunctionSupplier) { + EdgeArticulationFunctionSupplier edgeArticulationFunctionSupplier = + (EdgeArticulationFunctionSupplier) layout1; + EdgeShape.ArticulatedLine articulatedLineShape = EdgeShape.articulatedLine(); + articulatedLineShape.setEdgeArticulationFunction(edgeArticulationFunctionSupplier.getEdgeArticulationFunction()); + scene.setConnectionEdgeShape(articulatedLineShape); + } else { + scene.setConnectionEdgeShape((BiFunction) shapes.getSelectedItem()); + } + + SwingUtilities.invokeLater(() -> + scene.setGraphLayout(layout1, true)); + }); + + jf.add(bar, BorderLayout.NORTH); + bar.add(new MinSizePanel(scene.createSatelliteView())); + bar.setFloatable(false); + bar.setRollover(true); + + final JLabel selectionLabel = new JLabel(" "); + Lookup.Result selectedNodes = scene.getLookup().lookupResult(String.class); + selectedNodes.allInstances(); + LookupListener listener = new LookupListener() { + @Override + public void resultChanged(LookupEvent le) { + Lookup.Result res = (Lookup.Result) le.getSource(); + StringBuilder sb = new StringBuilder(""); + List l = new ArrayList<>(res.allInstances()); + Collections.sort(l); + for (String s : l) { + if (sb.length() != 6) { + sb.append(", "); + } + sb.append(s); + } + sb.append(""); + selectionLabel.setText(sb.toString()); + } + }; + selectionLabel.putClientProperty("ll", listener); // ensure it's not garbage collected + selectionLabel.putClientProperty("lr", selectedNodes); // ensure it's not garbage collected + selectedNodes.addLookupListener(listener); + selectedNodes.allInstances(); + + + checkbox.setSelected(true); + checkbox.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + scene.setAnimateIterativeLayouts(checkbox.isSelected()); + } + }); + bar.add(checkbox); + bar.add(selectionLabel); + selectionLabel.setHorizontalAlignment(SwingConstants.TRAILING); + +// jf.setSize(jf.getGraphicsConfiguration().getBounds().width - 120, 700); + jf.setSize(new Dimension(1280, 720)); + jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + jf.addWindowListener(new WindowAdapter() { + @Override + public void windowOpened(WindowEvent we) { + scene.relayout(true); + scene.validate(); + } + }); + jf.setVisible(true); + return jf; + } + + private static class MinSizePanel extends JPanel { + + MinSizePanel(JComponent inner) { + setLayout(new BorderLayout()); + add(inner, BorderLayout.CENTER); + setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); + } + + public Dimension getPreferredSize() { + Dimension result = super.getPreferredSize(); + result.height = 40; + return result; + } + } + + private static String[] elements(String line, String[] into) { + if (line.startsWith("#")) { + return null; + } + Matcher m = QUOTES.matcher(line); + if (m.find()) { + into[0] = m.group(1); + into[1] = m.group(2); + return into; + } else { + m = NO_QUOTES.matcher(line); + if (m.find()) { + into[0] = m.group(1); + into[1] = m.group(2); + return into; + } + } + return null; + } + + private static GraphAndForest loadGraph(String[] args) throws FileNotFoundException, IOException { + if (args.length > 0) { + File f = new File(args[0]); + if (!f.exists() || !f.isFile() || !f.canRead()) { + System.err.println("File does not exist, is not readable, or is not a file: " + f); + System.exit(1); + } + try { + Graph forest = GraphTypeBuilder.directed() + .buildGraph(); + String[] arr = new String[2]; + Set pairs = new HashSet<>(); + try (BufferedReader br = new BufferedReader(new FileReader(f))) { + int ix = 0; + for (String line; (line = br.readLine()) != null;) { + line = line.trim(); + String[] items = elements(line, arr); + if (items != null) { + if (items[0].equals(items[1])) { + continue; + } + String[] x = new String[]{items[0], items[1]}; + Arrays.sort(x); + String key = x[0] + "::" + x[1]; + if (!pairs.contains(key)) { + String edge = Integer.toString(ix); + forest.addEdge(items[0], items[1], edge); + pairs.add(key); + } else { + System.out.println("DUP: " + key); + } + } + ix++; + } + } + ListenableGraph g = new DefaultListenableGraph<>(forest); + return new GraphAndForest(g, forest); + } catch (Exception e) { + // Graph has cycles - try undirected + e.printStackTrace(); + Graph graph = GraphTypeBuilder.undirected() + .allowingSelfLoops(true) + .allowingMultipleEdges(true).buildGraph(); + + String[] arr = new String[2]; + Set pairs = new HashSet<>(); + try (BufferedReader br = new BufferedReader(new FileReader(f))) { + int ix = 0; + for (String line; (line = br.readLine()) != null;) { + line = line.trim(); + String[] items = elements(line, arr); + if (items != null) { + if (items[0].equals(items[1])) { + continue; + } + String edge = Integer.toString(ix); + try { + String[] x = new String[]{items[0], items[1]}; + Arrays.sort(x); + String key = x[0] + "::" + x[1]; + if (!pairs.contains(key)) { + graph.addEdge(items[0], items[1], edge); + pairs.add(key); + } else { + System.out.println("DUP: " + key); + } + } catch (IllegalArgumentException ex) { + System.err.println(ex.getMessage()); + } + } + ix++; + } + } + ListenableGraph g = (ListenableGraph)graph; + return new GraphAndForest(g, null); + } + } else { + Graph forest = GraphTypeBuilder.directed() + .edgeSupplier(BalloonLayoutDemo.edgeFactory).buildGraph(); + + ListenableGraph g = new DefaultListenableGraph(new BalloonLayoutDemo().createTree(forest)); + return new GraphAndForest(g, forest); + } + } +} diff --git a/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/BalloonLayoutDemo.java b/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/BalloonLayoutDemo.java new file mode 100644 index 0000000..a80bc20 --- /dev/null +++ b/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/BalloonLayoutDemo.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2013, Tim Boudreau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.timboudreau.vl.jungrapht.demo; + + +import org.jgrapht.Graph; + +import java.util.function.Supplier; + +/** + * 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 { + + // Code borrowed from JUNG's demos + + public static Supplier edgeFactory = new Supplier() { + int i = 0; + public String get() { + return Integer.toString(i++); + } + }; + + Graph createTree(Graph graph) { + String base = "Base Node"; + graph.addVertex(base); + graph.addVertex("B0"); + graph.addEdge(base, "B0"); + graph.addVertex("B1"); + graph.addEdge(base, "B1"); + graph.addVertex("B2"); + graph.addEdge(base, "B2"); + + graph.addVertex("C0"); + graph.addEdge("B0", "C0"); + graph.addVertex("C1"); + graph.addEdge("B0", "C1"); + graph.addVertex("C2"); + graph.addEdge("B0", "C2"); + graph.addVertex("C3"); + graph.addEdge("B0", "C3"); + + graph.addVertex("H0"); + graph.addEdge("C2", "H0"); + graph.addVertex("H1"); + graph.addEdge("C2", "H1"); + + graph.addVertex("D0"); + graph.addEdge("B1", "D0"); + graph.addVertex("D1"); + graph.addEdge("B1", "D1"); + graph.addVertex("D2"); + graph.addEdge("B1", "D2"); + + graph.addEdge(base, "D2"); + graph.addEdge(base, "C3"); + + graph.addVertex("E0"); + graph.addEdge("B2", "E0"); + graph.addVertex("E1"); + graph.addEdge("B2", "E1"); + graph.addVertex("E2"); + graph.addEdge("B2", "E2"); + + graph.addVertex("F0"); + graph.addEdge("D0", "F0"); + graph.addVertex("F1"); + graph.addEdge("D0", "F1"); + graph.addVertex("F2"); + graph.addEdge("D0", "F2"); + + graph.addVertex("G0"); + graph.addEdge("D1", "G0"); + graph.addVertex("G1"); + graph.addEdge("D1", "G1"); + graph.addVertex("G2"); + graph.addEdge("D1", "G2"); + graph.addVertex("G3"); + graph.addEdge("D1", "G3"); + graph.addVertex("G4"); + graph.addEdge("D1", "G4"); + graph.addVertex("G5"); + graph.addEdge("D1", "G5"); + graph.addVertex("G6"); + graph.addEdge("D1", "G6"); + graph.addVertex("G7"); + graph.addEdge("D1", "G7"); + + graph.addVertex("HA1"); + graph.addEdge(base, "HA1"); + graph.addVertex("HA2"); + graph.addEdge(base, "HA2"); + graph.addVertex("HA3"); + graph.addEdge(base, "HA3"); + graph.addVertex("I1"); + graph.addEdge("HA3", "I1"); + graph.addVertex("I2"); + graph.addEdge("HA3", "I2"); + + graph.addVertex("J1"); + graph.addEdge("I2", "J1"); + graph.addVertex("K0"); + graph.addVertex("K1"); + graph.addEdge("K0", "K1"); + graph.addVertex("K2"); + graph.addEdge("K0", "K2"); + graph.addVertex("K3"); + graph.addEdge("K0", "K3"); + + graph.addVertex("J0"); + graph.addVertex("J1"); + graph.addEdge("J0", "J1"); + graph.addVertex("J2"); + graph.addEdge("J0", "J2"); + graph.addVertex("J4"); + graph.addEdge("J1", "J4"); + graph.addVertex("J3"); + graph.addEdge("J2", "J3"); + + graph.addEdge(base, "J4"); + + graph.addVertex("J5"); + graph.addEdge("J2", "J5"); + graph.addVertex("J6"); + graph.addEdge("J4", "J6"); + graph.addVertex("J7"); + graph.addEdge("J4", "J7"); + graph.addVertex("J8"); + graph.addEdge("J3", "J8"); + graph.addVertex("B9"); + graph.addEdge("J6", "B9"); + return graph; + } +} diff --git a/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/DemoWidget.java b/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/DemoWidget.java new file mode 100644 index 0000000..8189f51 --- /dev/null +++ b/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/DemoWidget.java @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2013, Tim Boudreau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.timboudreau.vl.jungrapht.demo; + +import com.timboudreau.vl.jungrapht.JungraphtScene; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.color.ColorSpace; +import java.awt.geom.AffineTransform; +import java.awt.geom.RoundRectangle2D; +import java.util.HashMap; +import java.util.Map; +import org.netbeans.api.visual.action.ActionFactory; +import org.netbeans.api.visual.action.TextFieldInplaceEditor; +import org.netbeans.api.visual.widget.Widget; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; + +/** + * + * @author Tim Boudreau + */ +public class DemoWidget extends Widget { + + private Stroke stroke = new BasicStroke(2); + private String label = ""; + final String node; + private final Lookup lkp; + + public DemoWidget(JungraphtScene scene, String node) { + super(scene); + lkp = Lookups.fixed(node); + this.node = node; + setBackground(new Color(240, 240, 255)); + setForeground(Color.gray); + getActions().addAction(ActionFactory.createInplaceEditorAction(new TextFieldInplaceEditor() { + + @Override + public boolean isEnabled(Widget widget) { + return true; + } + + @Override + public String getText(Widget widget) { + return label; + } + + @Override + public void setText(Widget widget, String text) { + label = text; + revalidate(); + } + })); + } + + @Override + public Lookup getLookup() { + return lkp; + } + + public void setLabel(String label) { + this.label = label; + } + + public void setStroke(Stroke stroke) { + this.stroke = stroke; + } + + private JungraphtScene scene() { + return (JungraphtScene) getScene(); + } + + private Shape getShape() { + // Get the node + N node = (N) scene().findObject(this); + // Grow the shape based on the number of connections + int ix = scene().graph().degreeOf(node); + // Minimum size 18 pixels, grow by 3 for each connection + double w, h; + w = h = 18 + (ix * 3); + + double labelWidth = getGraphics().getFontMetrics().stringWidth(label); + w = Math.max(w, labelWidth + 18); + +// Ellipse2D.Double e = new Ellipse2D.Double(0, 0, w, h); + RoundRectangle2D.Double e = new RoundRectangle2D.Double(-w / 2D, -h / 2D, w, h, 8, 8); + return e; + } + + @Override + protected Rectangle calculateClientArea() { + // Stroke the shape to make sure we include the line width in our + // bounding box + return stroke.createStrokedShape(getShape()).getBounds(); + } + + private Paint getPaint() { + Color start = (Color) getBackground(); + Color end = start.brighter(); + Rectangle r = getClientArea(); + float x = r.x; + float y = r.y; + float x1 = r.x + r.width; + float y1 = r.y + r.height; + GradientPaint result; + if (getScene().getSceneAnimator().isAnimatingBackgroundColor(this)) { + // Don't cache transient gradient paints, just ones that will + // be used repeatedly + result = new GradientPaint(x, y, start, x1, y1, end); + } else { + result = CACHE.getPaint(x, y, start, x1, y1, end); + } + return result; + } + + @Override + protected void paintWidget() { + Graphics2D g = getGraphics(); + + g.setFont(getFont()); + g.setStroke(stroke); + // First fill the shape + Shape shape = getShape(); + // Set up our gradient + g.setPaint(getPaint()); + g.fill(shape); + + // Now draw the outline + g.setPaint(getForeground()); + g.draw(shape); + + // Now draw toString() on the node + float ht = g.getFontMetrics().getHeight(); + float w = g.getFontMetrics().stringWidth(label); + Rectangle r = getClientArea(); + g.setColor(getTextColor()); + float y = (float) r.getCenterY() - (ht / 2F); + y += g.getFontMetrics().getMaxAscent(); + float x = (float) r.getCenterX() - (w / 2F); + g.drawString(label, x, y); + + // Draw a highlight shape if it is selected + if (scene().getSelection().isSelected(scene().findObject(this))) { + g.setColor(new Color(150, 150, 250)); + AffineTransform scale = AffineTransform.getScaleInstance(0.8, 0.8); + double width = (shape.getBounds().getWidth() * 0.0125D); + scale.concatenate(AffineTransform.getTranslateInstance(width, width)); + shape = scale.createTransformedShape(shape); + g.draw(shape); + } + } + + private static GPCache CACHE = new GPCache(); + + private Color getTextColor() { + // Convoluted but works. + Color c = (Color) getBackground(); + // Get the grascale version of the color, so that we handle perceptual + // differences in dark/light - a highly saturated blue is bright according + // to HSB a dark color is not readable against it + ColorSpace grayscale = ColorSpace.getInstance(ColorSpace.CS_GRAY); + float[] rgb = new float[]{(float) c.getRed() / 255F, + (float) c.getGreen() / 255F, (float) c.getBlue() / 255F}; +// rgb = grayscale.fromRGB(rgb); + // Invert the grayscale version of all color components + for (int i = 0; i < rgb.length; i++) { + rgb[i] = 1F - rgb[i]; + } + // Convert it back to RGB values between 0.0F and 1.0F + rgb = grayscale.toRGB(rgb); + // Convert it back to a color, doing some additional computation - + // we want values very close to 0.5F - neutral gray - not to be + // indistinguishable, so have values between 0.3 and 0.7 repel + // the value + return new Color(toByteValue(rgb[0]), toByteValue(rgb[1]), toByteValue(rgb[2])); + } + + private int toByteValue(float f) { + float dist = 0.5F - f; + // bounce the value away from 0.5 + if (Math.abs(dist) < 0.4) { + f *= 1F - dist; + } + // multiply it back into a Color value and constrain it within 0-255 + float result = Math.min(255F, Math.max(0F, f * 255F)); + return (int) result; + } + + /** + * GradientPaint allocates a fairly large byte[] raster; since the number of + * these needed is finite, we use a cache to reduce memory pressure. + */ + private static class GPCache { + + private final Map paints = new HashMap<>(); + + public GradientPaint getPaint(float x, float y, Color start, float x1, float y1, Color end) { + // A key that will be unique by the passed parameters + String key = (int) x + "-" + (int) y + "-" + (int) x1 + "-" + (int) y1 + ':' + c2s(start) + ',' + c2s(end); + GradientPaint p = paints.get(key); + if (p == null) { + if (paints.size() > 50) { + // Don't let the cache get huge + paints.clear(); + } + p = new GradientPaint(x, y, start, x1, y1, end); + paints.put(key, p); + } + return p; + } + + private String c2s(Color color) { + return Integer.toHexString(color.getRGB()); + } + } +} diff --git a/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/SceneImpl.java b/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/SceneImpl.java new file mode 100644 index 0000000..507bc30 --- /dev/null +++ b/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/SceneImpl.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2013, Tim Boudreau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.timboudreau.vl.jungrapht.demo; + +import com.timboudreau.vl.jungrapht.MultiMoveAction; +import com.timboudreau.vl.jungrapht.ObjectSceneAdapter; +import com.timboudreau.vl.jungrapht.extensions.BaseJungraphtScene; +import org.jgrapht.ListenableGraph; +import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm; +import org.netbeans.api.visual.model.ObjectSceneEvent; +import org.netbeans.api.visual.model.ObjectSceneEventType; +import org.netbeans.api.visual.widget.LabelWidget; +import org.netbeans.api.visual.widget.LayerWidget; +import org.netbeans.api.visual.widget.Widget; +import org.openide.util.RequestProcessor; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.EventQueue; +import java.awt.Point; +import java.awt.Rectangle; +import java.io.IOException; + +/** + * + * @author Tim Boudreau + */ +class SceneImpl extends BaseJungraphtScene { + + private final LayerWidget edgeTooltipLayer = new LayerWidget(this); + private final LabelWidget label = new LabelWidget(this); + + public SceneImpl(ListenableGraph graph, LayoutAlgorithm layoutAlgorithm) throws IOException { + super(graph, layoutAlgorithm); + addChild(edgeTooltipLayer); + edgeTooltipLayer.addChild(label); + addObjectSceneListener(new HoverListener(), ObjectSceneEventType.OBJECT_HOVER_CHANGED); + } + + @Override + protected Widget createNodeWidget(String node) { + DemoWidget w = new DemoWidget(this, node); + w.setLabel(node + ""); + w.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + w.getActions().addAction(new MultiMoveAction(relatedProvider(), moveProvider())); + return w; + } + + private class HoverListener extends ObjectSceneAdapter implements Runnable { + + private final RequestProcessor.Task task = RequestProcessor.getDefault().create(this); + private Widget widget; + private String hover; + + @Override + public void run() { + if (!EventQueue.isDispatchThread()) { + EventQueue.invokeLater(this); + } else if (widget != null && hover != null) { + Rectangle r = widget.getClientArea(); + Point p = new Point((int) r.getCenterX(), (int) r.getCenterY()); + label.setForeground(new Color(255, 255, 255, 0)); + label.setPreferredLocation(p); + getSceneAnimator().animateForegroundColor(label, Color.black); + } + } + + @Override + public void hoverChanged(ObjectSceneEvent event, Object previousHoveredObject, Object newHoveredObject) { + if (newHoveredObject instanceof String) { + hover = (String) newHoveredObject; + widget = findWidget(hover); + task.schedule(750); + } else { + widget = null; + hover = null; + task.cancel(); + getSceneAnimator().animateForegroundColor(label, new Color(255, 255, 255, 0)); + } + } + } +} diff --git a/vl-jungrapht-demo/target/maven-archiver/pom.properties b/vl-jungrapht-demo/target/maven-archiver/pom.properties new file mode 100644 index 0000000..8d8c704 --- /dev/null +++ b/vl-jungrapht-demo/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=vl-jungrapht-demo +groupId=com.mastfrog +version=2.3 diff --git a/vl-jungrapht-demo/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/vl-jungrapht-demo/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..d1072f3 --- /dev/null +++ b/vl-jungrapht-demo/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,17 @@ +com/timboudreau/vl/jungrapht/demo/App$1.class +com/timboudreau/vl/jungrapht/demo/App$MinSizePanel.class +com/timboudreau/vl/jungrapht/demo/App$2.class +com/timboudreau/vl/jungrapht/demo/App$6.class +com/timboudreau/vl/jungrapht/demo/SceneImpl.class +com/timboudreau/vl/jungrapht/demo/App$GraphAndForest.class +com/timboudreau/vl/jungrapht/demo/App.class +com/timboudreau/vl/jungrapht/demo/SceneImpl$HoverListener.class +com/timboudreau/vl/jungrapht/demo/App$5.class +com/timboudreau/vl/jungrapht/demo/App$4.class +com/timboudreau/vl/jungrapht/demo/DemoWidget$GPCache.class +com/timboudreau/vl/jungrapht/demo/DemoWidget.class +com/timboudreau/vl/jungrapht/demo/BalloonLayoutDemo$1.class +com/timboudreau/vl/jungrapht/demo/BalloonLayoutDemo.class +com/timboudreau/vl/jungrapht/demo/SceneImpl$1.class +com/timboudreau/vl/jungrapht/demo/App$3.class +com/timboudreau/vl/jungrapht/demo/DemoWidget$1.class diff --git a/vl-jungrapht-demo/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/vl-jungrapht-demo/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..8d88d87 --- /dev/null +++ b/vl-jungrapht-demo/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,4 @@ +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/SceneImpl.java +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/DemoWidget.java +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/BalloonLayoutDemo.java +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht-demo/src/main/java/com/timboudreau/vl/jungrapht/demo/App.java diff --git a/vl-jungrapht-extensions/pom.xml b/vl-jungrapht-extensions/pom.xml new file mode 100644 index 0000000..755d6a3 --- /dev/null +++ b/vl-jungrapht-extensions/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + com.mastfrog + visual-library-jung + 2.3 + + com.mastfrog + vl-jungrapht-extensions + 2.3 + Visual Library JunGraphT Base Classes + + + org.slf4j + slf4j-api + ${version.slf4j} + + + junit + junit + 4.11 + test + + + com.mastfrog + vl-jungrapht + ${project.version} + + + diff --git a/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/BaseJungraphtScene.java b/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/BaseJungraphtScene.java new file mode 100644 index 0000000..f899703 --- /dev/null +++ b/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/BaseJungraphtScene.java @@ -0,0 +1,536 @@ +/* + * Copyright (c) 2013, Tim Boudreau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.timboudreau.vl.jungrapht.extensions; + +import com.timboudreau.vl.jungrapht.JungraphtConnectionWidget; +import com.timboudreau.vl.jungrapht.JungraphtScene; +import com.timboudreau.vl.jungrapht.ObjectSceneAdapter; +import com.timboudreau.vl.jungrapht.RingsWidget; +import static com.timboudreau.vl.jungrapht.extensions.States.CONNECTED_TO_SELECTION; +import static com.timboudreau.vl.jungrapht.extensions.States.HOVERED; +import static com.timboudreau.vl.jungrapht.extensions.States.INDIRECTLY_CONNECTED_TO_SELECTION; +import static com.timboudreau.vl.jungrapht.extensions.States.SELECTED; +import static com.timboudreau.vl.jungrapht.extensions.States.UNRELATED_TO_SELECTION; +import java.awt.Color; +import java.awt.Dimension; +import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import javax.swing.JComponent; + +import org.jgrapht.Graph; +import org.jungrapht.visualization.layout.algorithms.BalloonLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm; +import org.netbeans.api.visual.action.ActionFactory; +import org.netbeans.api.visual.action.WidgetAction; +import org.netbeans.api.visual.anchor.Anchor; +import org.netbeans.api.visual.anchor.AnchorFactory; +import org.netbeans.api.visual.layout.LayoutFactory; +import org.netbeans.api.visual.model.ObjectSceneEvent; +import org.netbeans.api.visual.model.ObjectSceneEventType; +import org.netbeans.api.visual.model.ObjectState; +import org.netbeans.api.visual.widget.ConnectionWidget; +import org.netbeans.api.visual.widget.LayerWidget; +import org.netbeans.api.visual.widget.Widget; +import org.netbeans.api.visual.widget.general.IconNodeWidget; + +/** + * A convenience base class for Jungrapht scenes which draws connections and + * handles layers correctly. + * + * @author Tim Boudreau + * @param + * @param + */ +public class BaseJungraphtScene extends JungraphtScene { + + protected final LayerWidget mainLayer = new LayerWidget(this); + protected final LayerWidget connectionLayer = new LayerWidget(this); + protected final LayerWidget selectionLayer = new LayerWidget(this); + private final HoverAndSelectionHandler hover = new HoverAndSelectionHandler(); + protected final Widget decorationLayer = new RingsWidget(this); + private final GraphTheme colors; + private WidgetAction scrollZoom; + private WidgetAction edgeClickSelect; + private Dimension lastSize = new Dimension(); + + public BaseJungraphtScene(Graph graph, LayoutAlgorithm layoutAlgorithm) throws IOException { + super(graph, layoutAlgorithm); + colors = createColors(); + // Selection layer is behind everything + addChild(selectionLayer); + // Rings also drawn behind everything but the selection + addChild(decorationLayer); + // Connections are drawn below the node widgets + addChild(connectionLayer); + // The layer where node widgets live + addChild(mainLayer); + + + // Set some layouts + connectionLayer.setLayout(LayoutFactory.createAbsoluteLayout()); + mainLayer.setLayout(LayoutFactory.createAbsoluteLayout()); + // Zoom on scroll by default + getActions().addAction(ActionFactory.createMouseCenteredZoomAction(1.1)); + getActions().addAction(ActionFactory.createPanAction()); + + // Use the built in rectangular selection action + getActions().addAction(ActionFactory.createRectangularSelectAction(this, + selectionLayer)); + + // Add the listener which will notice when we hover and update + // the node color + addObjectSceneListener(hover, ObjectSceneEventType.OBJECT_HOVER_CHANGED, + ObjectSceneEventType.OBJECT_SELECTION_CHANGED); + } + + /** + * Create a color theme. The default one starts with one color and + * modifies it to portray different selection states. + * @return A theme + */ + protected GraphTheme createColors() { + return new GraphThemeImpl(); + } + + /** + * Re-layout the graph + */ + public void relayout(boolean animate) { + JComponent vw = getView(); + if (vw != null) { + int preferredWidth = layoutModel().getPreferredWidth(); + int preferredHeight = layoutModel().getPreferredHeight(); + model.setSize(preferredWidth, preferredHeight); + } + super.performLayout(animate); + } + + protected Widget createNodeWidget(N node) { + IconNodeWidget result = new IconNodeWidget(this); + result.setLabel(node + ""); + return result; + } + + protected void attachActionsToNodeWidget(Widget widget) { + widget.getActions().addAction(createNodeMoveAction()); + widget.getActions().addAction(createObjectHoverAction()); + widget.getActions().addAction(createSelectAction()); + widget.getActions().addAction(createSelectByClickAction()); + } + + @Override + protected Widget attachNodeWidget(final N node) { + Widget widget = createNodeWidget(node); + // Set up the colors and actions + widget.setBackground(colors.getBackground()); + widget.setForeground(colors.getForeground()); + attachActionsToNodeWidget(widget); + mainLayer.addChild(widget); + validate(); + return widget; + } + + protected Widget createEdgeWidget(E edge) { + JungraphtConnectionWidget w = JungraphtConnectionWidget.createQuadratic(this, edge); + return w; + } + + @Override + protected Widget attachEdgeWidget(final E edge) { + Widget w = createEdgeWidget(edge); + w.setForeground(colors.getEdgeColor()); + w.getActions().addAction(createObjectHoverAction()); + w.getActions().addAction(createEdgeClickSelectAction()); + connectionLayer.addChild(w); + return w; + } + + protected WidgetAction createEdgeClickSelectAction() { + if (edgeClickSelect == null) { + edgeClickSelect = new EdgeClickSelectAction(); + } + return edgeClickSelect; + } + + @Override + public void onMove(N n, Widget widget) { + connectionLayer.repaint(); + if (layout instanceof BalloonLayoutAlgorithm) { + decorationLayer.revalidate(); + } + } + + @Override + protected void attachEdgeSourceAnchor(E edge, N oldSourceNode, N sourceNode) { + Widget w = findWidget(edge); + // Not the case by default, but could be overridden + if (w instanceof ConnectionWidget) { + Widget sourceNodeWidget = findWidget(sourceNode); + Anchor sourceAnchor = AnchorFactory.createCenterAnchor(sourceNodeWidget); + ConnectionWidget edgeWidget = (ConnectionWidget) w; + edgeWidget.setSourceAnchor(sourceAnchor); + } + } + + @Override + protected void attachEdgeTargetAnchor(E edge, N oldTargetNode, N targetNode) { + Widget w = findWidget(edge); + // Not the case by default, but could be overridden + if (w instanceof ConnectionWidget) { + Widget targetNodeWidget = findWidget(targetNode); + Anchor targetAnchor = AnchorFactory.createCenterAnchor(targetNodeWidget); + ConnectionWidget edgeWidget = (ConnectionWidget) findWidget(edge); + edgeWidget.setTargetAnchor(targetAnchor); + } + } + + /** + * Called when an edge stops being hovered + * @param edge The edge + * @param w The widget + */ + protected void onEdgeUnhover(E edge, Widget w) { + Color c = colors.getEdgeColor(); + getSceneAnimator().animateForegroundColor(w, c); + } + + /** + * Called when an edge becomes hovered + * @param edge The edge + * @param w The widget + */ + protected void onEdgeHover(E edge, Widget w) { + Color c = colors.getEdgeColor(HOVERED); + getSceneAnimator().animateForegroundColor(w, c); + } + + /** + * Called when a node stops being hovered + * @param n The node + * @param w The widget + */ + protected void onNodeUnhover(N n, Widget w) { + ObjectState state = getObjectState(n); + boolean hasSelection = !getSelectedObjects().isEmpty(); + boolean connected = getSelection().isConnectedToSelection(n); + boolean indirect = !connected && !state.isSelected() && getSelection().isIndirectlyConnectedToSelection(n); + States[] states = state.isSelected() ? new States[]{SELECTED} + : hasSelection ? new States[]{connected ? CONNECTED_TO_SELECTION : indirect ? INDIRECTLY_CONNECTED_TO_SELECTION : UNRELATED_TO_SELECTION} : new States[0]; + Color c = colors.getEdgeColor(states); + for (E edge : findNodeEdges(n, true, false)) { + Widget w1 = findWidget(edge); + getSceneAnimator().animateForegroundColor(w1, c); + } + getSceneAnimator().animateBackgroundColor(w, colors.getBackground(states)); + } + + /** + * Called when a node starts being hovered + * @param n The node + * @param w The widget + */ + protected void onNodeHover(N n, Widget w) { + ObjectState state = getObjectState(n); + boolean hasSelection = !getSelectedObjects().isEmpty(); + boolean connected = getSelection().isConnectedToSelection(n); + boolean indirect = !connected && !state.isSelected() && getSelection().isIndirectlyConnectedToSelection(n); + States[] states = state.isSelected() + ? new States[]{SELECTED, HOVERED} + : hasSelection + ? new States[]{HOVERED, connected ? CONNECTED_TO_SELECTION : indirect ? INDIRECTLY_CONNECTED_TO_SELECTION : UNRELATED_TO_SELECTION} + : new States[]{HOVERED}; + + Color c = colors.getEdgeColor(states); + for (E edge : findNodeEdges(n, true, false)) { + Widget w1 = findWidget(edge); + getSceneAnimator().animateForegroundColor(w1, c); + } + getSceneAnimator().animateBackgroundColor(w, colors.getBackground(states)); + } + + /** + * Called when the selection is cleared, once for every node widget + * @param w The widget + * @param n The node + */ + protected void onSelectionCleared(Widget w, N n) { + getSceneAnimator().animateBackgroundColor(w, colors.getBackground(statesFor(null, n))); + getSceneAnimator().animateForegroundColor(w, colors.getForeground(statesFor(null, n))); + } + + /** + * Called when the selection is cleared, once for every edge widget + * @param w The widget + * @param e The edge + */ + protected void onEdgeSelectionCleared(Widget w, E e) { + getSceneAnimator().animateForegroundColor(w, colors.getEdgeColor(statesFor(null, e))); + } + + /** + * Called when a node becomes selected + * @param w The widget + * @param n The node + */ + protected void onNodeSelected(Widget w, N n) { + getSceneAnimator().animateBackgroundColor(w, colors.getBackground(statesFor(SELECTED, n))); + getSceneAnimator().animateForegroundColor(w, colors.getForeground(statesFor(SELECTED, n))); + } + + /** + * Called when a node connected to this one was selected assuming it is not + * also selected + * @param w The widget + * @param n The node + */ + protected void onNodeConnectedToSelection(Widget w, N n) { + getSceneAnimator().animateBackgroundColor(w, + colors.getBackground(statesFor(CONNECTED_TO_SELECTION, n))); + getSceneAnimator().animateForegroundColor(w, + colors.getForeground(statesFor(CONNECTED_TO_SELECTION, n))); + } + + /** + * Called when a node connected to this one is connected to the selected node, + * assuming this one is not selected or directly connected to the selected node + * @param w The widget + * @param n The node + */ + protected void onNodeIndirectlyConnectedToSelection(Widget w, N n) { + getSceneAnimator().animateBackgroundColor(w, colors.getBackground(statesFor(INDIRECTLY_CONNECTED_TO_SELECTION, n))); + getSceneAnimator().animateForegroundColor(w, colors.getForeground(statesFor(INDIRECTLY_CONNECTED_TO_SELECTION, n))); + } + + /** + * Called when a node becomes unconnected to any selected node due to a + * selection change + * @param w The widget + * @param n The node + */ + protected void onNodeUnrelatedToSelection(Widget w, N n) { + getSceneAnimator().animateBackgroundColor(w, colors.getBackground(statesFor(UNRELATED_TO_SELECTION, n))); + getSceneAnimator().animateForegroundColor(w, colors.getForeground(statesFor(UNRELATED_TO_SELECTION, n))); + } + + /** + * Called when an edge becomes connected to the selection due to a selection + * change + * @param e The edge + */ + protected void onEdgeConnectedToSelection(E e) { + getSceneAnimator().animateForegroundColor(findWidget(e), colors.getEdgeColor(statesFor(CONNECTED_TO_SELECTION, e))); + } + + /** + * Called when an edge becomes connected to a node which is connected to the + * selection but not selected itself, due to a selection change + * @param e The edge + */ + protected void onEdgeIndirectlyConnectedToSelection(E e) { + getSceneAnimator().animateForegroundColor(findWidget(e), colors.getEdgeColor(statesFor(INDIRECTLY_CONNECTED_TO_SELECTION, e))); + } + + /** + * Get the set of states which apply to this object, in terms of an array + * of States. + * @param curr A state to include in the reuslt if non-null + * @param o The object - may be an edge or node + * @return + */ + public States[] statesFor(States curr, Object o) { + ObjectState st = getObjectState(o); + if (st.isHovered()) { + return curr == null ? new States[]{States.HOVERED} : new States[]{curr, States.HOVERED}; + } else { + return curr == null ? new States[0] : new States[]{curr}; + } + } + + private class HoverAndSelectionHandler extends ObjectSceneAdapter { + + @Override + public void hoverChanged(ObjectSceneEvent event, Object previousHoveredObject, Object newHoveredObject) { + @SuppressWarnings("element-type-mismatch") + boolean wasEdge = getEdges().contains(previousHoveredObject); + @SuppressWarnings("element-type-mismatch") + boolean wasNode = getNodes().contains(previousHoveredObject); + @SuppressWarnings("element-type-mismatch") + boolean isEdge = getEdges().contains(newHoveredObject); + @SuppressWarnings("element-type-mismatch") + boolean isNode = getNodes().contains(newHoveredObject); + if (wasEdge) { + E edge = (E) previousHoveredObject; + onEdgeUnhover(edge, findWidget(edge)); + } else if (wasNode) { + N node = (N) previousHoveredObject; + onNodeUnhover(node, findWidget(node)); + } + if (isEdge) { + E edge = (E) newHoveredObject; + onEdgeHover(edge, findWidget(edge)); + } else if (isNode) { + N node = (N) newHoveredObject; + onNodeHover(node, findWidget(node)); + } + } + + @Override + public void selectionChanged(ObjectSceneEvent event, Set previousSelection, Set newSelection) { + if (newSelection.isEmpty()) { + // Special case the selection being empty + for (N n : getNodes()) { + Widget w = findWidget(n); + onSelectionCleared(w, n); + } + for (E e : getEdges()) { + Widget w = findWidget(e); + if (w != null) { + onEdgeSelectionCleared(w, e); + } + } + return; + } + // Find all the nodes which used to be selected but are not anymore + Set noLongerSelected = new HashSet<>(previousSelection); + noLongerSelected.removeAll(newSelection); + + // Get the set of all nodes we know about in the sscene + Set remainingNodes = new HashSet(getNodes()); + + // Get the set of all edges we know about in the scene + Set otherEdges = new HashSet<>(getEdges()); + Set closeEdges = new HashSet<>(); + Set farEdges = new HashSet<>(); + + // Walk the graph, finding all the relevant edges and nodes and + // their selection state + Set newlySelected = new HashSet<>(newSelection); + newlySelected.removeAll(previousSelection); + for (Object o : newlySelected) { + remainingNodes.remove(o); + Widget w = findWidget(o); + onNodeSelected(w, (N) o); + } + Set connectedNodes = new HashSet<>(); + for (Object o : newlySelected) { + if (graph.vertexSet().contains(o)) { + N n = (N) o; + Collection edges = graph.outgoingEdgesOf(n); + if (edges != null) { + for (E edge : graph.outgoingEdgesOf(n)) { + closeEdges.add(edge); + otherEdges.remove(edge); + N opp = graph.getEdgeTarget(edge); + remainingNodes.remove(opp); + connectedNodes.add(opp); + if (!newlySelected.contains(opp)) { + Widget w = findWidget(opp); + onNodeConnectedToSelection(w, opp); + } + } + } + } + } + for (N n : connectedNodes) { + Collection edges = graph.incomingEdgesOf(n); + if (edges != null) { + for (E edge : graph.outgoingEdgesOf(n)) { + closeEdges.add(edge); + otherEdges.remove(edge); + N opp = graph.getEdgeTarget(edge); + remainingNodes.remove(opp); + if (!newSelection.contains(opp) && !connectedNodes.contains(opp)) { + Widget w = findWidget(opp); + onNodeIndirectlyConnectedToSelection(w, opp); + } + } + } + } + for (Object o : remainingNodes) { + Widget w = findWidget(o); + onNodeUnrelatedToSelection(w, (N) o); + } + for (E e : closeEdges) { + onEdgeConnectedToSelection(e); + } + for (E e : farEdges) { + onEdgeIndirectlyConnectedToSelection(e); + } + for (E e : otherEdges) { + onEdgeSelectionCleared(findWidget(e), e); + } + mainLayer.repaint(); + } + } + + private class EdgeClickSelectAction extends WidgetAction.Adapter { + + @Override + public WidgetAction.State mouseClicked(Widget widget, WidgetAction.WidgetMouseEvent event) { + E edge = (E) findObject(widget); + HashSet nue = new HashSet<>(); + nue.add(graph.getEdgeSource(edge)); + nue.add(graph.getEdgeTarget(edge)); +// graph.getEndpoints(edge)); + Set selection = new HashSet<>(getSelectedObjects()); + if (selection.isEmpty() || !event.isShiftDown()) { + setSelectedObjects(nue); + } else { + if (selection.containsAll(nue) && !event.isShiftDown()) { + selection.removeAll(nue); + setSelectedObjects(selection); + } else { + selection.addAll(nue); + setSelectedObjects(selection); + } + } + return WidgetAction.State.REJECTED; + } + } + + protected WidgetAction createScrollWheelAction() { + if (scrollZoom == null) { + scrollZoom = new ScrollWheelZoomAction(); + } + return scrollZoom; + } + + private class ScrollWheelZoomAction extends WidgetAction.Adapter { + + @Override + public WidgetAction.State mouseWheelMoved(Widget widget, WidgetAction.WidgetMouseWheelEvent event) { + double zoom = getZoomFactor(); + int units = event.getUnitsToScroll(); + double amt = (double) units * 0.025D; + zoom = Math.max(0.1D, zoom + amt); + setZoomFactor(zoom); + repaint(); + return WidgetAction.State.CONSUMED; + } + } +} diff --git a/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/GraphTheme.java b/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/GraphTheme.java new file mode 100644 index 0000000..011ef3b --- /dev/null +++ b/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/GraphTheme.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013, Tim Boudreau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.timboudreau.vl.jungrapht.extensions; + +import java.awt.Color; + +/** + * + * @author Tim Boudreau + */ +public interface GraphTheme { + + Color getBackground(States... states); + + Color getEdgeColor(States... states); + + Color getForeground(States... states); + +} diff --git a/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/GraphThemeImpl.java b/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/GraphThemeImpl.java new file mode 100644 index 0000000..de65d8f --- /dev/null +++ b/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/GraphThemeImpl.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2013, Tim Boudreau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.timboudreau.vl.jungrapht.extensions; + +import java.awt.Color; +import java.util.Arrays; +import java.util.List; + +/** + * Simple implementation of GraphTheme which uses base colors and modifies + * the hue, saturation and brightness to derive theme colors. + * + * @author Tim Boudreau + */ +public class GraphThemeImpl implements GraphTheme { + + private final Color bgBase; + private final Color fgBase; + + public GraphThemeImpl() { + this(new Color(80, 110, 240), new Color(130, 140, 230)); + } + + public GraphThemeImpl(Color bgBase) { + this.bgBase = bgBase; + this.fgBase = new Color( + 255 - bgBase.getRed(), + 255 - bgBase.getGreen(), + 255 - bgBase.getBlue()); + } + + public GraphThemeImpl(Color bgBase, Color fgBase) { + this.bgBase = bgBase; + this.fgBase = fgBase; + } + + @Override + public Color getBackground(States... states) { + List l = Arrays.asList(states); + if (l.size() == 2 && l.contains(States.UNRELATED_TO_SELECTION) + && l.contains(States.HOVERED)) { + return Color.ORANGE.darker(); + } + if (states.length == 0) { + return Color.GRAY; + } + return adjust(bgBase, 1, states); + } + + @Override + public Color getForeground(States... states) { + List l = Arrays.asList(states); + if (l.size() == 2 && l.contains(States.UNRELATED_TO_SELECTION) + && l.contains(States.HOVERED)) { + return Color.BLACK; + } + return adjust(fgBase, -1, states); + } + + @Override + public Color getEdgeColor(States... states) { + List l = Arrays.asList(states); + if (l.size() == 2 && l.contains(States.UNRELATED_TO_SELECTION) + && l.contains(States.HOVERED)) { + return new Color(240, 240, 120); + } + return adjust(fgBase, 1, states); + } + + private Color adjust(Color color, int direction, States... states) { + float dir = direction; + float[] hsb = new float[3]; + Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), hsb); + if (states.length == 0) { + hsb[1] = 0.1F; + } else { + for (States s : states) { + s.adjust(dir, hsb); + } + } + return new Color(Color.HSBtoRGB(hsb[0], hsb[1], hsb[2])); + } +} diff --git a/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/States.java b/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/States.java new file mode 100644 index 0000000..ef09f08 --- /dev/null +++ b/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/States.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2013, Tim Boudreau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.timboudreau.vl.jungrapht.extensions; + +/** + * + * @author Tim Boudreau + */ +public enum States { + SELECTED, HOVERED, CONNECTED_TO_SELECTION, INDIRECTLY_CONNECTED_TO_SELECTION, UNRELATED_TO_SELECTION; + + private float clamp(float flt) { + return Math.max(0F, Math.min(1.0F, flt)); + } + + private float rotate(float flt, float adj, float dir) { + adj *= dir; + flt += adj; + if (flt < 0F) { + flt = 1F - flt; + } else if (flt > 1F) { + flt -= 1F; + } + return clamp(flt); + } + + void adjust(float dir, float[] hsb) { + switch (this) { + case SELECTED: + hsb[1] = dir >= 1 ? 0.75F : 0.25F; + break; + case HOVERED: + hsb[0] = rotate(hsb[0], 0.10F, -dir); + hsb[1] = clamp(hsb[1] + 0.1F); + break; + case CONNECTED_TO_SELECTION: + hsb[0] = rotate(hsb[0], 0.05F, dir); + hsb[1] = 0.35F; + hsb[2] = clamp(hsb[2] + (-dir * 0.25F)); + break; + case INDIRECTLY_CONNECTED_TO_SELECTION: + hsb[0] = rotate(hsb[0], 0.05F, dir); + hsb[1] = 0.25F; + hsb[2] = clamp(hsb[2] + (dir * 0.1F)); + break; + case UNRELATED_TO_SELECTION: + hsb[1] = 0.1F; + hsb[2] = clamp(hsb[2] + 0.25F); + break; + } + } + +} diff --git a/vl-jungrapht-extensions/target/maven-archiver/pom.properties b/vl-jungrapht-extensions/target/maven-archiver/pom.properties new file mode 100644 index 0000000..34506cd --- /dev/null +++ b/vl-jungrapht-extensions/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=vl-jungrapht-extensions +groupId=com.mastfrog +version=2.3 diff --git a/vl-jungrapht-extensions/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/vl-jungrapht-extensions/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..1e3fe12 --- /dev/null +++ b/vl-jungrapht-extensions/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,9 @@ +com/timboudreau/vl/jungrapht/extensions/BaseJungraphtScene$EdgeClickSelectAction.class +com/timboudreau/vl/jungrapht/extensions/BaseJungraphtScene$HoverAndSelectionHandler.class +com/timboudreau/vl/jungrapht/extensions/GraphThemeImpl.class +com/timboudreau/vl/jungrapht/extensions/BaseJungraphtScene.class +com/timboudreau/vl/jungrapht/extensions/GraphTheme.class +com/timboudreau/vl/jungrapht/extensions/States$1.class +com/timboudreau/vl/jungrapht/extensions/BaseJungraphtScene$1.class +com/timboudreau/vl/jungrapht/extensions/States.class +com/timboudreau/vl/jungrapht/extensions/BaseJungraphtScene$ScrollWheelZoomAction.class diff --git a/vl-jungrapht-extensions/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/vl-jungrapht-extensions/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..2a160fd --- /dev/null +++ b/vl-jungrapht-extensions/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,4 @@ +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/States.java +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/GraphThemeImpl.java +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/GraphTheme.java +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht-extensions/src/main/java/com/timboudreau/vl/jungrapht/extensions/BaseJungraphtScene.java diff --git a/vl-jungrapht-nbm/licenseheader.txt b/vl-jungrapht-nbm/licenseheader.txt new file mode 120000 index 0000000..3a757f7 --- /dev/null +++ b/vl-jungrapht-nbm/licenseheader.txt @@ -0,0 +1 @@ +../licenseheader.txt \ No newline at end of file diff --git a/vl-jungrapht-nbm/nb-configuration.xml b/vl-jungrapht-nbm/nb-configuration.xml new file mode 100644 index 0000000..98de820 --- /dev/null +++ b/vl-jungrapht-nbm/nb-configuration.xml @@ -0,0 +1,18 @@ + + + + + + ${project.basedir}/licenseheader.txt + + diff --git a/vl-jungrapht-nbm/pom.xml b/vl-jungrapht-nbm/pom.xml new file mode 100644 index 0000000..8623216 --- /dev/null +++ b/vl-jungrapht-nbm/pom.xml @@ -0,0 +1,138 @@ + + + 4.0.0 + + com.mastfrog + visual-library-jung + 2.3 + + + vl-jungrapht-nbm + nbm + https://github.com:timboudreau/vl-jung + Visual Library + JUNG NetBeans Module Wrapper + + git@github.com:timboudreau/vl-jung.git + scm:git:https://github.com:timboudreau/vl-jung.git + git@github.com:timboudreau/vl-jung.git + + + Github + https://github.com/timboudreau/vl-jung/issues + + + Mastfrog Technologies + https://mastfrog.com + + + + BSD-2-Clause + https://opensource.org/licenses/BSD-2-Clause + repo + + + + + timboudreau + Tim Boudreau + https://timboudreau.com + tim+github@timboudreau.com + + + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-netbeans-api-annotations-common + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + + + org.netbeans.api + org-openide-util + + + org.netbeans.api + org-netbeans-api-visual + + + org.netbeans.api + org-netbeans-api-annotations-common + provided + + + com.google.guava + guava + + + org.slf4j + slf4j-api + + + ${project.groupId} + vl-jungrapht + ${project.version} + + + ${project.groupId} + vl-jungrapht-extensions + ${project.version} + + + org.jgrapht + jgrapht-core + ${version.jgrapht} + + + org.jgrapht + jgrapht-io + ${version.jgrapht} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + true + + BSD + ../license.txt + true + true + + com.timboudreau.vl.jungrapht + com.timboudreau.vl.jungrapht.nbm + com.timboudreau.vl.jungrapht.extensions + org.jungrapht.* + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + true + + + + + diff --git a/vl-jungrapht-nbm/src/main/nbm/manifest.mf b/vl-jungrapht-nbm/src/main/nbm/manifest.mf new file mode 100644 index 0000000..523fdfb --- /dev/null +++ b/vl-jungrapht-nbm/src/main/nbm/manifest.mf @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +OpenIDE-Module-Localizing-Bundle: com/timboudreau/vl/jungrapht/nbm/Bundle.properties diff --git a/vl-jungrapht-nbm/src/main/resources/com/timboudreau/vl/jungrapht/nbm/Bundle.properties b/vl-jungrapht-nbm/src/main/resources/com/timboudreau/vl/jungrapht/nbm/Bundle.properties new file mode 100644 index 0000000..673a11a --- /dev/null +++ b/vl-jungrapht-nbm/src/main/resources/com/timboudreau/vl/jungrapht/nbm/Bundle.properties @@ -0,0 +1,30 @@ +# +# Copyright (c) 2013, Tim Boudreau +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +OpenIDE-Module-Name=JUNG / Visual Library Integration +OpenIDE-Module-Short-Description=Integrates the Jungrapht/Graph framework (jungrapht) with NetBeans Visual Library +OpenIDE-Module-Long-Description=Integrates the Jungrapht/Graph framework (jungrapht) with NetBeans Visual Library. \ + Provides Visual Library layout wrappers for jungrapht layouts and widget wrappers for jungrapht's painting logic +OpenIDE-Module-Display-Category=Infrastructure diff --git a/vl-jungrapht-nbm/target/classes/META-INF/MANIFEST.MF b/vl-jungrapht-nbm/target/classes/META-INF/MANIFEST.MF new file mode 100644 index 0000000..1068941 --- /dev/null +++ b/vl-jungrapht-nbm/target/classes/META-INF/MANIFEST.MF @@ -0,0 +1,31 @@ +Manifest-Version: 1.0 +OpenIDE-Module-Localizing-Bundle: com/timboudreau/vl/jungrapht/nbm/Bun + dle.properties +OpenIDE-Module-Specification-Version: 2.3 +OpenIDE-Module-Implementation-Version: 2.3 +AutoUpdate-Show-In-Client: false +OpenIDE-Module-Build-Version: 202012072056 +OpenIDE-Module: com.mastfrog.vl.jungrapht.nbm +OpenIDE-Module-Public-Packages: com.timboudreau.vl.jungrapht.*, com.ti + mboudreau.vl.jungrapht.nbm.*, com.timboudreau.vl.jungrapht.extensions + .*, org.jungrapht.** +OpenIDE-Module-Requires: org.openide.modules.ModuleFormat1 +OpenIDE-Module-Display-Category: com.mastfrog +OpenIDE-Module-Name: Visual Library + JUNG NetBeans Module Wrapper +OpenIDE-Module-Short-Description: Parent project for Visual-Library + + JUNG integration +OpenIDE-Module-Long-Description: Parent project for Visual-Library + J + UNG integration +X-Class-Path: ext/com.mastfrog.vl-jungrapht-nbm/com-mastfrog/vl-jungra + pht.jar ext/com.mastfrog.vl-jungrapht-nbm/com-github-tomnelson/jungra + pht-layout.jar ext/com.mastfrog.vl-jungrapht-nbm/com-github-tomnelson + /jungrapht-visualization.jar ext/com.mastfrog.vl-jungrapht-nbm/com-ma + stfrog/vl-jungrapht-extensions.jar +Maven-Class-Path: com.mastfrog:vl-jungrapht:2.3 com.github.tomnelson:j + ungrapht-layout:1.0 com.github.tomnelson:jungrapht-visualization:1.0 + com.mastfrog:vl-jungrapht-extensions:2.3 +OpenIDE-Module-Module-Dependencies: org.openide.util > 9.14, org.openi + de.util.lookup > 8.40, org.netbeans.api.visual > 2.55, com.google.gua + va > 30.0.0, slf4j.api > 1.7.25, org.jgrapht.core > 1.5.0, org.jgraph + t.io > 1.5.0 + diff --git a/vl-jungrapht-nbm/target/classes/com/timboudreau/vl/jungrapht/nbm/Bundle.properties b/vl-jungrapht-nbm/target/classes/com/timboudreau/vl/jungrapht/nbm/Bundle.properties new file mode 100644 index 0000000..673a11a --- /dev/null +++ b/vl-jungrapht-nbm/target/classes/com/timboudreau/vl/jungrapht/nbm/Bundle.properties @@ -0,0 +1,30 @@ +# +# Copyright (c) 2013, Tim Boudreau +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +OpenIDE-Module-Name=JUNG / Visual Library Integration +OpenIDE-Module-Short-Description=Integrates the Jungrapht/Graph framework (jungrapht) with NetBeans Visual Library +OpenIDE-Module-Long-Description=Integrates the Jungrapht/Graph framework (jungrapht) with NetBeans Visual Library. \ + Provides Visual Library layout wrappers for jungrapht layouts and widget wrappers for jungrapht's painting logic +OpenIDE-Module-Display-Category=Infrastructure diff --git a/vl-jungrapht-nbm/target/maven-archiver/pom.properties b/vl-jungrapht-nbm/target/maven-archiver/pom.properties new file mode 100644 index 0000000..330a33b --- /dev/null +++ b/vl-jungrapht-nbm/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=vl-jungrapht-nbm +groupId=com.mastfrog +version=2.3 diff --git a/vl-jungrapht-nbm/target/nbm/netbeans/extra/config/Modules/com-mastfrog-vl-jungrapht-nbm.xml b/vl-jungrapht-nbm/target/nbm/netbeans/extra/config/Modules/com-mastfrog-vl-jungrapht-nbm.xml new file mode 100644 index 0000000..f965daa --- /dev/null +++ b/vl-jungrapht-nbm/target/nbm/netbeans/extra/config/Modules/com-mastfrog-vl-jungrapht-nbm.xml @@ -0,0 +1,9 @@ + + + + true + false + modules/com-mastfrog-vl-jungrapht-nbm.jar + false + diff --git a/vl-jungrapht-nbm/target/nbm/netbeans/extra/update_tracking/com-mastfrog-vl-jungrapht-nbm.xml b/vl-jungrapht-nbm/target/nbm/netbeans/extra/update_tracking/com-mastfrog-vl-jungrapht-nbm.xml new file mode 100644 index 0000000..d50f7c2 --- /dev/null +++ b/vl-jungrapht-nbm/target/nbm/netbeans/extra/update_tracking/com-mastfrog-vl-jungrapht-nbm.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/vl-jungrapht-nbm/target/nbm/vl-jungrapht-nbm-2.3.nbm b/vl-jungrapht-nbm/target/nbm/vl-jungrapht-nbm-2.3.nbm new file mode 100644 index 0000000..8e41cca Binary files /dev/null and b/vl-jungrapht-nbm/target/nbm/vl-jungrapht-nbm-2.3.nbm differ diff --git a/vl-jungrapht-nbm/target/vl-jungrapht-nbm-2.3.nbm b/vl-jungrapht-nbm/target/vl-jungrapht-nbm-2.3.nbm new file mode 100644 index 0000000..8e41cca Binary files /dev/null and b/vl-jungrapht-nbm/target/vl-jungrapht-nbm-2.3.nbm differ diff --git a/vl-jungrapht/nbactions.xml b/vl-jungrapht/nbactions.xml new file mode 100644 index 0000000..1c0885e --- /dev/null +++ b/vl-jungrapht/nbactions.xml @@ -0,0 +1,32 @@ + + + + run + + jar + + + process-classes + org.codehaus.mojo:exec-maven-plugin:1.2.1:exec + + + -classpath %classpath com.timboudreau.vl.demo.App + java + + + + debug + + jar + + + process-classes + org.codehaus.mojo:exec-maven-plugin:1.2.1:exec + + + -Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath com.timboudreau.vl.demo.App + java + true + + + diff --git a/vl-jungrapht/pom.xml b/vl-jungrapht/pom.xml new file mode 100644 index 0000000..53a6b33 --- /dev/null +++ b/vl-jungrapht/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + com.mastfrog + visual-library-jung + 2.3 + + vl-jungrapht + Visual Library + Jung + https://github.com:timboudreau/vl-jung + An alternative interactive UI for the JUNG graph library, which + uses NetBeans' excellent lightweight Visual Library component framework to simplify + creating interactive graph visualizations + + git@github.com:timboudreau/vl-jung.git + scm:git:https://github.com:timboudreau/vl-jung.git + git@github.com:timboudreau/vl-jung.git + + + Github + https://github.com/timboudreau/vl-jung/issues + + + Mastfrog Technologies + https://mastfrog.com + + + + BSD-2-Clause + https://opensource.org/licenses/BSD-2-Clause + repo + + + + + timboudreau + Tim Boudreau + https://timboudreau.com + tim+github@timboudreau.com + + + + bsd + 1.7.25 + 1.5.0 + 1.0 + + + + org.jgrapht + jgrapht-core + ${version.jgrapht} + + + com.github.tomnelson + jungrapht-layout + ${version.jungrapht} + + + com.github.tomnelson + jungrapht-visualization + ${version.jungrapht} + + + org.slf4j + slf4j-api + ${version.slf4j} + + + org.netbeans.api + org-openide-util-lookup + + + org.netbeans.api + org-netbeans-api-visual + + + junit + junit + test + + + diff --git a/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/GraphSelection.java b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/GraphSelection.java new file mode 100644 index 0000000..e48b262 --- /dev/null +++ b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/GraphSelection.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2013, Tim Boudreau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.timboudreau.vl.jungrapht; + +import org.jgrapht.Graph; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Object for performing graph-related queries about selection + * + * @author Tim Boudreau + */ +public final class GraphSelection { + + private final JungraphtScene scene; + + public GraphSelection(JungraphtScene scene) { + this.scene = scene; + } + + public boolean isSelected(N node) { + return scene.getSelectedObjects().contains(node); + } + + public boolean isIndirectlyConnectedToSelection(N node) { + Set selected = scene.getSelectedObjects(); + for (E e : scene.graph.incomingEdgesOf(node)) { + N opposite = scene.graph.getEdgeSource(e); + for (E e1 : scene.graph.incomingEdgesOf(opposite)) { + if (e1 != e) { + N opposite2 = scene.graph.getEdgeSource(e); + if (selected.contains(opposite2)) { + return true; + } + } + } + } + return false; + } + + public boolean isConnectedToSelection(N node) { + Set nodes = new HashSet<>(scene.graph.vertexSet()); + nodes.retainAll(scene.getSelectedObjects()); + if (nodes.contains(node)) { + return false; + } + for (N n : nodes) { + for (E edge : scene.graph.outgoingEdgesOf(n)) { + if (scene.graph.getEdgeTarget(edge).equals(node)) { + return true; + } + } + } + return false; + } + + public Set getSelection() { + Set nodes = new HashSet<>(scene.graph.vertexSet()); + nodes.retainAll(scene.getSelectedObjects()); + return nodes; + } + + public enum EdgeTypes { + IN, + OUT + } + + public Set getNodesConnectedToSelection(EdgeTypes... et) { + Set sel = getSelection(); + Set edges = getEdgesTouchingSelection(et); + Set result = new HashSet<>(); + for (E e : edges) { + N source = scene.graph.getEdgeSource(e); + N target = scene.graph.getEdgeTarget(e); + if (!sel.contains(source)) { + result.add(source); + } + if (!sel.contains(target)) { + result.add(target); + } + } + return result; + } + + public Set getEdgesTouchingSelection(EdgeTypes... types) { + List tps = Arrays.asList(types); + Set result = new HashSet<>(); + Graph gp = scene.graph; + for (N n : getSelection()) { + if (tps.contains(EdgeTypes.IN)) { + result.addAll(gp.incomingEdgesOf(n)); + } + if (tps.contains(EdgeTypes.OUT)) { + result.addAll(gp.outgoingEdgesOf(n)); + } + } + return result; + } + + public boolean isAttachedToSelection(E edge) { + Set nodes = getSelection(); + return nodes.contains(scene.graph.getEdgeSource(edge)) || nodes.contains(scene.graph.getEdgeTarget(edge)); + } +} diff --git a/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/JungraphtConnectionWidget.java b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/JungraphtConnectionWidget.java new file mode 100644 index 0000000..db11c6a --- /dev/null +++ b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/JungraphtConnectionWidget.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2013, Tim Boudreau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.timboudreau.vl.jungrapht; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Path2D; +import java.util.function.BiFunction; + +import org.jgrapht.Graph; +import org.jungrapht.visualization.decorators.EdgeShape; +import org.jungrapht.visualization.layout.model.LayoutModel; +import org.netbeans.api.visual.widget.Widget; + +/** + * Uses the Transformers defined in EdgeShape to paint edges using various kinds + * of lines and curves + * + * @author Tim Boudreau + */ +public class JungraphtConnectionWidget extends Widget { + private Stroke stroke = new BasicStroke(2); + private final E edge; + private BiFunction, E, Shape> transformer; + + public void setEdgeShapeFunction(BiFunction, E, Shape> transformer) { + this.transformer = transformer; + } + public static JungraphtConnectionWidget createQuadratic(JungraphtScene scene, E edge) { + return new JungraphtConnectionWidget<>(scene, new EdgeShape.QuadCurve(), edge); + } + + public static JungraphtConnectionWidget createCubic(JungraphtScene scene, E edge) { + return new JungraphtConnectionWidget<>(scene, new EdgeShape.CubicCurve(), edge); + } + + public static JungraphtConnectionWidget createOrthogonal(JungraphtScene scene, E edge) { + return new JungraphtConnectionWidget<>(scene, new EdgeShape.Orthogonal(), edge); + } + + public static JungraphtConnectionWidget createLoop(JungraphtScene scene, E edge) { + return new JungraphtConnectionWidget<>(scene, new EdgeShape.Loop(), edge); + } + + public static JungraphtConnectionWidget createBox(JungraphtScene scene, E edge) { + return new JungraphtConnectionWidget<>(scene, new EdgeShape.Box(), edge); + } + + public JungraphtConnectionWidget(JungraphtScene scene, E edge) { + this(scene, new EdgeShape.QuadCurve(), edge); + } + + public JungraphtConnectionWidget(JungraphtScene scene, BiFunction, E, Shape> transformer, E edge) { + super(scene); + this.edge = edge; + this.transformer = transformer; + setForeground(new Color(190, 190, 255)); + setOpaque(false); + } + + public void setFunction(BiFunction, E, Shape> transformer) { + this.transformer = transformer; + } + + @Override + protected boolean isRepaintRequiredForRevalidating() { + return true; + } + + public void setStroke(Stroke stroke) { + assert stroke != null : "Stroke null"; + this.stroke = stroke; + } + + public Stroke getStroke() { + return stroke; + } + + private Graph getGraph() { + JungraphtScene scene = (JungraphtScene) getScene(); + return scene.graph; + } + + @Override + protected Rectangle calculateClientArea() { + Shape shape = getShape().getBounds2D(); + Rectangle result = new Rectangle(stroke.createStrokedShape(shape).getBounds()); + result = convertLocalToScene(result); + return result; + } + + @Override + public boolean isHitAt(Point localLocation) { + Stroke stk = new BasicStroke(5); + Shape shape = stk.createStrokedShape(getShape()); + return shape.contains(convertLocalToScene(localLocation)); + } + + private Shape getShape() { + JungraphtScene scene = (JungraphtScene) getScene(); + + Graph graph = getGraph(); + Shape edgeShape = transformer.apply(graph, edge); + + V source = graph.getEdgeSource(edge); + V target = graph.getEdgeTarget(edge); + LayoutModel layoutModel = scene.model; + + Widget w1 = scene.findWidget(source); + Rectangle r1 = w1.getClientArea(); + Widget w2 = scene.findWidget(target); + Rectangle r2 = w2.getClientArea(); + + org.jungrapht.visualization.layout.model.Point firstLoc = layoutModel.apply(source); + org.jungrapht.visualization.layout.model.Point secondLoc = layoutModel.apply(target); +// if (r1 != null) { +// r1.x = 0; +// r1.y = 0; +// firstLoc = new Point2D.Double(firstLoc.getX() + (double) r1.getCenterX(), +// firstLoc.getY() + (double) r1.getCenterY()); +// } +// if (r2 != null) { +// r2.x = 0; +// r2.y = 0; +// secondLoc = new Point2D.Double(secondLoc.getX() + (double) r2.getCenterX(), +// secondLoc.getY() + (double) r2.getCenterY()); +// } + + float x1 = (float) firstLoc.x; + float y1 = (float) firstLoc.y; + float x2 = (float) secondLoc.x; + float y2 = (float) secondLoc.y; + + AffineTransform xform = AffineTransform.getTranslateInstance(firstLoc.x, firstLoc.y); + + 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); + + if (edgeShape instanceof Path2D) { + xform.scale(dist, dist); + } else { + xform.scale(dist, 1.0); + } + + edgeShape = xform.createTransformedShape(edgeShape); + return edgeShape; + } + + @Override + protected void paintWidget() { + Graphics2D g = getGraphics(); + g.setPaint(getForeground()); + g.setStroke(stroke); + Shape edgeShape = getShape(); + g.draw(edgeShape); + } +} diff --git a/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/JungraphtScene.java b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/JungraphtScene.java new file mode 100644 index 0000000..183c796 --- /dev/null +++ b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/JungraphtScene.java @@ -0,0 +1,913 @@ +/* + * Copyright (c) 2013, Tim Boudreau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.timboudreau.vl.jungrapht; + +import java.awt.Color; +import java.awt.Point; +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.geom.Point2D; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import javax.swing.JComponent; +import javax.swing.Timer; + +import org.jgrapht.Graph; +import org.jgrapht.ListenableGraph; +import org.jgrapht.event.GraphEdgeChangeEvent; +import org.jgrapht.event.GraphListener; +import org.jgrapht.event.GraphVertexChangeEvent; +import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.util.IterativeContext; +import org.jungrapht.visualization.layout.model.LayoutModel; +import org.netbeans.api.visual.action.ActionFactory; +import org.netbeans.api.visual.action.MoveProvider; +import org.netbeans.api.visual.action.WidgetAction; +import org.netbeans.api.visual.graph.GraphScene; +import org.netbeans.api.visual.layout.SceneLayout; +import org.netbeans.api.visual.model.ObjectSceneEvent; +import org.netbeans.api.visual.model.ObjectSceneEventType; +import org.netbeans.api.visual.widget.ConnectionWidget; +import org.netbeans.api.visual.widget.Widget; +import org.openide.util.Lookup; +import org.openide.util.Parameters; +import org.openide.util.lookup.AbstractLookup; +import org.openide.util.lookup.InstanceContent; + +/** + * Base class for Visual Library scenes which use Jungrapht to manage layout and + * connection drawing for graphs. You pass a Graph and a Layout to the + * constructor; if the graph implements ListenableGraph, then the scene will + * automatically update itself when the graph is modified; otherwise call + * sync() when the graph is modified. + *

+ * To be pre-populated, subclasses should call init() at the end of their + * constructor. + * + * @author Tim Boudreau + */ +public abstract class JungraphtScene extends GraphScene { + + protected final Graph graph; + protected LayoutAlgorithm layout; + protected LayoutModel model; + private final LayoutAdapter sceneLayout = new LayoutAdapter(); + private boolean initialized; + private SelectByClickAction clickAction; + private final GraphSelection selection = new GraphSelection<>(this); + private LayoutAnimationEvaluator evaluator = new LayoutAnimationEvaluator(); + private boolean animate = true; + private final ActionListener timerListener = new TimerListener(); + private int fastForwardIterations = 300; + private final Timer timer = new Timer(1000 / 24, timerListener); + private final Lookup lkp; + protected Predicate vertexDisplayPredicate = n -> true; + protected Predicate edgeDisplayPredicate = e -> true; + + + protected JungraphtScene(Graph graph, LayoutAlgorithm layout) { + this(graph, layout, g -> { // this is a WAG to try to make the layout area good for the graph size + int vertexCount = g.vertexSet().size(); + return Math.min(Math.max(600, vertexCount*2), 10000); + }); + super.setBackground(Color.white); + } + + /** + * Create a new Scene backed by the passed graph, and whose initial layout + * is done using the passed layout. + * + * @param graph A JGraphT graph which will be used to provide the graph + * @param layout A jungrapht layout to be used as the initial layout + */ + protected JungraphtScene(Graph graph, LayoutAlgorithm layout, + Function, Integer> initialDimensionFunction) { + this.graph = graph; + this.layout = layout; + int dimension = initialDimensionFunction.apply(graph); + this.model = LayoutModel.builder().graph(graph).size(dimension, dimension) + .createVisRunnable(false).build(); + this.model.accept(this.layout); + timer.setRepeats(true); + timer.setCoalesce(true); + timer.setInitialDelay(200); + timer.stop(); + if (graph instanceof ListenableGraph) { + ((ListenableGraph) graph).addGraphListener(new GraphEventAdapter()); + } + final InstanceContent content = new InstanceContent(); + lkp = new AbstractLookup(content); + // Export the selection as the contents of the lookup, for external + // components to monitor the selection + addObjectSceneListener(new ObjectSceneAdapter() { + @Override + public void selectionChanged(ObjectSceneEvent event, Set previousSelection, Set newSelection) { + // Compute the intersections and remove and add only those + // that have changed + Set toRemove = new HashSet<>(previousSelection); + toRemove.removeAll(newSelection); + for (Object o : toRemove) { + content.remove(o); + } + Set toAdd = new HashSet<>(newSelection); + toAdd.removeAll(previousSelection); + for (Object o : toAdd) { + content.add(o); + } + } + }, ObjectSceneEventType.OBJECT_SELECTION_CHANGED); + } + + public Predicate getVertexDisplayPredicate() { + return vertexDisplayPredicate; + } + + public void setVertexDisplayPredicate(Predicate vertexDisplayPredicate) { + this.vertexDisplayPredicate = vertexDisplayPredicate; + } + + public Predicate getEdgeDisplayPredicate() { + return edgeDisplayPredicate; + } + + public void setEdgeDisplayPredicate(Predicate edgeDisplayPredicate) { + this.edgeDisplayPredicate = edgeDisplayPredicate; + } + + public Widget findNodeWidget(N node) { + return findWidget(node); + } + + public Widget findEdgeWidget(E edge) { + return findWidget(edge); + } + + public N nodeForWidget(Widget w, Class type) { + return objFor(w, type); + } + + public E edgeForWidget(Widget w, Class type) { + return objFor(w, type); + } + + private T objFor(Widget w, Class type) { + Object result = findObject(w); + return type.isInstance(result) ? type.cast(result) : null; + } + + private MultiMoveAction.RelatedWidgetProvider relatedProvider; + + public MultiMoveAction.RelatedWidgetProvider relatedProvider() { + if (relatedProvider == null) { + relatedProvider = new RelatedProvider(); + } + return relatedProvider; + } + + private class RelatedProvider implements MultiMoveAction.RelatedWidgetProvider { + + @Override + public void findWidgets(Widget relatedTo, Collection addTo, int depth) { + N model = (N) findObject(relatedTo); + Set all = new HashSet<>(); + findEdges(model, all, depth); + for (N mdl : all) { + Widget w = findNodeWidget(mdl); + if (w != null) { + addTo.add(w); + } + } + } + + private void findEdges(N model, Set related, int depth) { + Collection edges = findNodeEdges(model, true, false); + Set found = new HashSet<>(); + for (E n : edges) { + found.add(graph.getEdgeTarget(n)); + } + if (depth > 0) { + for (N f : found) { + findEdges(f, related, depth - 1); + } + } + related.addAll(found); + } + }; + + /** + * The lookup contains the currently selected nodes, and you can listen for + * changes on it to get the current set of selected nodes + * + * @return The selection + */ + @Override + public final Lookup getLookup() { + return lkp; + } + + /** + * Gets the jungrapht layout + * + * @return + */ + public final LayoutModel layoutModel() { + return model; + } + + /** + * Gets the jungrapht layoutAlgorithm + * + * @return + */ + public final LayoutAlgorithm layoutAlgorithm() { + return layout; + } + + + /** + * Gets the JGraphT graph + * + * @return + */ + public final Graph graph() { + return graph; + } + + /** + * Gets an object which can answer graph-oriented questions about the + * selection (i.e. is a node connected to the selection by one or more edges + * indirection) + * + * @return + */ + public final GraphSelection getSelection() { + return selection; + } + + /** + * Overridden to inform the layout of the view's size + * + * @return The view + */ + @Override + public JComponent createView() { + boolean was = initialized; + if (!initialized) { + initialized = true; + sync(); + sceneLayout.performLayout(); + } + JComponent view = super.createView(); + if (!was && layout instanceof IterativeContext && animate) { + startAnimation(); + } + return view; + } + + /** + * Re-layout the scene using the jungrapht layout. + */ + public final void performLayout() { + performLayout(false); + } + + /** + * Re-layout the scene, optionally animating the node positions + * + * @param animate Animate the transition of node positions + */ + public final void performLayout(boolean animate) { +// sceneLayout.invokeLayout(); + sceneLayout.performLayout(animate); + } + + /** + * Set the number of pre-iterations to perform on layouts which implement + * IterativeContext, if not animating the layout with a timer. + *

+ * Some jungrapht layouts implement IterativeContext and are expected to be + * re-laid out repeatedly until the system reaches a good state; the initial + * layout is often simply random placement of nodes. + *

+ * In the case that animation is off, we still want to display a good + * looking graph, so calls to setGraphLayout can automatically iterate the + * layout a bunch of steps immediately. Depending on how computationally + * expensive the layout is, and the number of nodes and edges in the graph, + * this could be slow, so we provide this way to limit the number of + * iterations. + * + * @param val The number of iterations; <=0 equals none. + */ + public final void setFastForwardIterations(int val) { + this.fastForwardIterations = val; + } + + /** + * Set the jungrapht layout, triggering a re-layout of the scene + * + * @param layout The layout, may not be null + * @param animate If true, animate the node widgets to their new locations + */ + public final void setGraphLayout(LayoutAlgorithm layout, boolean animate) { + assert layout != null : "Layout null"; + timer.stop(); + this.layout = layout; + this.model.accept(layout); + sceneLayout.performLayout(animate); + if (this.animate && layout instanceof IterativeContext && getView() != null) { + startAnimation(); + } else if (!this.animate && layout instanceof IterativeContext) { + // Fast forward it a bit + IterativeContext ctx = (IterativeContext) layout; + if (!ctx.done() && fastForwardIterations > 0) { + try { + for (int i = 0; i < fastForwardIterations; i++) { + ctx.step(); + if (ctx.done()) { + break; + } + } + } catch (Exception e) { + Logger.getLogger(JungraphtScene.class.getName()).log(Level.INFO, null, e); + } + } + sceneLayout.performLayout(true); + validate(); + repaint(); + } + } + + /** + * Some jungrapht layouts support iteratively evolving toward an optimal layout + * (where precomputing this is too expensive). If true, setting one of these + * layouts will trigger a timer that re-layouts the scene until the layout + * says it has reached an optimal state or this property is set to false. + * + * @return Whether or not to animate + */ + public final boolean isAnimateIterativeLayouts() { + return animate; + } + + /** + * Some jungrapht layouts support iteratively evolving toward an optimal layout + * (where precomputing this is too expensive). If true, setting one of these + * layouts will trigger a timer that re-layouts the scene until the layout + * says it has reached an optimal state or this property is set to false. + * + * @param val to animate or not + */ + public final void setAnimateIterativeLayouts(boolean val) { + boolean old = this.animate; + if (old != val) { + this.animate = val; + if (val && this.layout instanceof IterativeContext && getView() != null) { + startAnimation(); + } else if (!val) { + timer.stop(); + } + } + } + + /** + * Some jungrapht layouts support iteratively evolving toward an optimal layout + * (where precomputing this is too expensive). This property sets the frame + * rate for animating them, in frames per second. + * + * @param fps Frames per second - must be >= 1 + */ + public final void setLayoutAnimationFramesPerSecond(int fps) { + if (fps < 1) { + throw new IllegalArgumentException("Frame rate must be at least 1. " + + "Use setAnimateIterativeLayouts() to disable animation."); + } + timer.setDelay(1000 / fps); + } + + /** + * Some jungrapht layouts support iteratively evolving toward an optimal layout + * (where precomputing this is too expensive). This is the frame rate for + * the animation timer (actual results may vary if the machine cannot keep + * up with the math involved). + * + * @return The requested frame rate + */ + public int getAnimationFramesPerSecond() { + int delay = timer.getDelay(); + return delay / 1000; + } + + /** + * Start the animation timer and reset the evaluator's count of + * insignificant changes. + */ + private void startAnimation() { + evaluator.reset(); + timer.start(); + } + + /** + * Set the jungrapht layout, triggering a re-layout of the scene + * + * @param layout + */ + public final void setGraphLayout(LayoutAlgorithm layout) { + setGraphLayout(layout, false); + } + + /** + * Returns an AutoCloseable which can be used in a try-with-resources loop + * to modify the graph and automatically sync the widgets on-screen with the + * nodes on close. + * + * @return A GraphMutator. + */ + public final GraphMutator modifyGraph() { + return new GraphMutator(this); + } + + /** + * Add a node to this graph. Use this method in place of + * addNode(). If the graph passed to this scene's constructor + * does not implement ObservableGraph, you will need to + * manually call sync() to update the scene from the graph (or + * use GraphMutator in a try-with-resources loop). + * + * @param node A node + */ + public final void addGraphNode(N node) { + graph.addVertex(node); + } + + /** + * Add an edge to this graph, associating it with the nodes it connects. If + * the graph passed to this scene's constructor does not implement + * ObservableGraph, you will need to manually call + * sync() to update the scene from the graph (or use + * GraphMutator in a try-with-resources loop). + * + * @param edge The edge + * @param source The source node + * @param target The target node + */ + public final void addGraphEdge(E edge, N source, N target) { + graph.addEdge(source, target, edge); + } + + public static final class GraphMutator implements AutoCloseable { + + private final JungraphtScene scene; + + GraphMutator(JungraphtScene scene) { + this.scene = scene; + } + + /** + * Add a node to this graph. Use this method in place of + * addNode(). If the graph passed to this scene's + * constructor does not implement ObservableGraph, you will + * need to manually call sync() to update the scene from + * the graph (or use GraphMutator in a try-with-resources + * loop). + * + * @param node A node + */ + public void addGraphNode(N node) { + scene.addGraphNode(node); + } + + /** + * Add an edge to this graph, associating it with the nodes it connects. + * If the graph passed to this scene's constructor does not implement + * ObservableGraph, you will need to manually call + * sync() to update the scene from the graph (or use + * GraphMutator in a try-with-resources loop). + * + * @param edge The edge + * @param source The source node + * @param target The target node + */ + public void addGraphEdge(E edge, N source, N target) { + scene.addGraphEdge(edge, source, target); + } + + /** + * Synchronizes the scene's graph's contents with the nodes the scene + * knows about. + */ + @Override + public void close() { + scene.sync(); + scene.performLayout(true); + } + } + + public void updateVisibility() { + Set currNodes = new HashSet<>(super.getNodes()); + // Get the set the graph knows about, filtered by the Predicate + Collection filteredNodes = graph.vertexSet().stream() + .filter(vertexDisplayPredicate).collect(Collectors.toSet()); + currNodes.forEach(n -> { + findWidget(n).setVisible(filteredNodes.contains(n)); + }); + Set currEdges = new HashSet<>(super.getEdges()); + Set filteredEdges = graph.edgeSet().stream().filter(edgeDisplayPredicate) + .filter(e -> filteredNodes.contains(graph.getEdgeSource(e)) && filteredNodes.contains(graph.getEdgeTarget(e))) + .collect(Collectors.toSet()); + currEdges.forEach(e -> + findEdgeWidget(e).setVisible(filteredEdges.contains(e))); + sync(); + } + /** + * Cause the scene to sync itself with the graph. If the Graph passed to the + * constructor implements ObservableGraph, you will not need to call this; + * otherwise, call this if the graph has been externally modified. + */ + public final void sync() { + // Get the current set of nodes the scene knows about + Set currNodes = new HashSet<>(super.getNodes()); + // Get the set the graph knows about + Collection nodes = graph.vertexSet(); + // Add any not present in the current set + for (N n : nodes) { + if (!currNodes.contains(n)) { + addNode(n); + currNodes.add(n); + } + } + // Remove all of the ones still part of the graph so we are + // left with the set of nodes which are still held by the scene + // but were removed from the graph, and remove them + currNodes.removeAll(nodes); + for (N n : currNodes) { + Widget w = findWidget(n); + w.removeFromParent(); + removeNode(n); + } + // Remove any edges we need to, and add any we don't know about + Set currEdges = new HashSet<>(super.getEdges()); + for (E e : graph.edgeSet()) { + if (!currEdges.contains(e)) { + N src = graph.getEdgeSource(e); + N dest = graph.getEdgeTarget(e); + addEdge(e); + setEdgeSource(e, src); + setEdgeTarget(e, dest); + currEdges.add(e); + } + } + validate(); + } + + /** + * Get a MoveAction for node widgets which will update the graph's layout so + * that connections will be updated correctly. Use this if you are using + * JungraphtConnectionWidget. + * + * @return An action + */ + public final WidgetAction createNodeMoveAction() { + return ActionFactory.createMoveAction(ActionFactory.createFreeMoveStrategy(), + sceneLayout); + } + + protected MoveProvider moveProvider() { + return sceneLayout; + } + + /** + * Set the object which creates Shape objects for edges when using + * JungraphtConnectionWidget. Jungrapht's class EdgeShape contains a number of useful + * implementations. + * + * @param transformer An thing which makes line shapes + */ + @SuppressWarnings(value = "unchecked") + public void setConnectionEdgeShape(BiFunction, E, Shape> transformer) { + Set parents = new HashSet<>(); + for (E edge : getEdges()) { + Widget w = findWidget(edge); + if (w instanceof JungraphtConnectionWidget) { + parents.add(w.getParentWidget()); + ((JungraphtConnectionWidget) w).setFunction(transformer); + w.revalidate(); + } + } + if (!parents.isEmpty()) { + for (Widget connectionLayer : parents) { //typically there is only one + connectionLayer.revalidate(); + connectionLayer.repaint(); + } + } + repaint(); + } + + /** + * Create a move provider which will update the jungrapht layout as needed, so + * that user-dragged locations are not discarded by the layout, and dragging + * while animating works as expected. + * + * @return A move provider + */ + protected final MoveProvider createMoveProvider() { + return sceneLayout; + } + + /** + * Get the SceneLayout implementation which wrappers the jungrapht layout and + * lays out the widgets + * + * @return A SceneLayout + */ + public final SceneLayout getSceneLayout() { + return sceneLayout; + } + + /** + * Set the object which will decide when animated jungrapht layouts have done + * everything useful they're going to do. + * + * @param eval An evaluator + * @see LayoutAnimationEvaluator + */ + public void setLayoutAnimationEvaluator(LayoutAnimationEvaluator eval) { + Parameters.notNull("eval", eval); + this.evaluator = eval; + } + + /** + * Get the object which decides when any animated jungrapht layout has done + * everything useful it is going to do + * + * @return An evaluator + * @see LayoutAnimationEvaluator + */ + public LayoutAnimationEvaluator getLayoutAnimationEvaluator() { + return evaluator; + } + + /** + * Adapter which implements SceneLayout and uses the layout logic of the + * jungrapht layout. Also acts as our MoveProvider which will tell the layout + * about user-made changes to node positions, so these are remembered + */ + private class LayoutAdapter extends SceneLayout implements MoveProvider { + + LayoutAdapter() { + super(JungraphtScene.this); + } + + private Point toPoint(Point2D p) { + // A little pointless conversion code to get a java.awt.Point from a + // Point2D + Point result; + if (p instanceof Point) { + result = (Point) p; + } else { + result = new Point((int) p.getX(), (int) p.getY()); + } + return result; + } + + @Override + protected void performLayout() { + performLayout(false); + } + + private double minDist = Double.MAX_VALUE; + private double maxDist = Double.MIN_VALUE; + private double avgDist = 0D; + + protected void performLayout(boolean animate) { + // Make sure the layout knows about the size of the view + + model.setSize(model.getPreferredWidth(), model.getPreferredHeight()); + + minDist = Double.MAX_VALUE; + maxDist = Double.MIN_VALUE; + avgDist = 0D; + + boolean animating = timer.isRunning(); + + // Iterate the vertices and make sure the widgets locations + // match the graph + Collection nodes = graph.vertexSet(); + for (N n : nodes) { + Widget widget = findWidget(n); + Point2D oldLocation = widget.getPreferredLocation(); + org.jungrapht.visualization.layout.model.Point p = model.apply(n); + Point2D newLocation = new Point2D.Double(p.x, p.y); + + if (oldLocation != null && animating) { + double length = Math.abs(oldLocation.distance(newLocation)); + minDist = Math.min(minDist, length); + maxDist = Math.max(maxDist, length); + avgDist += length; + } + + Point p1 = toPoint(newLocation); + if (animate) { + getSceneAnimator().animatePreferredLocation(widget, p1); + } else { + widget.setPreferredLocation(p1); + } + } + // Avoid div by zero + avgDist /= nodes.isEmpty() ? 0D : (double) nodes.size(); + for (E e : graph.edgeSet()) { + Widget w = (Widget) findWidget(e); + if (w instanceof ConnectionWidget) { + ((ConnectionWidget) w).reroute(); + } else { + w.revalidate(); + } + } + JungraphtScene.this.validate(); + if (animating && evaluator.animationIsFinished(minDist, maxDist, avgDist, layout)) { + timer.stop(); + } + } + + private MoveProvider delegate = ActionFactory.createDefaultMoveProvider(); + + @Override + public void movementStarted(Widget widget) { + delegate.movementStarted(widget); + } + + @Override + public void movementFinished(Widget widget) { + delegate.movementFinished(widget); + onMove((N) findObject(widget), widget); + } + + @Override + public Point getOriginalLocation(Widget widget) { + return delegate.getOriginalLocation(widget); + } + + @Override + public void setNewLocation(Widget widget, Point location) { + org.jungrapht.visualization.layout.model.Point p = + org.jungrapht.visualization.layout.model.Point.of(location.x, location.y); + N node = null; + for (N n : getNodes()) { + if (findWidget(n) == widget) { + node = n; + break; + } + } + if (node != null) { + model.set(node, p); + for (E e : graph.outgoingEdgesOf(node)) { + Widget w = findWidget(e); + w.revalidate(); + } + for (E e : graph.incomingEdgesOf(node)) { + Widget w = findWidget(e); + w.revalidate(); + + } + onMove(node, widget); + } + delegate.setNewLocation(widget, location); + } + } + + /** + * Create an action for selecting a widget by clicking it. To disable that + * behavior, override and return a do-nothing action. + * + * @return An action + */ + public WidgetAction createSelectByClickAction() { + if (clickAction == null) { + clickAction = new SelectByClickAction(); + } + return clickAction; + } + + private class SelectByClickAction extends WidgetAction.Adapter { + + @Override + public WidgetAction.State mouseClicked(Widget widget, WidgetAction.WidgetMouseEvent event) { + Object o = findObject(widget); + if (o != null && getNodes().contains(o)) { + if (event.isShiftDown()) { + Set sel = new HashSet<>(getSelectedObjects()); + if (sel.contains(o)) { + sel.remove(o); + } else { + sel.add(o); + } + setSelectedObjects(sel); + } else { + setSelectedObjects(new HashSet<>(Arrays.asList(o))); + } + return WidgetAction.State.CONSUMED; + } + return WidgetAction.State.REJECTED; + } + } + + /** + * Subclasses should override this to revalidate the connection layer if + * using JungraphtConnectionWidget + */ + protected void onMove(N n, Widget widget) { + } + + /** + * If the graph is an ObservableGraph, listens for changes in it and + * adds/removes nodes appropriately + */ + private class GraphEventAdapter implements GraphListener { + + @Override + public void vertexAdded(GraphVertexChangeEvent e) { + addNode(e.getVertex()); + validate(); + } + @Override + public void vertexRemoved(GraphVertexChangeEvent e) { + removeNode(e.getVertex()); + validate(); + } + @Override + public void edgeAdded(GraphEdgeChangeEvent e) { + E edge = e.getEdge(); + N source = graph.getEdgeSource(edge); + N target = graph.getEdgeTarget(edge); + addEdge(edge); + setEdgeSource(edge, source); + setEdgeTarget(edge, target); + validate(); + } + @Override + public void edgeRemoved(GraphEdgeChangeEvent e) { + removeEdge(e.getEdge()); + validate(); + } + } + + private class TimerListener implements ActionListener { + + + @Override + public void actionPerformed(ActionEvent e) { + if (!animate) { + return; + } + if (layout instanceof IterativeContext) { + IterativeContext c = (IterativeContext) layout; + try { + c.step(); + } catch (Exception ex) { + // e.g. IllegalArgumentException: Unexpected mathematical result in FRLayout:calcPositions + // Some layouts are buggy. +// Logger.getLogger(JungraphtScene.class.getName()).log(Level.INFO, null, ex); + ex.printStackTrace(); +// timer.stop(); + } + if (c.done()) { + timer.stop(); + } + getSceneLayout().invokeLayout(); + validate(); + repaint(); + } + } + } +} diff --git a/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/LayoutAnimationEvaluator.java b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/LayoutAnimationEvaluator.java new file mode 100644 index 0000000..bef691b --- /dev/null +++ b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/LayoutAnimationEvaluator.java @@ -0,0 +1,169 @@ +package com.timboudreau.vl.jungrapht; + + +import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm; + +/** + * Many jungrapht layouts never quite settle into a stable state, but leave + * the nodes permanently jiggling by a few pixels. Animated jungrapht layouts implement + * an interface IterativeContext, which has a done() + * method which is supposed to provide this value, but many implementations never + * return true from it; and in other cases, reaching a stable state may take a + * very long time, while the node positions are fairly good fairly quickly. + * An endlessly jiggling graph is interesting looking but not necessarily great + * to work with or read. + *

+ * This class exists to augment the layouts evaluation of "done" with our own, + * based on what we think the user needs. + *

+ * This class evaluates the minimum and maximum distances one frame of animation + * has caused nodes to move, and is used to turn off the animation timer once + * the movements are minimal enough. Subclasses would override the test() + * method to return true if no node has moved by a distance considered + * significant - i.e. if no node has moved more than 10 pixels for 10 iterations, + * the default implementation will consider it done. + *

+ * This class is only used if the associated scene's animation timer is running. + * + * @author Tim Boudreau + */ +public class LayoutAnimationEvaluator { + + private int callCount; + /** + * The default number of iterations that no significant change has to happen + * for the animation to be considered done. + */ + public static final int DEFAULT_MINIMUM_ITERATIONS_STABLE_STATE = 480; + /** + * The default number of pixels any node needs to have moved for the + * default implementation to consider the animation as still doing + * productive things. + */ + public static final double DEfAULT_MAX_DISTANCE = 9D; + + private int minIterations = DEFAULT_MINIMUM_ITERATIONS_STABLE_STATE; + private double distance = DEfAULT_MAX_DISTANCE; + + /** + * An instance which lets the animation run forever. + */ + public static LayoutAnimationEvaluator NO_OP = new LayoutAnimationEvaluator() { + @Override + protected boolean animationIsFinished(double min, double max, double average, LayoutAlgorithm layoutAlgorithm) { + return false; + } + }; + + /** + * Get the number of consecutive times that test() must return true + * for the animation to be considered done. + * + * @return the number of iterations + */ + public int getMinimumIterations() { + return minIterations; + } + + /** + * Set the number of consecutive times that test() must return + * true for the animation to be considered done. + * + * @param iterations The number of iterations + */ + public final void setMinimumIterations(int iterations) { + if (iterations <= 0) { + throw new IllegalArgumentException("Iterations must be at least one, not " + iterations); + } + this.minIterations = iterations; + } + + /** + * Get the maximum distance between points for which the default implementation + * of test() should return true. If the max parameter passed to + * test() is less than this number minimumIterations + * consecutive times, the animationIsFinished() will return true. + * + * @return the distance + */ + public final double getDistanceConsideredStable() { + return distance; + } + + /** + * Get the maximum distance between points for which the default implementation + * of test() should return true. If the max parameter passed to + * test() is less than this number minimumIterations + * consecutive times, the animationIsFinished() will return true. + * + * @param distance The distance, non-negative + */ + public final void setDistanceConsideredStable(double distance) { + if (distance < 0D) { + throw new IllegalArgumentException("Negative values not allowed: " + distance); + } + this.distance = distance; + } + + /** + * Reset the counter that determines how many times this has been called + * with insignificant changes. + */ + protected void reset() { + callCount = 0; + } + + /** + * Perform the test which decides. The default implementation is + * return max <= getDistance(). + * + * @param min The minimum distance any point moved + * @param max The maximum distance any point moved + * @param average The average distance points moved + * @param layoutAlgorithm The jungrapht layoutAlgorithm - may want to special case layouts with + * different characteristics + * @return True if no significant moves have occurred + */ + protected boolean test (double min, double max, double average, LayoutAlgorithm layoutAlgorithm) { + return max <= getDistanceConsideredStable(); + } + + /** + * Get the number of consecutive times animationIsFinished() + * and test() returned true since the last call to reset(). + * + * @return The number of times + */ + protected int getConsecutiveCallCount() { + return callCount; + } + + /** + * Test if the animation has finished running, using the standard that + * a call to test() must have returned true more than some + * threshold number of previous calls. + * + * @param min The minimum distance any point moved + * @param max The maximum distance any point moved + * @param average The average distance points moved + * @param layoutAlgorithm The layout being used. Some jungrapht layouts never settle but + * repeatedly produce large moves; this parameter can be used to evaluate if + * this is such a case + * @return True if no significant moves have occurred + */ + protected boolean animationIsFinished(double min, double max, double average, LayoutAlgorithm layoutAlgorithm) { + if (callCount++ < minIterations) { + return false; + } +// System.out.println(min + " <-> " + max + " :: " + average); + boolean result = test(min, max, average, layoutAlgorithm); + if (!result) { + reset(); + } else { + if (callCount++ < minIterations) { + result = false; + } + } + return result; + } +} diff --git a/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/MultiMoveAction.java b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/MultiMoveAction.java new file mode 100644 index 0000000..f25360d --- /dev/null +++ b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/MultiMoveAction.java @@ -0,0 +1,162 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.timboudreau.vl.jungrapht; + +import java.awt.Point; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import org.netbeans.api.visual.action.ActionFactory; +import org.netbeans.api.visual.action.MoveProvider; +import org.netbeans.api.visual.action.MoveStrategy; +import org.netbeans.api.visual.action.WidgetAction; +import org.netbeans.api.visual.widget.Widget; + +/** + * + * @author Tim Boudreau + */ +public final class MultiMoveAction extends WidgetAction.LockedAdapter { + + private final MoveStrategy strategy; + private final MoveProvider provider; + private final RelatedWidgetProvider related; + private MoveData moveData; + + public MultiMoveAction(RelatedWidgetProvider related, MoveProvider provider) { + this.strategy = ActionFactory.createFreeMoveStrategy(); + this.provider = provider; + this.related = related; + } + + public interface RelatedWidgetProvider { + + public void findWidgets(Widget relatedTo, Collection addTo, int additionalDepth); + } + + protected boolean isLocked() { + return moveData != null; + } + + static final class MoveData implements Iterable { + + final Widget movingWidget; + final Point dragSceneLocation; + final Point originalSceneLocation; + final Point initialMouseLocation; + final List md = new ArrayList<>(20); + + public MoveData(Widget widget, WidgetAction.WidgetMouseEvent event, MoveProvider provider) { + movingWidget = widget; + initialMouseLocation = event.getPoint(); + Point originalSceneLocation = provider.getOriginalLocation(widget); + if (originalSceneLocation == null) { + originalSceneLocation = new Point(); + } + this.originalSceneLocation = originalSceneLocation; + dragSceneLocation = widget.convertLocalToScene(event.getPoint()); + md.add(this); + } + + boolean contains(Widget widget) { + if (widget == movingWidget) { + return true; + } + for (MoveData m : this) { + if (widget == m.movingWidget) { + return true; + } + } + return false; + } + + public MoveData add(Widget widget, WidgetAction.WidgetMouseEvent event, MoveProvider provider) { + md.add(new MoveData(widget, event, provider)); + return this; + } + + @Override + public Iterator iterator() { + return md.iterator(); + } + } + + public WidgetAction.State mousePressed(Widget widget, WidgetAction.WidgetMouseEvent event) { + if (isLocked()) { + return WidgetAction.State.createLocked(widget, this); + } + if (event.getButton() == MouseEvent.BUTTON1 && event.getClickCount() == 1 && (event.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) == KeyEvent.SHIFT_DOWN_MASK) { + int depth = 0; + if ((event.getModifiersEx() & KeyEvent.ALT_DOWN_MASK) == KeyEvent.ALT_DOWN_MASK) { + depth++; + } + if (depth == 1 && (event.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) == KeyEvent.CTRL_DOWN_MASK) { + depth++; + } + if (depth == 2 && (event.getModifiersEx() & KeyEvent.META_DOWN_MASK) == KeyEvent.META_DOWN_MASK) { + depth++; + } + moveData = new MoveData(widget, event, provider); + Set others = new HashSet<>(); + related.findWidgets(widget, others, depth); + provider.movementStarted(widget); + for (Widget w : others) { + moveData.add(w, event, provider); + provider.movementStarted(w); + } + return WidgetAction.State.createLocked(widget, this); + } + return WidgetAction.State.REJECTED; + } + + public WidgetAction.State mouseReleased(Widget widget, WidgetAction.WidgetMouseEvent event) { + boolean state; + if (moveData.initialMouseLocation != null && moveData.initialMouseLocation.equals(event.getPoint())) { + state = true; + } else { + state = move(widget, event.getPoint()); + } + if (state) { + MoveData data = moveData; + moveData = null; + for (MoveData other : data) { + provider.movementFinished(other.movingWidget); + } + } + return state ? WidgetAction.State.CONSUMED : WidgetAction.State.REJECTED; + } + + @Override + public WidgetAction.State mouseDragged(Widget widget, WidgetAction.WidgetMouseEvent event) { + Point point = event.getPoint(); + boolean moved = move(widget, point); + if (moved) { + for (MoveData md : moveData) { + move(md.movingWidget, point); + } + } + return moved ? WidgetAction.State.createLocked(widget, this) : WidgetAction.State.REJECTED; + } + + private boolean move(Widget widget, Point newLocation) { + if (moveData == null || (moveData != null && !moveData.contains(widget))) { + System.out.println("No move for " + widget); + return false; + } + newLocation = widget.convertLocalToScene(newLocation); + for (MoveData md : moveData) { + Point location = new Point(md.originalSceneLocation.x + newLocation.x - md.dragSceneLocation.x, md.originalSceneLocation.y + newLocation.y - md.dragSceneLocation.y); + provider.setNewLocation(widget, strategy.locationSuggested(widget, md.originalSceneLocation, location)); + } + return true; + } + +} diff --git a/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/ObjectSceneAdapter.java b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/ObjectSceneAdapter.java new file mode 100644 index 0000000..a28bcef --- /dev/null +++ b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/ObjectSceneAdapter.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013, Tim Boudreau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.timboudreau.vl.jungrapht; + +import java.util.Set; +import org.netbeans.api.visual.model.ObjectSceneEvent; +import org.netbeans.api.visual.model.ObjectSceneListener; +import org.netbeans.api.visual.model.ObjectState; + +/** + * Missing from VL's API; saves on verbosity + * + * @author Tim Boudreau + */ +public abstract class ObjectSceneAdapter implements ObjectSceneListener { + + @Override + public void objectAdded(ObjectSceneEvent event, Object addedObject) { + } + + @Override + public void objectRemoved(ObjectSceneEvent event, Object removedObject) { + } + + @Override + public void objectStateChanged(ObjectSceneEvent event, Object changedObject, ObjectState previousState, ObjectState newState) { + } + + @Override + public void selectionChanged(ObjectSceneEvent event, Set previousSelection, Set newSelection) { + } + + @Override + public void highlightingChanged(ObjectSceneEvent event, Set previousHighlighting, Set newHighlighting) { + } + + @Override + public void hoverChanged(ObjectSceneEvent event, Object previousHoveredObject, Object newHoveredObject) { + } + + @Override + public void focusChanged(ObjectSceneEvent event, Object previousFocusedObject, Object newFocusedObject) { + } +} diff --git a/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/RingsWidget.java b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/RingsWidget.java new file mode 100644 index 0000000..8332da0 --- /dev/null +++ b/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/RingsWidget.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2013, Tim Boudreau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.timboudreau.vl.jungrapht; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; + +import org.jungrapht.visualization.layout.algorithms.BalloonLayoutAlgorithm; +import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm; +import org.jungrapht.visualization.layout.model.LayoutModel; +import org.jungrapht.visualization.layout.model.Point; +import org.netbeans.api.visual.widget.Widget; + +/** + * LayerWidget which displays rings, for use with BalloonLayout + * + * @author Tim Boudreau + */ +public final class RingsWidget extends Widget { + private Stroke stroke = new BasicStroke(1); + + public RingsWidget(JungraphtScene scene) { + super(scene); + setForeground(new Color(220, 220, 80)); + } + + public void setStroke(Stroke stroke) { + assert stroke != null : "Stroke null"; + if (!this.stroke.equals(stroke)) { + this.stroke = stroke; + revalidate(); + } + } + + @Override + public void paintWidget() { + JungraphtScene scene = (JungraphtScene) getScene(); + LayoutAlgorithm l = scene.layout; + LayoutModel layoutModel = scene.model; + if (l instanceof BalloonLayoutAlgorithm) { + BalloonLayoutAlgorithm layoutAlgorithm = (BalloonLayoutAlgorithm) l; + + Graphics2D g2d = getGraphics(); + g2d.setColor(getForeground()); + + Ellipse2D ellipse = new Ellipse2D.Double(); + for (N v : layoutModel.getGraph().vertexSet()) { + Double radius = layoutAlgorithm.getRadii().get(v); + if (radius == null) { + continue; + } + Point p = layoutModel.apply(v); + ellipse.setFrame(-radius, -radius, 2 * radius, 2 * radius); + AffineTransform at = AffineTransform.getTranslateInstance(p.x, p.y); + // Transform it to the center of the widget +// Widget w = scene.findWidget(v); +// if (w != null) { +// Rectangle r = w.getClientArea(); +// if (r != null) { +// at.concatenate(AffineTransform.getTranslateInstance(r.width / 2, r.height / 2)); +// } +// } + + Shape shape = at.createTransformedShape(ellipse); + g2d.draw(shape); + } + } + } + + @Override + protected Rectangle calculateClientArea() { + Rectangle result = new Rectangle(); + JungraphtScene scene = (JungraphtScene) getScene(); + LayoutAlgorithm l = scene.layout; + LayoutModel layoutModel = scene.model; + if (l instanceof BalloonLayoutAlgorithm) { + BalloonLayoutAlgorithm layout = (BalloonLayoutAlgorithm) l; + Ellipse2D ellipse = new Ellipse2D.Double(); + for (N v : layoutModel.getGraph().vertexSet()) { + Double radius = layout.getRadii().get(v); + if (radius == null) { + continue; + } + Point p = layoutModel.apply(v); + ellipse.setFrame(-radius, -radius, 2 * radius, 2 * radius); + AffineTransform at = AffineTransform.getTranslateInstance(p.x, p.y); + + // Transform it to the center of the widget +// Widget w = scene.findWidget(v); +// if (w != null) { +// Rectangle r = w.getClientArea(); +// if (r != null) { +// at.concatenate(AffineTransform.getTranslateInstance(r.width / 2, r.height / 2)); +// } +// } + Shape shape = at.createTransformedShape(ellipse); + shape = stroke.createStrokedShape(shape); + result.add(shape.getBounds()); + } +// result = convertLocalToScene(result); + } + return result; + } +} diff --git a/vl-jungrapht/src/main/resources/com/timboudreau/vl/demo/icon.png b/vl-jungrapht/src/main/resources/com/timboudreau/vl/demo/icon.png new file mode 100644 index 0000000..ef3e2b3 Binary files /dev/null and b/vl-jungrapht/src/main/resources/com/timboudreau/vl/demo/icon.png differ diff --git a/vl-jungrapht/src/test/java/com/timboudreau/vl/demo/AppTest.java b/vl-jungrapht/src/test/java/com/timboudreau/vl/demo/AppTest.java new file mode 100644 index 0000000..4de39d7 --- /dev/null +++ b/vl-jungrapht/src/test/java/com/timboudreau/vl/demo/AppTest.java @@ -0,0 +1,38 @@ +package com.timboudreau.vl.demo; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/vl-jungrapht/target/classes/com/timboudreau/vl/demo/icon.png b/vl-jungrapht/target/classes/com/timboudreau/vl/demo/icon.png new file mode 100644 index 0000000..ef3e2b3 Binary files /dev/null and b/vl-jungrapht/target/classes/com/timboudreau/vl/demo/icon.png differ diff --git a/vl-jungrapht/target/maven-archiver/pom.properties b/vl-jungrapht/target/maven-archiver/pom.properties new file mode 100644 index 0000000..3ffc99e --- /dev/null +++ b/vl-jungrapht/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=vl-jungrapht +groupId=com.mastfrog +version=2.3 diff --git a/vl-jungrapht/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/vl-jungrapht/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..add5853 --- /dev/null +++ b/vl-jungrapht/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,18 @@ +com/timboudreau/vl/jungrapht/JungraphtConnectionWidget.class +com/timboudreau/vl/jungrapht/JungraphtScene$GraphEventAdapter.class +com/timboudreau/vl/jungrapht/JungraphtScene$1.class +com/timboudreau/vl/jungrapht/JungraphtScene$RelatedProvider.class +com/timboudreau/vl/jungrapht/JungraphtScene.class +com/timboudreau/vl/jungrapht/JungraphtScene$TimerListener.class +com/timboudreau/vl/jungrapht/MultiMoveAction.class +com/timboudreau/vl/jungrapht/GraphSelection$EdgeTypes.class +com/timboudreau/vl/jungrapht/LayoutAnimationEvaluator.class +com/timboudreau/vl/jungrapht/JungraphtScene$GraphMutator.class +com/timboudreau/vl/jungrapht/LayoutAnimationEvaluator$1.class +com/timboudreau/vl/jungrapht/RingsWidget.class +com/timboudreau/vl/jungrapht/ObjectSceneAdapter.class +com/timboudreau/vl/jungrapht/JungraphtScene$LayoutAdapter.class +com/timboudreau/vl/jungrapht/MultiMoveAction$RelatedWidgetProvider.class +com/timboudreau/vl/jungrapht/GraphSelection.class +com/timboudreau/vl/jungrapht/MultiMoveAction$MoveData.class +com/timboudreau/vl/jungrapht/JungraphtScene$SelectByClickAction.class diff --git a/vl-jungrapht/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/vl-jungrapht/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..bef20ca --- /dev/null +++ b/vl-jungrapht/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,7 @@ +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/JungraphtConnectionWidget.java +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/MultiMoveAction.java +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/LayoutAnimationEvaluator.java +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/RingsWidget.java +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/JungraphtScene.java +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/GraphSelection.java +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht/src/main/java/com/timboudreau/vl/jungrapht/ObjectSceneAdapter.java diff --git a/vl-jungrapht/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst b/vl-jungrapht/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst new file mode 100644 index 0000000..e6cd681 --- /dev/null +++ b/vl-jungrapht/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst @@ -0,0 +1 @@ +com/timboudreau/vl/demo/AppTest.class diff --git a/vl-jungrapht/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst b/vl-jungrapht/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst new file mode 100644 index 0000000..a027b17 --- /dev/null +++ b/vl-jungrapht/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst @@ -0,0 +1 @@ +/home/tom/projects/forkvl-jung/vl-jung/vl-jungrapht/src/test/java/com/timboudreau/vl/demo/AppTest.java diff --git a/vl-jungrapht/target/surefire-reports/TEST-com.timboudreau.vl.demo.AppTest.xml b/vl-jungrapht/target/surefire-reports/TEST-com.timboudreau.vl.demo.AppTest.xml new file mode 100644 index 0000000..71a8306 --- /dev/null +++ b/vl-jungrapht/target/surefire-reports/TEST-com.timboudreau.vl.demo.AppTest.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vl-jungrapht/target/surefire-reports/com.timboudreau.vl.demo.AppTest.txt b/vl-jungrapht/target/surefire-reports/com.timboudreau.vl.demo.AppTest.txt new file mode 100644 index 0000000..dd84cf1 --- /dev/null +++ b/vl-jungrapht/target/surefire-reports/com.timboudreau.vl.demo.AppTest.txt @@ -0,0 +1,4 @@ +------------------------------------------------------------------------------- +Test set: com.timboudreau.vl.demo.AppTest +------------------------------------------------------------------------------- +Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.031 sec