Update code.

This commit is contained in:
Mikael Capelle 2018-02-20 11:27:12 +01:00
parent a6e8a22081
commit 71accfe13b
18 changed files with 436 additions and 266 deletions

View File

@ -1,42 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry kind="src" output="target/classes" path="src/main"> <classpathentry kind="src" output="target/classes" path="src/main">
<attributes> <attributes>
<attribute name="optional" value="true"/> <attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test"> <classpathentry kind="src" output="target/test-classes" path="src/test">
<attributes> <attributes>
<attribute name="optional" value="true"/> <attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
<classpathentry kind="lib" path="libs/piccolo2d-core-3.0.jar"> <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes> <attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/be-graphes-base/libs/piccolo2d-core-3.0-javadoc.jar!/"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="lib" path="libs/piccolo2d-extras-3.0.jar"> <classpathentry kind="output" path="target/classes"/>
<attributes> </classpath>
<attribute name="javadoc_location" value="jar:platform:/resource/be-graphes-base/libs/piccolo2d-extras-3.0-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="libs/piccolo2d-swt-3.0.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/be-graphes-base/libs/piccolo2d-swt-3.0-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@ -1,24 +1,22 @@
package org.insa.algo ; package org.insa.algo ;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
public abstract class AbstractAlgorithm implements Runnable { public abstract class AbstractAlgorithm<Observer> {
protected AbstractInstance instance; protected AbstractInstance instance;
protected AbstractSolution solution; protected ArrayList<Observer> observers;
protected ArrayList<AbstractObserver> observers;
protected AbstractAlgorithm(AbstractInstance instance) { protected AbstractAlgorithm(AbstractInstance instance) {
this.instance = instance; this.instance = instance;
this.observers = new ArrayList<AbstractObserver>(); this.observers = new ArrayList<Observer>();
this.solution = null;
} }
protected AbstractAlgorithm(AbstractInstance instance, ArrayList<AbstractObserver> observers) { protected AbstractAlgorithm(AbstractInstance instance, ArrayList<Observer> observers) {
this.instance = instance; this.instance = instance;
this.observers = observers;; this.observers = observers;;
this.solution = null;
} }
/** /**
@ -26,44 +24,32 @@ public abstract class AbstractAlgorithm implements Runnable {
* *
* @param observer * @param observer
*/ */
public void addObserver(AbstractObserver observer) { public void addObserver(Observer observer) {
observers.add(observer); observers.add(observer);
} }
/** /**
* @return The list of observers for this algorithm. * @return The list of observers for this algorithm.
*/ */
public ArrayList<AbstractObserver> getObservers() { public ArrayList<Observer> getObservers() {
return observers; return observers;
} }
/**
* Update the current solution.
*
* @param solution New solution, or null to unset the current solution.
*
*/
protected void updateLastSolution(AbstractSolution solution) {
this.solution = solution;
}
/** /**
* @return Instance corresponding to this algorithm. * @return Instance corresponding to this algorithm.
*/ */
public AbstractInstance getInstance() { return instance; } public AbstractInstance getInstance() { return instance; }
/**
* @return Last solution, or null if no solution was stored.
*/
public AbstractSolution getLastSolution() { return solution; }
/** /**
* Run the algorithm and update the current solution. * Run the algorithm and update the current solution.
* *
* @return true if a feasible solution was found (even non-optimal). * @return true if a feasible solution was found (even non-optimal).
*/ */
public void run() { public AbstractSolution run() {
this.solution = this.doRun(); Instant start = Instant.now();
AbstractSolution solution = this.doRun();
solution.setSolvingTime(Duration.between(start, Instant.now()));
return solution;
} }
/** /**

View File

@ -1,20 +0,0 @@
package org.insa.algo;
public abstract class AbstractObserver {
// Specify if the observer is graphic or not.
private final boolean isgraphic;
protected AbstractObserver(boolean isGraphic) {
this.isgraphic = isGraphic;
}
/**
* @return true if this observer is graphic (use drawing to display
* information).
*/
public boolean isGraphic() {
return isgraphic;
}
}

View File

@ -35,10 +35,8 @@ public abstract class AbstractSolution {
this.status = Status.UNKNOWN; this.status = Status.UNKNOWN;
} }
protected AbstractSolution(AbstractInstance instance, protected AbstractSolution(AbstractInstance instance, Status status) {
Duration solvingTime, Status status) {
this.instance = instance; this.instance = instance;
this.solvingTime = solvingTime;
this.status = status; this.status = status;
} }
@ -57,6 +55,15 @@ public abstract class AbstractSolution {
*/ */
public Duration getSolvingTime() { return solvingTime; } public Duration getSolvingTime() { return solvingTime; }
/**
* Set the solving time of this solution.
*
* @param solvingTime Solving time for the solution.
*/
protected void setSolvingTime(Duration solvingTime) {
this.solvingTime = solvingTime;
}
/** /**
* @return true if the solution is feasible or optimal. * @return true if the solution is feasible or optimal.
*/ */

View File

@ -0,0 +1,30 @@
package org.insa.algo.strongconnectivity;
import java.util.ArrayList;
import org.insa.graph.Node;
public interface StronglyConnectedComponentObserver {
/**
* Notify that the algorithm is entering a new component.
*
* @param curNode Starting node for the component.
*/
public void notifyStartComponent(Node curNode);
/**
* Notify that a new node has been found for the current component.
*
* @param node New node found for the current component.
*/
public void notifyNewNodeInComponent(Node node);
/**
* Notify that the algorithm has computed a new component.
*
* @param nodes List of nodes in the component.
*/
public void notifyEndComponent(ArrayList<Node> nodes);
}

View File

@ -2,7 +2,7 @@ package org.insa.algo.strongconnectivity ;
import org.insa.algo.AbstractAlgorithm; import org.insa.algo.AbstractAlgorithm;
public abstract class StronglyConnectedComponentsAlgorithm extends AbstractAlgorithm { public abstract class StronglyConnectedComponentsAlgorithm extends AbstractAlgorithm<StronglyConnectedComponentObserver> {
/** /**
* *
@ -12,5 +12,15 @@ public abstract class StronglyConnectedComponentsAlgorithm extends AbstractAlgor
public StronglyConnectedComponentsAlgorithm(StronglyConnectedComponentsInstance instance) { public StronglyConnectedComponentsAlgorithm(StronglyConnectedComponentsInstance instance) {
super(instance); super(instance);
} }
@Override
public StronglyConnectedComponentsSolution run() {
return (StronglyConnectedComponentsSolution)super.run();
}
@Override
public StronglyConnectedComponentsInstance getInstance() {
return (StronglyConnectedComponentsInstance)super.getInstance();
}
} }

View File

@ -1,6 +1,5 @@
package org.insa.algo.strongconnectivity; package org.insa.algo.strongconnectivity;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import org.insa.algo.AbstractSolution; import org.insa.algo.AbstractSolution;
@ -16,8 +15,8 @@ public class StronglyConnectedComponentsSolution extends AbstractSolution {
} }
protected StronglyConnectedComponentsSolution(StronglyConnectedComponentsInstance instance, protected StronglyConnectedComponentsSolution(StronglyConnectedComponentsInstance instance,
Duration solvingTime, Status status, ArrayList<ArrayList<Node>> components) { Status status, ArrayList<ArrayList<Node>> components) {
super(instance, solvingTime, status); super(instance, status);
this.components = components; this.components = components;
} }

View File

@ -1,12 +1,9 @@
package org.insa.algo.strongconnectivity; package org.insa.algo.strongconnectivity;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Stack; import java.util.Stack;
import org.insa.algo.AbstractSolution;
import org.insa.algo.AbstractSolution.Status; import org.insa.algo.AbstractSolution.Status;
import org.insa.graph.Arc; import org.insa.graph.Arc;
import org.insa.graph.Graph; import org.insa.graph.Graph;
@ -74,7 +71,6 @@ public class TarjanAlgorithm extends StronglyConnectedComponentsAlgorithm {
* @return The strong component containing the given node. * @return The strong component containing the given node.
*/ */
protected void findAndAddStrongComponent(Node v) { protected void findAndAddStrongComponent(Node v) {
Graph graph = getInstance().getGraph();
// Update node info, index and push the node. // Update node info, index and push the node.
indexes[v.getId()] = index; indexes[v.getId()] = index;
@ -117,14 +113,11 @@ public class TarjanAlgorithm extends StronglyConnectedComponentsAlgorithm {
} }
@Override @Override
protected AbstractSolution doRun() { protected StronglyConnectedComponentsSolution doRun() {
Graph graph = getInstance().getGraph(); Graph graph = getInstance().getGraph();
components = new ArrayList<ArrayList<Node>>(); components = new ArrayList<ArrayList<Node>>();
// Starting time...
Instant start = Instant.now();
// Initialize everything // Initialize everything
final int nbNodes = graph.getNodes().size(); final int nbNodes = graph.getNodes().size();
stack = new Stack<Node>(); stack = new Stack<Node>();
@ -144,12 +137,8 @@ public class TarjanAlgorithm extends StronglyConnectedComponentsAlgorithm {
findAndAddStrongComponent(node); findAndAddStrongComponent(node);
} }
} }
// Duration...
Duration solvingTime = Duration.between(start, Instant.now());
return new StronglyConnectedComponentsSolution((StronglyConnectedComponentsInstance)getInstance(), return new StronglyConnectedComponentsSolution(getInstance(), Status.OPTIMAL, components);
solvingTime, Status.OPTIMAL, components);
} }
} }

