Commit 124c3abf authored by Nico Mack's avatar Nico Mack

Initial check-in of experimental force directed graph functionality

parent 5a556945
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. All rights reserved.
*
* This file is part of TULIP.
*
* TULIP is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, version 3 of the
* License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.utility.graph;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import java.util.Objects;
/**
* @author mack
* @since 2.5
* @version 2.5.0
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public class Edge implements Comparable<Edge> {
private Integer id;
private Node source;
private Node target;
private double length;
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Creates a new node instance
*
* @param id
* @param source
* @param target
* @param data
*/
// ---------------------------------------------------------------------------
public Edge(Integer id, Node source, Node target) {
this.id = id;
this.source = source;
this.target = target;
this.length = 1.0;
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Simple getter method for id.
*
* @return The value of id.
*/
// ---------------------------------------------------------------------------
public Integer getId() {
return id;
}
// ---------------------------------------------------------------------------
/**
* Simple setter method for id.
*
* @param id
* The value to set id to.
*/
// ---------------------------------------------------------------------------
public void setId(Integer id) {
this.id = id;
}
// ---------------------------------------------------------------------------
/**
* Simple getter method for source id.
*
* @return The value of source id.
*/
// ---------------------------------------------------------------------------
public Node getSource() {
return this.source;
}
// ---------------------------------------------------------------------------
/**
* Simple getter method for target id.
*
* @return The value of target id.
*/
// ---------------------------------------------------------------------------
public Node getTarget() {
return this.target;
}
// ---------------------------------------------------------------------------
/**
* Simple getter method for length.
*
* @return The value of length.
*/
// ---------------------------------------------------------------------------
public double getLength() {
return length;
}
// ---------------------------------------------------------------------------
/**
* Simple setter method for length.
*
* @param length
* The value to set length to.
*/
// ---------------------------------------------------------------------------
public void setLength(double length) {
this.length = length;
}
// ---------------------------------------------------------------------------
/** {@inheritDoc} */
// ---------------------------------------------------------------------------
@Override
public int compareTo(Edge edge) {
if (edge == null)
throw new NullPointerException();
return Integer.compare(id, edge.id);
}
// ---------------------------------------------------------------------------
@Override
public boolean equals(@Nullable Object object) {
if (object == null)
return false;
if (object == this)
return true;
if (object instanceof Edge) {
Edge edge = (Edge) object;
return id == edge.id;
}
return false;
}
// ---------------------------------------------------------------------------
@Override
public int hashCode() {
return Objects.hashCode(id);
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * End of Class
// ***************************************************************************
// ---------------------------------------------------------------------------
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. All rights reserved.
*
* This file is part of TULIP.
*
* TULIP is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, version 3 of the
* License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.utility.graph;
import com.jgoodies.common.base.Preconditions;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* @author mack
* @since 2.5
* @version 2.5.0
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public class Graph {
private Map<Integer, Node> nodesLookup;
private Map<Integer, Map<Integer, List<Edge>>> neighbours;
private List<Node> nodes;
private List<Edge> edges;
private int nextNodeId;
private int nextEdgeId;
private List<GraphListener> listeners;
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Creates a new Graph instance
*/
// ---------------------------------------------------------------------------
public Graph() {
this.nodesLookup = new HashMap<>();
this.neighbours = new HashMap<>();
this.nodes = new ArrayList<>();
this.edges = new ArrayList<>();
this.nextNodeId = 0;
this.nextEdgeId = 0;
this.listeners = new ArrayList<>();
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitives
// ***************************************************************************
// ---------------------------------------------------------------------------
private void notifyListeners() {
for (GraphListener listener : listeners) {
listener.graphChanged();
}
}
// ---------------------------------------------------------------------------
private void detachNode(Node nodeToDetach) {
List<Edge> edgesToRemove = new ArrayList<>();
for (Edge edge : edges) {
if (edge.getSource().equals(nodeToDetach)
|| edge.getTarget().equals(nodeToDetach)) {
edgesToRemove.add(edge);
}
}
this.edges.removeAll(edgesToRemove);
this.notifyListeners();
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* @param listener
*/
// ---------------------------------------------------------------------------
public void addGraphListener(GraphListener listener) {
this.listeners.add(listener);
}
// ---------------------------------------------------------------------------
/**
* Adds the specified node to this graph and notifies registered listeners.
*
* @param nodeToAdd
* specifies the node to add.
* @throws IllegalArgumentException
* if specified not is <code>NULL</code>.
*/
// ---------------------------------------------------------------------------
public void addNode(Node nodeToAdd) {
Preconditions.checkArgument(nodeToAdd != null, "Node to add MUST not be null!"); //$NON-NLS-1$
assert nodeToAdd != null;
if (!this.nodesLookup.containsKey(nodeToAdd.getId())) {
this.nodes.add(nodeToAdd);
}
this.nodesLookup.put(nodeToAdd.getId(), nodeToAdd);
this.notifyListeners();
}
// ---------------------------------------------------------------------------
/**
* @param nodesToAdd
*/
// ---------------------------------------------------------------------------
public void addNodes(Node... nodesToAdd) {
for (Node node : nodesToAdd) {
this.addNode(node);
}
}
// ---------------------------------------------------------------------------
/**
* @param nodeToRemove
*/
// ---------------------------------------------------------------------------
public void removeNode(Node nodeToRemove) {
Preconditions.checkArgument(nodeToRemove != null, "Node to remove MUST not be null!"); //$NON-NLS-1$
assert nodeToRemove != null;
this.nodesLookup.remove(nodeToRemove);
this.nodes.remove(nodeToRemove);
this.detachNode(nodeToRemove);
}
// ---------------------------------------------------------------------------
/**
* @param edgeToAdd
*/
// ---------------------------------------------------------------------------
public void addEdge(Edge edgeToAdd) {
Preconditions.checkArgument(edgeToAdd != null, "Edge to add MUST not be null!"); //$NON-NLS-1$
assert edgeToAdd != null;
boolean alreadyKnown = false;
for (Edge edge : this.edges) {
alreadyKnown |= (edgeToAdd.getId() == edge.getId());
}
if (!alreadyKnown) {
this.edges.add(edgeToAdd);
}
if (!this.neighbours.containsKey(edgeToAdd.getSource().getId())) {
Map<Integer, List<Edge>> entry = new HashMap<>();
entry.put(edgeToAdd.getTarget().getId(), new ArrayList<>());
this.neighbours.put(edgeToAdd.getSource().getId(), entry);
}
alreadyKnown = false;
for (Edge edge : this.neighbours.get(edgeToAdd.getSource().getId()).get(edgeToAdd.getTarget().getId())) {
alreadyKnown |= (edgeToAdd.getId() == edge.getId());
}
if (!alreadyKnown) {
this.neighbours.get(edgeToAdd.getSource().getId()).get(edgeToAdd.getTarget().getId()).add(edgeToAdd);
}
this.notifyListeners();
}
// ---------------------------------------------------------------------------
/**
* @param edgeToRemove
*/
// ---------------------------------------------------------------------------
public void removeEdge(Edge edgeToRemove) {
Preconditions.checkArgument(edgeToRemove != null, "Edge to be removed MUST not be null!"); //$NON-NLS-1$
this.edges.remove(edgeToRemove);
Map<Integer, Map<Integer, List<Edge>>> emptyOnes = new HashMap<>();
for (Entry<Integer, Map<Integer, List<Edge>>> source : this.neighbours.entrySet()) {
for (Entry<Integer, List<Edge>> target : source.getValue().entrySet()) {
target.getValue().remove(edgeToRemove);
if (target.getValue().isEmpty()) {
emptyOnes.put(source.getKey(), source.getValue());
}
}
}
for (Entry<Integer, Map<Integer, List<Edge>>> source : emptyOnes.entrySet()) {
this.neighbours.remove(source.getKey(), source.getValue());
}
this.notifyListeners();
}
// ---------------------------------------------------------------------------
/**
* @param source
* @param target
* @return
*/
// ---------------------------------------------------------------------------
public List<Edge> getEdges(Node source, Node target) {
if (this.neighbours.containsKey(source.getId())
&& (this.neighbours.get(source.getId()).containsKey(target.getId()))) {
return this.neighbours.get(source.getId()).get(target.getId());
}
return new ArrayList<>();
}
// ---------------------------------------------------------------------------
/**
* @return
*/
// ---------------------------------------------------------------------------
public List<Node> getNodes() {
return this.nodes;
}
// ---------------------------------------------------------------------------
/**
* @return
*/
// ---------------------------------------------------------------------------
public List<Edge> getEdges() {
return this.edges;
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * End of Class
// ***************************************************************************
// ---------------------------------------------------------------------------
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. All rights reserved.
*
* This file is part of TULIP.
*
* TULIP is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, version 3 of the
* License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.utility.graph;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
public interface GraphListener {
/**
*
*/
public void graphChanged();
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. All rights reserved.
*
* This file is part of TULIP.
*
* TULIP is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, version 3 of the
* License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.utility.graph;
import com.jgoodies.common.base.Preconditions;
/**
* @author mack
* @since 2.5
* @version 2.5.0
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public class Node {
private Integer id;
private double mass;
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Creates a new node instance
*
* @param id
* @param data
*/
// ---------------------------------------------------------------------------
public Node(Integer id) {
this.id = id;
this.mass = 1.0;
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Simple getter method for id.
*
* @return The value of id.
*/
// ---------------------------------------------------------------------------
public Integer getId() {
return id;
}
// ---------------------------------------------------------------------------
/**
* Simple setter method for id.
*
* @param id
* The value to set id to.
*/
// ---------------------------------------------------------------------------
public void setId(Integer id) {
this.id = id;
}
// ---------------------------------------------------------------------------
/**
* Simple getter method for mass.
*
* @return The value of mass.
*/
// ---------------------------------------------------------------------------
public double getMass() {
return this.mass;
}
// ---------------------------------------------------------------------------
/**
* Simple setter method for mass.
*
* @param mass
* The value to set mass to.
*/
// ---------------------------------------------------------------------------
public void setMass(double mass) {
Preconditions.checkArgument(mass > 0, "Mass MUST be greater then 0!"); //$NON-NLS-1$
this.mass = mass;
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * End of Class
// ***************************************************************************
// ---------------------------------------------------------------------------
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. All rights reserved.
*
* This file is part of TULIP.
*
* TULIP is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, version 3 of the
* License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.utility.graph.forcedirected;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public class Body {
private Dimension position;
private double mass;
private Dimension velocity;
private Dimension acceleration;
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* @param position
* @param mass
*/
// ---------------------------------------------------------------------------
public Body(Dimension position, double mass) {
this.position = position;
this.mass = mass;
this.velocity = new Dimension(0, 0);
this.acceleration = new Dimension(0, 0);
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* @param force
*/
// ---------------------------------------------------------------------------
public void applyForce(Dimension force) {
this.acceleration = this.acceleration.add(force.divideBy(this.mass));
}
// ---------------------------------------------------------------------------
/**
* Simple getter method for position.
*
* @return The value of position.
*/
// ---------------------------------------------------------------------------
public Dimension getPosition() {
return position;
}
// ---------------------------------------------------------------------------
/**
* Simple setter method for position.
*
* @param position
* The value to set position to.
*/
// ---------------------------------------------------------------------------
public void setPosition(Dimension position) {
this.position = position;
}
// ---------------------------------------------------------------------------
/**
* Simple getter method for mass.
*
* @return The value of mass.
*/
// ---------------------------------------------------------------------------
public double getMass() {
return mass;
}
// ---------------------------------------------------------------------------
/**
* Simple setter method for mass.
*
* @param mass
* The value to set mass to.
*/
// ---------------------------------------------------------------------------
public void setMass(double mass) {
this.mass = mass;
}
// ---------------------------------------------------------------------------
/**
* Simple getter method for velocity.
*
* @return The value of velocity.
*/