View File

@ -7,7 +7,7 @@ import org.insa.drawing.Drawing;
import org.insa.drawing.graph.GraphDrawing; import org.insa.drawing.graph.GraphDrawing;
import org.insa.graph.Node; import org.insa.graph.Node;
public class WeaklyConnectedComponentGraphicObserver extends WeaklyConnectedComponentObserver { public class WeaklyConnectedComponentGraphicObserver implements WeaklyConnectedComponentObserver {
private static final Color[] COLORS = { private static final Color[] COLORS = {
Color.BLUE, Color.ORANGE, Color.GREEN, Color.YELLOW, Color.RED Color.BLUE, Color.ORANGE, Color.GREEN, Color.YELLOW, Color.RED
@ -21,7 +21,6 @@ public class WeaklyConnectedComponentGraphicObserver extends WeaklyConnectedComp
private int cindex = 0; private int cindex = 0;
public WeaklyConnectedComponentGraphicObserver(Drawing drawing) { public WeaklyConnectedComponentGraphicObserver(Drawing drawing) {
super(true);
this.drawing = drawing; this.drawing = drawing;
this.gdrawing = new GraphDrawing(drawing); this.gdrawing = new GraphDrawing(drawing);
this.drawing.setAutoRepaint(true); this.drawing.setAutoRepaint(true);

View File

@ -2,37 +2,29 @@ package org.insa.algo.weakconnectivity;
import java.util.ArrayList; import java.util.ArrayList;
import org.insa.algo.AbstractObserver;
import org.insa.graph.Node; import org.insa.graph.Node;
public abstract class WeaklyConnectedComponentObserver extends AbstractObserver { public interface WeaklyConnectedComponentObserver {
/**
* {@inheritDoc}
*/
protected WeaklyConnectedComponentObserver(boolean isGraphic) {
super(isGraphic);
}
/** /**
* Notify that the algorithm is entering a new component. * Notify that the algorithm is entering a new component.
* *
* @param curNode Starting node for the component. * @param curNode Starting node for the component.
*/ */
public abstract void notifyStartComponent(Node curNode); public void notifyStartComponent(Node curNode);
/** /**
* Notify that a new node has been found for the current component. * Notify that a new node has been found for the current component.
* *
* @param node New node found for the current component. * @param node New node found for the current component.
*/ */
public abstract void notifyNewNodeInComponent(Node node); public void notifyNewNodeInComponent(Node node);
/** /**
* Notify that the algorithm has computed a new component. * Notify that the algorithm has computed a new component.
* *
* @param nodes List of nodes in the component. * @param nodes List of nodes in the component.
*/ */
public abstract void notifyEndComponent(ArrayList<Node> nodes); public void notifyEndComponent(ArrayList<Node> nodes);
} }

View File

@ -5,7 +5,7 @@ import java.util.ArrayList;
import org.insa.graph.Node; import org.insa.graph.Node;
public class WeaklyConnectedComponentTextObserver extends WeaklyConnectedComponentObserver { public class WeaklyConnectedComponentTextObserver implements WeaklyConnectedComponentObserver {
// Number of the current component. // Number of the current component.
private int numComponent = 1; private int numComponent = 1;
@ -14,7 +14,6 @@ public class WeaklyConnectedComponentTextObserver extends WeaklyConnectedCompone
PrintStream stream; PrintStream stream;
public WeaklyConnectedComponentTextObserver(PrintStream stream) { public WeaklyConnectedComponentTextObserver(PrintStream stream) {
super(false);
this.stream = stream; this.stream = stream;
} }

View File

@ -1,23 +1,18 @@
package org.insa.algo.weakconnectivity; package org.insa.algo.weakconnectivity;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Queue; import java.util.Queue;
import java.util.HashSet; import java.util.HashSet;
import org.insa.algo.AbstractAlgorithm; import org.insa.algo.AbstractAlgorithm;
import org.insa.algo.AbstractObserver;
import org.insa.algo.AbstractSolution;
import org.insa.algo.AbstractSolution.Status; import org.insa.algo.AbstractSolution.Status;
import org.insa.graph.Arc; import org.insa.graph.Arc;
import org.insa.graph.Graph; import org.insa.graph.Graph;
import org.insa.graph.Node; import org.insa.graph.Node;
public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm { public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm<WeaklyConnectedComponentObserver>{
/** /**
* *
@ -28,6 +23,49 @@ public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm {
super(instance); super(instance);
} }
@Override
public WeaklyConnectedComponentsSolution run() {
return (WeaklyConnectedComponentsSolution)super.run();
}
@Override
public WeaklyConnectedComponentsInstance getInstance() {
return (WeaklyConnectedComponentsInstance)super.getInstance();
}
/**
* Notify all observers that the algorithm is entering a new component.
*
* @param curNode Starting node for the component.
*/
protected void notifyStartComponent(Node curNode) {
for (WeaklyConnectedComponentObserver obs: getObservers()) {
obs.notifyStartComponent(curNode);
}
}
/**
* Notify all observers that a new node has been found for the current component.
*
* @param node New node found for the current component.
*/
protected void notifyNewNodeInComponent(Node node) {
for (WeaklyConnectedComponentObserver obs: getObservers()) {
obs.notifyNewNodeInComponent(node);
}
}
/**
* Notify all observers that the algorithm has computed a new component.
*
* @param nodes List of nodes in the component.
*/
protected void notifyEndComponent(ArrayList<Node> nodes) {
for (WeaklyConnectedComponentObserver obs: getObservers()) {
obs.notifyEndComponent(nodes);
}
}
/** /**
* @return An adjacency list for the undirected graph equivalent to the stored graph. * @return An adjacency list for the undirected graph equivalent to the stored graph.
*/ */
@ -66,9 +104,8 @@ public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm {
// Using a queue because we are doing a BFS // Using a queue because we are doing a BFS
Queue<Integer> queue = new LinkedList<Integer>(); Queue<Integer> queue = new LinkedList<Integer>();
for (AbstractObserver obs: getObservers()) { // Notify observers about the current component.
((WeaklyConnectedComponentObserver)obs).notifyStartComponent(nodes.get(cur)); notifyStartComponent(nodes.get(cur));
}
// Add original node and loop until the queue is empty. // Add original node and loop until the queue is empty.
queue.add(cur); queue.add(cur);
@ -77,8 +114,8 @@ public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm {
Node node = nodes.get(queue.remove()); Node node = nodes.get(queue.remove());
component.add(node); component.add(node);
// notify observers // Notify observers
for (AbstractObserver obs: getObservers()) ((WeaklyConnectedComponentObserver)obs).notifyNewNodeInComponent(node); notifyNewNodeInComponent(node);
for (Integer destId: ugraph.get(node.getId())) { for (Integer destId: ugraph.get(node.getId())) {
Node dest = nodes.get(destId); Node dest = nodes.get(destId);
@ -89,18 +126,14 @@ public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm {
} }
} }
for (AbstractObserver obs: getObservers()) { notifyEndComponent(component);
((WeaklyConnectedComponentObserver)obs).notifyEndComponent(component);
}
return component; return component;
} }
@Override @Override
protected AbstractSolution doRun() { protected WeaklyConnectedComponentsSolution doRun() {
Instant start = Instant.now();
Graph graph = getInstance().getGraph(); Graph graph = getInstance().getGraph();
ArrayList<HashSet<Integer>> ugraph = createUndirectedGraph(); ArrayList<HashSet<Integer>> ugraph = createUndirectedGraph();
boolean[] marked = new boolean[graph.getNodes().size()]; boolean[] marked = new boolean[graph.getNodes().size()];
@ -117,11 +150,8 @@ public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm {
// Find next non-marked // Find next non-marked
for (; cur < marked.length && marked[cur]; ++cur); for (; cur < marked.length && marked[cur]; ++cur);
} }
Duration solvingTime = Duration.between(start, Instant.now()); return new WeaklyConnectedComponentsSolution(getInstance(), Status.OPTIMAL, components);
return new WeaklyConnectedComponentsSolution((WeaklyConnectedComponentsInstance)getInstance(),
solvingTime, Status.OPTIMAL, components);
} }
} }

View File

@ -1,6 +1,5 @@
package org.insa.algo.weakconnectivity; package org.insa.algo.weakconnectivity;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import org.insa.algo.AbstractSolution; import org.insa.algo.AbstractSolution;
@ -16,8 +15,8 @@ public class WeaklyConnectedComponentsSolution extends AbstractSolution {
} }
protected WeaklyConnectedComponentsSolution(WeaklyConnectedComponentsInstance instance, protected WeaklyConnectedComponentsSolution(WeaklyConnectedComponentsInstance instance,
Duration solvingTime, Status status, ArrayList<ArrayList<Node>> components) { Status status, ArrayList<ArrayList<Node>> components) {
super(instance, solvingTime, status); super(instance, status);
this.components = components; this.components = components;
} }

View File

@ -5,17 +5,23 @@ import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter; import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent; import java.awt.event.WindowEvent;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.lang.reflect.Constructor; import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JFrame; import javax.swing.JFrame;
@ -30,10 +36,10 @@ import javax.swing.JSplitPane;
import javax.swing.JTextArea; import javax.swing.JTextArea;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import org.insa.algo.AbstractSolution;
import org.insa.algo.shortestpath.BellmanFordAlgorithm; import org.insa.algo.shortestpath.BellmanFordAlgorithm;
import org.insa.algo.shortestpath.ShortestPathAlgorithm; import org.insa.algo.shortestpath.ShortestPathAlgorithm;
import org.insa.algo.shortestpath.ShortestPathGraphicObserver; import org.insa.algo.shortestpath.ShortestPathGraphicObserver;
@ -41,7 +47,6 @@ import org.insa.algo.shortestpath.ShortestPathInstance;
import org.insa.algo.shortestpath.ShortestPathInstance.Mode; import org.insa.algo.shortestpath.ShortestPathInstance.Mode;
import org.insa.algo.shortestpath.ShortestPathSolution; import org.insa.algo.shortestpath.ShortestPathSolution;
import org.insa.algo.weakconnectivity.WeaklyConnectedComponentGraphicObserver; import org.insa.algo.weakconnectivity.WeaklyConnectedComponentGraphicObserver;
import org.insa.algo.weakconnectivity.WeaklyConnectedComponentTextObserver;
import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsAlgorithm; import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsAlgorithm;
import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsInstance; import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsInstance;
import org.insa.drawing.Drawing; import org.insa.drawing.Drawing;
@ -49,17 +54,17 @@ import org.insa.drawing.graph.BlackAndWhiteGraphPalette;
import org.insa.drawing.graph.GraphDrawing; import org.insa.drawing.graph.GraphDrawing;
import org.insa.drawing.graph.PathDrawing; import org.insa.drawing.graph.PathDrawing;
import org.insa.graph.Graph; import org.insa.graph.Graph;
import org.insa.graph.Node;
import org.insa.graph.Path; import org.insa.graph.Path;
import org.insa.graph.Point;
import org.insa.graph.io.BinaryGraphReader; import org.insa.graph.io.BinaryGraphReader;
import org.insa.graph.io.BinaryPathReader; import org.insa.graph.io.BinaryPathReader;
import org.insa.graph.io.MapMismatchException; import org.insa.graph.io.MapMismatchException;
import org.insa.graph.io.Openfile; import org.insa.graph.io.Openfile;
import com.sun.glass.events.KeyEvent;
public class MainWindow extends JFrame { public class MainWindow extends JFrame {
public class JOutputStream extends OutputStream { protected class JOutputStream extends OutputStream {
private JTextArea textArea; private JTextArea textArea;
public JOutputStream(JTextArea textArea) { public JOutputStream(JTextArea textArea) {
@ -76,7 +81,91 @@ public class MainWindow extends JFrame {
textArea.update(textArea.getGraphics()); textArea.update(textArea.getGraphics());
} }
} }
protected interface CallableWithNodes {
void call(ArrayList<Node> nodes);
};
protected class DrawingClickListener extends MouseAdapter {
// Enable/Disable.
private boolean enabled = false;
// List of points.
private ArrayList<Node> points = new ArrayList<Node>();
// Number of points to find before running.
private int nTargetPoints = 0;
// Callable to call when points are reached.
CallableWithNodes callable = null;
/**
* @return true if this listener is enabled.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Enable this listener.
*
* @param nTargetPoints Number of point to found before calling the callable.
*/
public void enable(int nTargetPoints, CallableWithNodes callable) {
this.enabled = true;
MainWindow.this.getJMenuBar().setEnabled(false);
this.nTargetPoints = nTargetPoints;
this.points.clear();
this.callable = callable;
}
/**
* Disable this listener.
*/
public void disable() {
this.enabled = false;
MainWindow.this.getJMenuBar().setEnabled(true);
}
public void mouseClicked(MouseEvent evt) {
if (!isEnabled()) {
return;
}
Point lonlat;
try {
lonlat = drawing.getLongitudeLatitude(evt);
}
catch (NoninvertibleTransformException e) {
// Should never happens in "normal" circumstances...
e.printStackTrace();
return;
}
System.out.println("MOUSE CLICKED: " + evt.getPoint() + " -> " + lonlat);
ArrayList<Node> nodes = graph.getNodes();
Node node = null;
double minDis = Double.POSITIVE_INFINITY;
for (int n = 0 ; n < nodes.size(); ++n) {
double dis = lonlat.distanceTo(nodes.get(n).getPoint());
if (dis < minDis) {
node = nodes.get(n);
minDis = dis;
}
}
new GraphDrawing(drawing).drawPoint(node.getPoint(), 10, Color.BLUE);
points.add(node);
if (points.size() == nTargetPoints) {
System.out.println("CALLABLE!");
callable.call(points);
this.disable();
}
}
};
/** /**
* *
*/ */
@ -86,11 +175,11 @@ public class MainWindow extends JFrame {
* *
*/ */
private static final String WINDOW_TITLE = "BE Graphes INSA"; private static final String WINDOW_TITLE = "BE Graphes INSA";
/** /**
* *
*/ */
private static final Dimension DEFAULT_DIMENSION = new Dimension(800, 600); private static final int THREAD_TIMER_DELAY = 1000; // in milliseconds
// Current graph. // Current graph.
private Graph graph; private Graph graph;
@ -98,6 +187,10 @@ public class MainWindow extends JFrame {
// Current loaded path. // Current loaded path.
private Path currentPath; private Path currentPath;
// Drawing and click adapter.
private Drawing drawing;
private DrawingClickListener clickAdapter;
// List of item for the top menus. // List of item for the top menus.
private JMenuItem openMapItem; private JMenuItem openMapItem;
@ -107,25 +200,24 @@ public class MainWindow extends JFrame {
// Label containing the map ID of the current graph. // Label containing the map ID of the current graph.
private JLabel mapIdPanel; private JLabel mapIdPanel;
// Thread information
private Instant threadStartTime;
private Timer threadTimer;
private JPanel threadPanel; private JPanel threadPanel;
// Log stream and print stream // Log stream and print stream
private JOutputStream logStream; private JOutputStream logStream;
@SuppressWarnings("unused")
private PrintStream printStream; private PrintStream printStream;
// Current running thread // Current running thread
private Thread currentThread; private Thread currentThread;
/**
*
*/
private Drawing drawing;
public MainWindow() { public MainWindow() {
super(WINDOW_TITLE); super(WINDOW_TITLE);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
setLayout(new BorderLayout()); setLayout(new BorderLayout());
setSize(DEFAULT_DIMENSION);
setJMenuBar(createMenuBar()); setJMenuBar(createMenuBar());
addWindowListener(new WindowAdapter() { addWindowListener(new WindowAdapter() {
@ -145,7 +237,10 @@ public class MainWindow extends JFrame {
JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
drawing = new Drawing(); drawing = new Drawing();
drawing.setBackground(Color.WHITE);
// Click adapter
this.clickAdapter = new DrawingClickListener();
drawing.addMouseListener(this.clickAdapter);
JTextArea infoPanel = new JTextArea(); JTextArea infoPanel = new JTextArea();
infoPanel.setMinimumSize(new Dimension(200, 50)); infoPanel.setMinimumSize(new Dimension(200, 50));
@ -168,22 +263,45 @@ public class MainWindow extends JFrame {
this.add(createStatusBar(), BorderLayout.SOUTH); this.add(createStatusBar(), BorderLayout.SOUTH);
} }
private void launchThread(Runnable runnable) { private void restartThreadTimer() {
currentThread = new Thread(new Runnable() { threadStartTime = Instant.now();
@Override threadTimer.restart();
public void run() { }
threadPanel.setVisible(true);
runnable.run(); private void stopThreadTimer() {
threadPanel.setVisible(false); threadTimer.stop();
} }
});
/**
* @param runnable
* @param canInterrupt
*/
private void launchThread(Runnable runnable, boolean canInterrupt) {
if (canInterrupt) {
currentThread = new Thread(new Runnable() {
@Override
public void run() {
restartThreadTimer();
threadPanel.setVisible(true);
runnable.run();
clearCurrentThread();
}
});
}
else {
currentThread = new Thread(runnable);
}
currentThread.start(); currentThread.start();
} }
private void launchThread(Runnable runnable) {
private ShortestPathInstance getShortestPathParameters() { launchThread(runnable, true);
// TODO: Select origin / end nodes. }
return new ShortestPathInstance(
graph, graph.getNodes().get(2), graph.getNodes().get(139), Mode.TIME);
private void clearCurrentThread() {
stopThreadTimer();
threadPanel.setVisible(false);
currentThread = null;
} }
private void launchShortestPathThread(ShortestPathAlgorithm spAlgorithm) { private void launchShortestPathThread(ShortestPathAlgorithm spAlgorithm) {
@ -192,16 +310,14 @@ public class MainWindow extends JFrame {
launchThread(new Runnable() { launchThread(new Runnable() {
@Override @Override
public void run() { public void run() {
spAlgorithm.run(); ShortestPathSolution solution = spAlgorithm.run();
AbstractSolution solution = spAlgorithm.getLastSolution();
if (solution != null && solution.isFeasible()) { if (solution != null && solution.isFeasible()) {
new PathDrawing(drawing).drawPath(((ShortestPathSolution)solution).getPath()); new PathDrawing(drawing).drawPath(solution.getPath());
} }
} }
}); });
} }
@SuppressWarnings("restriction")
private JMenuBar createMenuBar() { private JMenuBar createMenuBar() {
// Open Map item... // Open Map item...
@ -218,28 +334,33 @@ public class MainWindow extends JFrame {
chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); chooser.setCurrentDirectory(new File(System.getProperty("user.dir")));
chooser.setFileFilter(filter); chooser.setFileFilter(filter);
if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) { if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) {
BinaryGraphReader reader; launchThread(new Runnable() {
try { @Override
reader = new BinaryGraphReader( public void run() {
Openfile.open(chooser.getSelectedFile().getAbsolutePath())); BinaryGraphReader reader;
} catch (IOException e1) { try {
JOptionPane.showMessageDialog(MainWindow.this, "Cannot open the selected file."); reader = new BinaryGraphReader(
return ; Openfile.open(chooser.getSelectedFile().getAbsolutePath()));
} } catch (IOException e1) {
try { JOptionPane.showMessageDialog(MainWindow.this, "Cannot open the selected file.");
graph = reader.read(); return ;
} }
catch (Exception exception) { try {
JOptionPane.showMessageDialog(MainWindow.this, "Unable to read graph from the selected file."); graph = reader.read();
return ; }
} catch (Exception exception) {
drawing.clear(); JOptionPane.showMessageDialog(MainWindow.this, "Unable to read graph from the selected file.");
new GraphDrawing(drawing).drawGraph(graph); return ;
}
drawing.clear();
new GraphDrawing(drawing).drawGraph(graph);
for (JMenuItem item: graphItems) { for (JMenuItem item: graphItems) {
item.setEnabled(true); item.setEnabled(true);
} }
mapIdPanel.setText("Map ID: 0x" + Integer.toHexString(graph.getMapId())); mapIdPanel.setText("Map ID: 0x" + Integer.toHexString(graph.getMapId()));
}
}, false);
} }
} }
}); });
@ -352,7 +473,12 @@ public class MainWindow extends JFrame {
WeaklyConnectedComponentsAlgorithm algo = new WeaklyConnectedComponentsAlgorithm(instance); WeaklyConnectedComponentsAlgorithm algo = new WeaklyConnectedComponentsAlgorithm(instance);
algo.addObserver(new WeaklyConnectedComponentGraphicObserver(drawing)); algo.addObserver(new WeaklyConnectedComponentGraphicObserver(drawing));
// algo.addObserver(new WeaklyConnectedComponentTextObserver(printStream)); // algo.addObserver(new WeaklyConnectedComponentTextObserver(printStream));
launchThread(algo); launchThread(new Runnable() {
@Override
public void run() {
algo.run();
}
});
} }
}); });
@ -361,7 +487,13 @@ public class MainWindow extends JFrame {
bellmanItem.addActionListener(new ActionListener() { bellmanItem.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
launchShortestPathThread(new BellmanFordAlgorithm(getShortestPathParameters())); clickAdapter.enable(2, new CallableWithNodes() {
@Override
public void call(ArrayList<Node> nodes) {
launchShortestPathThread(new BellmanFordAlgorithm(
new ShortestPathInstance(graph, nodes.get(0), nodes.get(1), Mode.TIME)));
}
});
} }
}); });
graphItems.add(wccItem); graphItems.add(wccItem);
@ -386,12 +518,20 @@ public class MainWindow extends JFrame {
return menuBar; return menuBar;
} }
@SuppressWarnings("deprecation")
private void stopCurrentThread() {
// Should not be used in production code, but here I have no idea how
// to do this properly... Cannot use .interrupt() because it would requires
// the algorithm to watch the ThreadInteruption exception.
currentThread.stop();
}
private JPanel createStatusBar() { private JPanel createStatusBar() {
// create the status bar panel and shove it down the bottom of the frame // create the status bar panel and shove it down the bottom of the frame
JPanel statusPanel = new JPanel(); JPanel statusPanel = new JPanel();
statusPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.GRAY)); statusPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.GRAY));
statusPanel.setPreferredSize(new Dimension(getWidth(), 34)); statusPanel.setPreferredSize(new Dimension(getWidth(), 38));
statusPanel.setLayout(new BorderLayout()); statusPanel.setLayout(new BorderLayout());
mapIdPanel = new JLabel(); mapIdPanel = new JLabel();
@ -399,9 +539,10 @@ public class MainWindow extends JFrame {
statusPanel.add(mapIdPanel, BorderLayout.WEST); statusPanel.add(mapIdPanel, BorderLayout.WEST);
JLabel threadInfo = new JLabel("Thread running... "); JLabel threadInfo = new JLabel("Thread running... ");
JLabel threadTimerLabel = new JLabel("00:00:00");
JButton threadButton = new JButton("Stop"); JButton threadButton = new JButton("Stop");
threadButton.addActionListener(new ActionListener() { threadButton.addActionListener(new ActionListener() {
@SuppressWarnings("deprecation")
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (currentThread != null && currentThread.isAlive()) { if (currentThread != null && currentThread.isAlive()) {
@ -409,17 +550,30 @@ public class MainWindow extends JFrame {
"Are you sure you want to kill the running thread?", "Kill Confirmation", "Are you sure you want to kill the running thread?", "Kill Confirmation",
JOptionPane.YES_NO_OPTION); JOptionPane.YES_NO_OPTION);
if (confirmed == JOptionPane.YES_OPTION) { if (confirmed == JOptionPane.YES_OPTION) {
currentThread.stop(); stopCurrentThread();
currentThread = null; clearCurrentThread();
threadPanel.setVisible(false); threadPanel.setVisible(false);
} }
} }
} }
}); });
threadTimer = new Timer(THREAD_TIMER_DELAY, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Duration elapsed = Duration.between(threadStartTime, Instant.now());
long seconds = elapsed.getSeconds();
threadTimerLabel.setText(String.format(
"%02d:%02d:%02d", seconds/3600, seconds/60 % 60, seconds % 60));
}
});
threadTimer.setInitialDelay(0);
threadPanel = new JPanel(); threadPanel = new JPanel();
threadPanel.add(threadInfo); threadPanel.add(threadInfo);
threadPanel.add(threadTimerLabel);
threadPanel.add(threadButton); threadPanel.add(threadButton);
// threadPanel.setVisible(false); threadPanel.setVisible(false);
statusPanel.add(threadPanel, BorderLayout.EAST); statusPanel.add(threadPanel, BorderLayout.EAST);
return statusPanel; return statusPanel;

View File

@ -5,10 +5,15 @@ import java.awt.Color;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Image; import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.image.*; import java.awt.image.*;
import javax.swing.JPanel; import javax.swing.JPanel;
import org.insa.graph.Point;
/** /**
* Cette implementation de la classe Dessin produit vraiment un affichage * Cette implementation de la classe Dessin produit vraiment un affichage
* (au contraire de la classe DessinInvisible). * (au contraire de la classe DessinInvisible).
@ -23,15 +28,10 @@ public class Drawing extends JPanel {
private final Graphics2D gr; private final Graphics2D gr;
private float long1; private double long1, long2, lat1, lat2;
private float long2;
private float lat1;
private float lat2;
// Width and height of the image // Width and height of the image
private final int width, height; private final int width, height;
private boolean bb_is_set ;
private Image image; private Image image;
private ZoomAndPanListener zoomAndPanListener; private ZoomAndPanListener zoomAndPanListener;
@ -57,14 +57,11 @@ public class Drawing extends JPanel {
this.gr = img.createGraphics(); this.gr = img.createGraphics();
this.zoomAndPanListener.setCoordTransform(this.gr.getTransform()); this.zoomAndPanListener.setCoordTransform(this.gr.getTransform());
this.bb_is_set = false;
this.long1 = -180;
this.long1 = 0.0f; this.long2 = 180;
this.long2 = this.width; this.lat1 = -90;
this.lat1 = 0.0f; this.lat2 = 90;
this.lat2 = this.height;
this.clear(); this.clear();
this.repaint(); this.repaint();
@ -108,13 +105,11 @@ public class Drawing extends JPanel {
throw new Error("DessinVisible.setBB : mauvaises coordonnees."); throw new Error("DessinVisible.setBB : mauvaises coordonnees.");
} }
this.long1 = (float)long1; this.long1 = long1;
this.long2 = (float)long2; this.long2 = long2;
this.lat1= (float)lat1; this.lat1= lat1;
this.lat2 = (float)lat2; this.lat2 = lat2;
this.bb_is_set = true;
double scale = 1 / Math.max(this.width / (double)this.getWidth(), this.height / (double)this.getHeight()); double scale = 1 / Math.max(this.width / (double)this.getWidth(), this.height / (double)this.getHeight());
this.zoomAndPanListener.getCoordTransform().setToIdentity(); this.zoomAndPanListener.getCoordTransform().setToIdentity();
@ -126,44 +121,58 @@ public class Drawing extends JPanel {
} }
private int projx(float lon) { private int projx(double lon) {
return (int)(width * (lon - this.long1) / (this.long2 - this.long1)) ; return (int)(width * (lon - this.long1) / (this.long2 - this.long1)) ;
} }
private int projy(float lat) { private int projy(double lat) {
return (int)(height * (1 - (lat - this.lat1) / (this.lat2 - this.lat1))) ; return (int)(height * (1 - (lat - this.lat1) / (this.lat2 - this.lat1))) ;
} }
private void checkBB() { /**
if (!this.bb_is_set) { * Return the longitude and latitude corresponding to the given
throw new Error("Classe DessinVisible : vous devez invoquer la methode setBB avant de dessiner.") ; * position of the MouseEvent.
} *
* @param event
*
* @return
*/
public Point getLongitudeLatitude(MouseEvent event) throws NoninvertibleTransformException {
// Get the point using the inverse transform of the Zoom/Pan object, this gives us
// a point within the drawing box (between [0, 0] and [width, height]).
Point2D ptDst = this.zoomAndPanListener.getCoordTransform().inverseTransform(event.getPoint(), null);
// Inverse the "projection" on x/y to get longitude and latitude.
double lon = ptDst.getX();
double lat = ptDst.getY();
lon = (lon / this.width) * (this.long2 - this.long1) + this.long1;
lat = (1 - lat / this.height) * (this.lat2 - this.lat1) + this.lat1;
// Return a new point.
return new Point(lon, lat);
} }
public void drawLine(float long1, float lat1, float long2, float lat2) { public void drawLine(Point from, Point to) {
this.checkBB() ; int x1 = this.projx(from.getLongitude()) ;
int x1 = this.projx(long1) ; int x2 = this.projx(to.getLongitude()) ;
int x2 = this.projx(long2) ; int y1 = this.projy(from.getLatitude()) ;
int y1 = this.projy(lat1) ; int y2 = this.projy(to.getLatitude()) ;
int y2 = this.projy(lat2) ;
gr.drawLine(x1, y1, x2, y2) ; gr.drawLine(x1, y1, x2, y2) ;
this.doAutoPaint(); this.doAutoPaint();
} }
public void drawPoint(float lon, float lat, int width) { public void drawPoint(Point point, int width) {
this.checkBB() ; int x = this.projx(point.getLongitude()) - width / 2;
int x = this.projx(lon) - width / 2 ; int y = this.projy(point.getLatitude()) - width / 2;
int y = this.projy(lat) - width / 2 ; gr.fillOval(x, y, width, width);
gr.fillOval (x, y, width, width) ;
this.doAutoPaint(); this.doAutoPaint();
} }
public void putText(float lon, float lat, String txt) { public void putText(Point point, String txt) {
this.checkBB() ; int x = this.projx(point.getLongitude());
int x = this.projx(lon) ; int y = this.projy(point.getLatitude());
int y = this.projy(lat) ; gr.drawString(txt, x, y);
gr.drawString (txt, x, y) ;
this.doAutoPaint(); this.doAutoPaint();
} }

View File

@ -9,7 +9,6 @@ import org.insa.graph.Arc;
import org.insa.graph.Graph; import org.insa.graph.Graph;
import org.insa.graph.Node; import org.insa.graph.Node;
import org.insa.graph.Point; import org.insa.graph.Point;
import org.insa.graph.RoadInformation.RoadType;
public class GraphDrawing { public class GraphDrawing {
@ -30,8 +29,7 @@ public class GraphDrawing {
} }
public void drawLine(Point p1, Point p2) { public void drawLine(Point p1, Point p2) {
drawing.drawLine(p1.getLongitude(), p1.getLatitude(), drawing.drawLine(p1, p2);
p2.getLongitude(), p2.getLatitude());
} }
public void drawPoint(Point p) { public void drawPoint(Point p) {
@ -39,12 +37,12 @@ public class GraphDrawing {
} }
public void drawPoint(Point p, int width) { public void drawPoint(Point p, int width) {
drawing.drawPoint(p.getLongitude(), p.getLatitude(), width); drawing.drawPoint(p, width);
} }
public void drawPoint(Point p, int width, Color c) { public void drawPoint(Point p, int width, Color c) {
drawing.setColor(c); drawing.setColor(c);
drawing.drawPoint(p.getLongitude(), p.getLatitude(), width); drawing.drawPoint(p, width);
} }
/** /**

View File

@ -26,14 +26,14 @@ public class Point {
} }
// Longitude and latitude of the point. // Longitude and latitude of the point.
private float longitude, latitude; private double longitude, latitude;
/** /**
* *
* @param longitude Longitude of the point, in degrees. * @param longitude Longitude of the point, in degrees.
* @param latitude Latitude of the point, in degrees. * @param latitude Latitude of the point, in degrees.
*/ */
public Point(float longitude, float latitude) { public Point(double longitude, double latitude) {
this.longitude = longitude; this.longitude = longitude;
this.latitude = latitude; this.latitude = latitude;
} }
@ -41,12 +41,12 @@ public class Point {
/** /**
* @return Longitude of this point (in degrees). * @return Longitude of this point (in degrees).
*/ */
public float getLongitude() { return longitude; } public double getLongitude() { return longitude; }
/** /**
* @return Latitude of this point (in degrees). * @return Latitude of this point (in degrees).
*/ */
public float getLatitude() { return latitude; } public double getLatitude() { return latitude; }
/** /**
* Compute the distance from this point to the given point * Compute the distance from this point to the given point
@ -58,5 +58,9 @@ public class Point {
public double distanceTo(Point target) { public double distanceTo(Point target) {
return distance(this, target); return distance(this, target);
} }
@Override
public String toString() {
return String.format("Point(%f, %f)", getLongitude(), getLatitude());
}
} }

View File

@ -65,7 +65,7 @@ public class BinaryPathReader extends BinaryReader implements AbstractPathReader
current = node; current = node;
} }
return new Path(graph, nodes.get(0), arcs); return new Path(graph, arcs);
} }
/** /**