From 71accfe13bed5f888233835e5fa54826b7e21870 Mon Sep 17 00:00:00 2001 From: Mikael Capelle Date: Tue, 20 Feb 2018 11:27:12 +0100 Subject: [PATCH] Update code. --- .classpath | 69 ++--- src/main/org/insa/algo/AbstractAlgorithm.java | 40 +-- src/main/org/insa/algo/AbstractObserver.java | 20 -- src/main/org/insa/algo/AbstractSolution.java | 13 +- .../StronglyConnectedComponentObserver.java | 30 ++ .../StronglyConnectedComponentsAlgorithm.java | 12 +- .../StronglyConnectedComponentsSolution.java | 5 +- .../strongconnectivity/TarjanAlgorithm.java | 15 +- ...aklyConnectedComponentGraphicObserver.java | 3 +- .../WeaklyConnectedComponentObserver.java | 16 +- .../WeaklyConnectedComponentTextObserver.java | 3 +- .../WeaklyConnectedComponentsAlgorithm.java | 74 +++-- .../WeaklyConnectedComponentsSolution.java | 5 +- src/main/org/insa/base/MainWindow.java | 278 ++++++++++++++---- src/main/org/insa/drawing/Drawing.java | 95 +++--- .../org/insa/drawing/graph/GraphDrawing.java | 8 +- src/main/org/insa/graph/Point.java | 14 +- .../org/insa/graph/io/BinaryPathReader.java | 2 +- 18 files changed, 436 insertions(+), 266 deletions(-) delete mode 100644 src/main/org/insa/algo/AbstractObserver.java create mode 100644 src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentObserver.java diff --git a/.classpath b/.classpath index ed0e8f4..e6ce8ae 100644 --- a/.classpath +++ b/.classpath @@ -1,42 +1,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/org/insa/algo/AbstractAlgorithm.java b/src/main/org/insa/algo/AbstractAlgorithm.java index bcb0d04..4f664c0 100644 --- a/src/main/org/insa/algo/AbstractAlgorithm.java +++ b/src/main/org/insa/algo/AbstractAlgorithm.java @@ -1,24 +1,22 @@ package org.insa.algo ; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; -public abstract class AbstractAlgorithm implements Runnable { +public abstract class AbstractAlgorithm { protected AbstractInstance instance; - protected AbstractSolution solution; - - protected ArrayList observers; + protected ArrayList observers; protected AbstractAlgorithm(AbstractInstance instance) { this.instance = instance; - this.observers = new ArrayList(); - this.solution = null; + this.observers = new ArrayList(); } - protected AbstractAlgorithm(AbstractInstance instance, ArrayList observers) { + protected AbstractAlgorithm(AbstractInstance instance, ArrayList observers) { this.instance = instance; this.observers = observers;; - this.solution = null; } /** @@ -26,44 +24,32 @@ public abstract class AbstractAlgorithm implements Runnable { * * @param observer */ - public void addObserver(AbstractObserver observer) { + public void addObserver(Observer observer) { observers.add(observer); } /** * @return The list of observers for this algorithm. */ - public ArrayList getObservers() { + public ArrayList getObservers() { 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. */ 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. * * @return true if a feasible solution was found (even non-optimal). */ - public void run() { - this.solution = this.doRun(); + public AbstractSolution run() { + Instant start = Instant.now(); + AbstractSolution solution = this.doRun(); + solution.setSolvingTime(Duration.between(start, Instant.now())); + return solution; } /** diff --git a/src/main/org/insa/algo/AbstractObserver.java b/src/main/org/insa/algo/AbstractObserver.java deleted file mode 100644 index 53ca878..0000000 --- a/src/main/org/insa/algo/AbstractObserver.java +++ /dev/null @@ -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; - } - -} \ No newline at end of file diff --git a/src/main/org/insa/algo/AbstractSolution.java b/src/main/org/insa/algo/AbstractSolution.java index 87bddc2..bb23186 100644 --- a/src/main/org/insa/algo/AbstractSolution.java +++ b/src/main/org/insa/algo/AbstractSolution.java @@ -35,10 +35,8 @@ public abstract class AbstractSolution { this.status = Status.UNKNOWN; } - protected AbstractSolution(AbstractInstance instance, - Duration solvingTime, Status status) { + protected AbstractSolution(AbstractInstance instance, Status status) { this.instance = instance; - this.solvingTime = solvingTime; this.status = status; } @@ -57,6 +55,15 @@ public abstract class AbstractSolution { */ 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. */ diff --git a/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentObserver.java b/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentObserver.java new file mode 100644 index 0000000..69f1a01 --- /dev/null +++ b/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentObserver.java @@ -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 nodes); + +} diff --git a/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsAlgorithm.java b/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsAlgorithm.java index 5973890..183b69f 100644 --- a/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsAlgorithm.java +++ b/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsAlgorithm.java @@ -2,7 +2,7 @@ package org.insa.algo.strongconnectivity ; import org.insa.algo.AbstractAlgorithm; -public abstract class StronglyConnectedComponentsAlgorithm extends AbstractAlgorithm { +public abstract class StronglyConnectedComponentsAlgorithm extends AbstractAlgorithm { /** * @@ -12,5 +12,15 @@ public abstract class StronglyConnectedComponentsAlgorithm extends AbstractAlgor public StronglyConnectedComponentsAlgorithm(StronglyConnectedComponentsInstance instance) { super(instance); } + + @Override + public StronglyConnectedComponentsSolution run() { + return (StronglyConnectedComponentsSolution)super.run(); + } + + @Override + public StronglyConnectedComponentsInstance getInstance() { + return (StronglyConnectedComponentsInstance)super.getInstance(); + } } diff --git a/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsSolution.java b/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsSolution.java index 6018966..3503c72 100644 --- a/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsSolution.java +++ b/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsSolution.java @@ -1,6 +1,5 @@ package org.insa.algo.strongconnectivity; -import java.time.Duration; import java.util.ArrayList; import org.insa.algo.AbstractSolution; @@ -16,8 +15,8 @@ public class StronglyConnectedComponentsSolution extends AbstractSolution { } protected StronglyConnectedComponentsSolution(StronglyConnectedComponentsInstance instance, - Duration solvingTime, Status status, ArrayList> components) { - super(instance, solvingTime, status); + Status status, ArrayList> components) { + super(instance, status); this.components = components; } diff --git a/src/main/org/insa/algo/strongconnectivity/TarjanAlgorithm.java b/src/main/org/insa/algo/strongconnectivity/TarjanAlgorithm.java index a9a9a01..0fe7fac 100644 --- a/src/main/org/insa/algo/strongconnectivity/TarjanAlgorithm.java +++ b/src/main/org/insa/algo/strongconnectivity/TarjanAlgorithm.java @@ -1,12 +1,9 @@ package org.insa.algo.strongconnectivity; -import java.time.Duration; -import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Stack; -import org.insa.algo.AbstractSolution; import org.insa.algo.AbstractSolution.Status; import org.insa.graph.Arc; import org.insa.graph.Graph; @@ -74,7 +71,6 @@ public class TarjanAlgorithm extends StronglyConnectedComponentsAlgorithm { * @return The strong component containing the given node. */ protected void findAndAddStrongComponent(Node v) { - Graph graph = getInstance().getGraph(); // Update node info, index and push the node. indexes[v.getId()] = index; @@ -117,14 +113,11 @@ public class TarjanAlgorithm extends StronglyConnectedComponentsAlgorithm { } @Override - protected AbstractSolution doRun() { + protected StronglyConnectedComponentsSolution doRun() { Graph graph = getInstance().getGraph(); components = new ArrayList>(); - // Starting time... - Instant start = Instant.now(); - // Initialize everything final int nbNodes = graph.getNodes().size(); stack = new Stack(); @@ -144,12 +137,8 @@ public class TarjanAlgorithm extends StronglyConnectedComponentsAlgorithm { findAndAddStrongComponent(node); } } - - // Duration... - Duration solvingTime = Duration.between(start, Instant.now()); - return new StronglyConnectedComponentsSolution((StronglyConnectedComponentsInstance)getInstance(), - solvingTime, Status.OPTIMAL, components); + return new StronglyConnectedComponentsSolution(getInstance(), Status.OPTIMAL, components); } } diff --git a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentGraphicObserver.java b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentGraphicObserver.java index 09db88a..2f24dd9 100644 --- a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentGraphicObserver.java +++ b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentGraphicObserver.java @@ -7,7 +7,7 @@ import org.insa.drawing.Drawing; import org.insa.drawing.graph.GraphDrawing; import org.insa.graph.Node; -public class WeaklyConnectedComponentGraphicObserver extends WeaklyConnectedComponentObserver { +public class WeaklyConnectedComponentGraphicObserver implements WeaklyConnectedComponentObserver { private static final Color[] COLORS = { Color.BLUE, Color.ORANGE, Color.GREEN, Color.YELLOW, Color.RED @@ -21,7 +21,6 @@ public class WeaklyConnectedComponentGraphicObserver extends WeaklyConnectedComp private int cindex = 0; public WeaklyConnectedComponentGraphicObserver(Drawing drawing) { - super(true); this.drawing = drawing; this.gdrawing = new GraphDrawing(drawing); this.drawing.setAutoRepaint(true); diff --git a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentObserver.java b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentObserver.java index 9e6e0a6..9347af6 100644 --- a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentObserver.java +++ b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentObserver.java @@ -2,37 +2,29 @@ package org.insa.algo.weakconnectivity; import java.util.ArrayList; -import org.insa.algo.AbstractObserver; 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. * * @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. * * @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. * * @param nodes List of nodes in the component. */ - public abstract void notifyEndComponent(ArrayList nodes); + public void notifyEndComponent(ArrayList nodes); } diff --git a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentTextObserver.java b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentTextObserver.java index b4d4184..0c4eaf6 100644 --- a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentTextObserver.java +++ b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentTextObserver.java @@ -5,7 +5,7 @@ import java.util.ArrayList; import org.insa.graph.Node; -public class WeaklyConnectedComponentTextObserver extends WeaklyConnectedComponentObserver { +public class WeaklyConnectedComponentTextObserver implements WeaklyConnectedComponentObserver { // Number of the current component. private int numComponent = 1; @@ -14,7 +14,6 @@ public class WeaklyConnectedComponentTextObserver extends WeaklyConnectedCompone PrintStream stream; public WeaklyConnectedComponentTextObserver(PrintStream stream) { - super(false); this.stream = stream; } diff --git a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsAlgorithm.java b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsAlgorithm.java index e9980d7..50adb1b 100644 --- a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsAlgorithm.java +++ b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsAlgorithm.java @@ -1,23 +1,18 @@ package org.insa.algo.weakconnectivity; -import java.time.Duration; -import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.LinkedList; import java.util.Queue; import java.util.HashSet; import org.insa.algo.AbstractAlgorithm; -import org.insa.algo.AbstractObserver; -import org.insa.algo.AbstractSolution; import org.insa.algo.AbstractSolution.Status; import org.insa.graph.Arc; import org.insa.graph.Graph; import org.insa.graph.Node; -public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm { +public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm{ /** * @@ -28,6 +23,49 @@ public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm { 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 nodes) { + for (WeaklyConnectedComponentObserver obs: getObservers()) { + obs.notifyEndComponent(nodes); + } + } + /** * @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 Queue queue = new LinkedList(); - for (AbstractObserver obs: getObservers()) { - ((WeaklyConnectedComponentObserver)obs).notifyStartComponent(nodes.get(cur)); - } + // Notify observers about the current component. + notifyStartComponent(nodes.get(cur)); // Add original node and loop until the queue is empty. queue.add(cur); @@ -77,8 +114,8 @@ public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm { Node node = nodes.get(queue.remove()); component.add(node); - // notify observers - for (AbstractObserver obs: getObservers()) ((WeaklyConnectedComponentObserver)obs).notifyNewNodeInComponent(node); + // Notify observers + notifyNewNodeInComponent(node); for (Integer destId: ugraph.get(node.getId())) { Node dest = nodes.get(destId); @@ -89,18 +126,14 @@ public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm { } } - for (AbstractObserver obs: getObservers()) { - ((WeaklyConnectedComponentObserver)obs).notifyEndComponent(component); - } + notifyEndComponent(component); return component; } @Override - protected AbstractSolution doRun() { + protected WeaklyConnectedComponentsSolution doRun() { - Instant start = Instant.now(); - Graph graph = getInstance().getGraph(); ArrayList> ugraph = createUndirectedGraph(); boolean[] marked = new boolean[graph.getNodes().size()]; @@ -117,11 +150,8 @@ public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm { // Find next non-marked for (; cur < marked.length && marked[cur]; ++cur); } - - Duration solvingTime = Duration.between(start, Instant.now()); - - return new WeaklyConnectedComponentsSolution((WeaklyConnectedComponentsInstance)getInstance(), - solvingTime, Status.OPTIMAL, components); + + return new WeaklyConnectedComponentsSolution(getInstance(), Status.OPTIMAL, components); } } diff --git a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsSolution.java b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsSolution.java index a8f82bc..e28e825 100644 --- a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsSolution.java +++ b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsSolution.java @@ -1,6 +1,5 @@ package org.insa.algo.weakconnectivity; -import java.time.Duration; import java.util.ArrayList; import org.insa.algo.AbstractSolution; @@ -16,8 +15,8 @@ public class WeaklyConnectedComponentsSolution extends AbstractSolution { } protected WeaklyConnectedComponentsSolution(WeaklyConnectedComponentsInstance instance, - Duration solvingTime, Status status, ArrayList> components) { - super(instance, solvingTime, status); + Status status, ArrayList> components) { + super(instance, status); this.components = components; } diff --git a/src/main/org/insa/base/MainWindow.java b/src/main/org/insa/base/MainWindow.java index d2e1399..f296fde 100644 --- a/src/main/org/insa/base/MainWindow.java +++ b/src/main/org/insa/base/MainWindow.java @@ -5,17 +5,23 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; 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.WindowEvent; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Point2D; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; -import java.lang.reflect.Constructor; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import javax.swing.BorderFactory; -import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; @@ -30,10 +36,10 @@ import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.KeyStroke; import javax.swing.SwingConstants; +import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.filechooser.FileNameExtensionFilter; -import org.insa.algo.AbstractSolution; import org.insa.algo.shortestpath.BellmanFordAlgorithm; import org.insa.algo.shortestpath.ShortestPathAlgorithm; 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.ShortestPathSolution; import org.insa.algo.weakconnectivity.WeaklyConnectedComponentGraphicObserver; -import org.insa.algo.weakconnectivity.WeaklyConnectedComponentTextObserver; import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsAlgorithm; import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsInstance; 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.PathDrawing; import org.insa.graph.Graph; +import org.insa.graph.Node; import org.insa.graph.Path; +import org.insa.graph.Point; import org.insa.graph.io.BinaryGraphReader; import org.insa.graph.io.BinaryPathReader; import org.insa.graph.io.MapMismatchException; import org.insa.graph.io.Openfile; -import com.sun.glass.events.KeyEvent; - public class MainWindow extends JFrame { - public class JOutputStream extends OutputStream { + protected class JOutputStream extends OutputStream { private JTextArea textArea; public JOutputStream(JTextArea textArea) { @@ -76,7 +81,91 @@ public class MainWindow extends JFrame { textArea.update(textArea.getGraphics()); } } + + protected interface CallableWithNodes { + + void call(ArrayList nodes); + + }; + protected class DrawingClickListener extends MouseAdapter { + + // Enable/Disable. + private boolean enabled = false; + + // List of points. + private ArrayList points = new ArrayList(); + + // 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 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 Dimension DEFAULT_DIMENSION = new Dimension(800, 600); + private static final int THREAD_TIMER_DELAY = 1000; // in milliseconds // Current graph. private Graph graph; @@ -98,6 +187,10 @@ public class MainWindow extends JFrame { // Current loaded path. private Path currentPath; + // Drawing and click adapter. + private Drawing drawing; + private DrawingClickListener clickAdapter; + // List of item for the top menus. private JMenuItem openMapItem; @@ -107,25 +200,24 @@ public class MainWindow extends JFrame { // Label containing the map ID of the current graph. private JLabel mapIdPanel; + // Thread information + private Instant threadStartTime; + private Timer threadTimer; private JPanel threadPanel; // Log stream and print stream private JOutputStream logStream; + + @SuppressWarnings("unused") private PrintStream printStream; // Current running thread private Thread currentThread; - /** - * - */ - private Drawing drawing; - public MainWindow() { super(WINDOW_TITLE); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); setLayout(new BorderLayout()); - setSize(DEFAULT_DIMENSION); setJMenuBar(createMenuBar()); addWindowListener(new WindowAdapter() { @@ -145,7 +237,10 @@ public class MainWindow extends JFrame { JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); drawing = new Drawing(); - drawing.setBackground(Color.WHITE); + + // Click adapter + this.clickAdapter = new DrawingClickListener(); + drawing.addMouseListener(this.clickAdapter); JTextArea infoPanel = new JTextArea(); infoPanel.setMinimumSize(new Dimension(200, 50)); @@ -168,22 +263,45 @@ public class MainWindow extends JFrame { this.add(createStatusBar(), BorderLayout.SOUTH); } - private void launchThread(Runnable runnable) { - currentThread = new Thread(new Runnable() { - @Override - public void run() { - threadPanel.setVisible(true); - runnable.run(); - threadPanel.setVisible(false); - } - }); + private void restartThreadTimer() { + threadStartTime = Instant.now(); + threadTimer.restart(); + } + + private void stopThreadTimer() { + 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(); } - - private ShortestPathInstance getShortestPathParameters() { - // TODO: Select origin / end nodes. - return new ShortestPathInstance( - graph, graph.getNodes().get(2), graph.getNodes().get(139), Mode.TIME); + private void launchThread(Runnable runnable) { + launchThread(runnable, true); + } + + + private void clearCurrentThread() { + stopThreadTimer(); + threadPanel.setVisible(false); + currentThread = null; } private void launchShortestPathThread(ShortestPathAlgorithm spAlgorithm) { @@ -192,16 +310,14 @@ public class MainWindow extends JFrame { launchThread(new Runnable() { @Override public void run() { - spAlgorithm.run(); - AbstractSolution solution = spAlgorithm.getLastSolution(); + ShortestPathSolution solution = spAlgorithm.run(); if (solution != null && solution.isFeasible()) { - new PathDrawing(drawing).drawPath(((ShortestPathSolution)solution).getPath()); + new PathDrawing(drawing).drawPath(solution.getPath()); } } }); } - @SuppressWarnings("restriction") private JMenuBar createMenuBar() { // Open Map item... @@ -218,28 +334,33 @@ public class MainWindow extends JFrame { chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); chooser.setFileFilter(filter); if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) { - BinaryGraphReader reader; - try { - reader = new BinaryGraphReader( - Openfile.open(chooser.getSelectedFile().getAbsolutePath())); - } catch (IOException e1) { - JOptionPane.showMessageDialog(MainWindow.this, "Cannot open the selected file."); - return ; - } - try { - graph = reader.read(); - } - catch (Exception exception) { - JOptionPane.showMessageDialog(MainWindow.this, "Unable to read graph from the selected file."); - return ; - } - drawing.clear(); - new GraphDrawing(drawing).drawGraph(graph); + launchThread(new Runnable() { + @Override + public void run() { + BinaryGraphReader reader; + try { + reader = new BinaryGraphReader( + Openfile.open(chooser.getSelectedFile().getAbsolutePath())); + } catch (IOException e1) { + JOptionPane.showMessageDialog(MainWindow.this, "Cannot open the selected file."); + return ; + } + try { + graph = reader.read(); + } + catch (Exception exception) { + JOptionPane.showMessageDialog(MainWindow.this, "Unable to read graph from the selected file."); + return ; + } + drawing.clear(); + new GraphDrawing(drawing).drawGraph(graph); - for (JMenuItem item: graphItems) { - item.setEnabled(true); - } - mapIdPanel.setText("Map ID: 0x" + Integer.toHexString(graph.getMapId())); + for (JMenuItem item: graphItems) { + item.setEnabled(true); + } + mapIdPanel.setText("Map ID: 0x" + Integer.toHexString(graph.getMapId())); + } + }, false); } } }); @@ -352,7 +473,12 @@ public class MainWindow extends JFrame { WeaklyConnectedComponentsAlgorithm algo = new WeaklyConnectedComponentsAlgorithm(instance); algo.addObserver(new WeaklyConnectedComponentGraphicObserver(drawing)); // 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() { @Override public void actionPerformed(ActionEvent e) { - launchShortestPathThread(new BellmanFordAlgorithm(getShortestPathParameters())); + clickAdapter.enable(2, new CallableWithNodes() { + @Override + public void call(ArrayList nodes) { + launchShortestPathThread(new BellmanFordAlgorithm( + new ShortestPathInstance(graph, nodes.get(0), nodes.get(1), Mode.TIME))); + } + }); } }); graphItems.add(wccItem); @@ -386,12 +518,20 @@ public class MainWindow extends JFrame { 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() { // create the status bar panel and shove it down the bottom of the frame JPanel statusPanel = new JPanel(); 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()); mapIdPanel = new JLabel(); @@ -399,9 +539,10 @@ public class MainWindow extends JFrame { statusPanel.add(mapIdPanel, BorderLayout.WEST); JLabel threadInfo = new JLabel("Thread running... "); + JLabel threadTimerLabel = new JLabel("00:00:00"); JButton threadButton = new JButton("Stop"); threadButton.addActionListener(new ActionListener() { - @SuppressWarnings("deprecation") + @Override public void actionPerformed(ActionEvent e) { 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", JOptionPane.YES_NO_OPTION); if (confirmed == JOptionPane.YES_OPTION) { - currentThread.stop(); - currentThread = null; + stopCurrentThread(); + clearCurrentThread(); 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.add(threadInfo); + threadPanel.add(threadTimerLabel); threadPanel.add(threadButton); - // threadPanel.setVisible(false); + threadPanel.setVisible(false); statusPanel.add(threadPanel, BorderLayout.EAST); return statusPanel; diff --git a/src/main/org/insa/drawing/Drawing.java b/src/main/org/insa/drawing/Drawing.java index f19cbd2..ce6c8c9 100644 --- a/src/main/org/insa/drawing/Drawing.java +++ b/src/main/org/insa/drawing/Drawing.java @@ -5,10 +5,15 @@ import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; +import java.awt.event.MouseEvent; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Point2D; import java.awt.image.*; import javax.swing.JPanel; +import org.insa.graph.Point; + /** * Cette implementation de la classe Dessin produit vraiment un affichage * (au contraire de la classe DessinInvisible). @@ -23,15 +28,10 @@ public class Drawing extends JPanel { private final Graphics2D gr; - private float long1; - private float long2; - private float lat1; - private float lat2; + private double long1, long2, lat1, lat2; // Width and height of the image private final int width, height; - - private boolean bb_is_set ; private Image image; private ZoomAndPanListener zoomAndPanListener; @@ -57,14 +57,11 @@ public class Drawing extends JPanel { this.gr = img.createGraphics(); this.zoomAndPanListener.setCoordTransform(this.gr.getTransform()); - - this.bb_is_set = false; - - this.long1 = 0.0f; - this.long2 = this.width; - this.lat1 = 0.0f; - this.lat2 = this.height; + this.long1 = -180; + this.long2 = 180; + this.lat1 = -90; + this.lat2 = 90; this.clear(); this.repaint(); @@ -108,13 +105,11 @@ public class Drawing extends JPanel { throw new Error("DessinVisible.setBB : mauvaises coordonnees."); } - this.long1 = (float)long1; - this.long2 = (float)long2; - this.lat1= (float)lat1; - this.lat2 = (float)lat2; - - this.bb_is_set = true; - + this.long1 = long1; + this.long2 = long2; + this.lat1= lat1; + this.lat2 = lat2; + double scale = 1 / Math.max(this.width / (double)this.getWidth(), this.height / (double)this.getHeight()); 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)) ; } - private int projy(float lat) { + private int projy(double lat) { return (int)(height * (1 - (lat - this.lat1) / (this.lat2 - this.lat1))) ; } - - private void checkBB() { - if (!this.bb_is_set) { - throw new Error("Classe DessinVisible : vous devez invoquer la methode setBB avant de dessiner.") ; - } + + /** + * Return the longitude and latitude corresponding to the given + * 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) { - this.checkBB() ; - int x1 = this.projx(long1) ; - int x2 = this.projx(long2) ; - int y1 = this.projy(lat1) ; - int y2 = this.projy(lat2) ; + public void drawLine(Point from, Point to) { + int x1 = this.projx(from.getLongitude()) ; + int x2 = this.projx(to.getLongitude()) ; + int y1 = this.projy(from.getLatitude()) ; + int y2 = this.projy(to.getLatitude()) ; gr.drawLine(x1, y1, x2, y2) ; this.doAutoPaint(); } - public void drawPoint(float lon, float lat, int width) { - this.checkBB() ; - int x = this.projx(lon) - width / 2 ; - int y = this.projy(lat) - width / 2 ; - gr.fillOval (x, y, width, width) ; + public void drawPoint(Point point, int width) { + int x = this.projx(point.getLongitude()) - width / 2; + int y = this.projy(point.getLatitude()) - width / 2; + gr.fillOval(x, y, width, width); this.doAutoPaint(); } - public void putText(float lon, float lat, String txt) { - this.checkBB() ; - int x = this.projx(lon) ; - int y = this.projy(lat) ; - gr.drawString (txt, x, y) ; + public void putText(Point point, String txt) { + int x = this.projx(point.getLongitude()); + int y = this.projy(point.getLatitude()); + gr.drawString(txt, x, y); this.doAutoPaint(); } diff --git a/src/main/org/insa/drawing/graph/GraphDrawing.java b/src/main/org/insa/drawing/graph/GraphDrawing.java index 5bb0ea7..e64a001 100644 --- a/src/main/org/insa/drawing/graph/GraphDrawing.java +++ b/src/main/org/insa/drawing/graph/GraphDrawing.java @@ -9,7 +9,6 @@ import org.insa.graph.Arc; import org.insa.graph.Graph; import org.insa.graph.Node; import org.insa.graph.Point; -import org.insa.graph.RoadInformation.RoadType; public class GraphDrawing { @@ -30,8 +29,7 @@ public class GraphDrawing { } public void drawLine(Point p1, Point p2) { - drawing.drawLine(p1.getLongitude(), p1.getLatitude(), - p2.getLongitude(), p2.getLatitude()); + drawing.drawLine(p1, p2); } public void drawPoint(Point p) { @@ -39,12 +37,12 @@ public class GraphDrawing { } 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) { drawing.setColor(c); - drawing.drawPoint(p.getLongitude(), p.getLatitude(), width); + drawing.drawPoint(p, width); } /** diff --git a/src/main/org/insa/graph/Point.java b/src/main/org/insa/graph/Point.java index 162c147..8aa353f 100644 --- a/src/main/org/insa/graph/Point.java +++ b/src/main/org/insa/graph/Point.java @@ -26,14 +26,14 @@ public class Point { } // Longitude and latitude of the point. - private float longitude, latitude; + private double longitude, latitude; /** * * @param longitude Longitude 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.latitude = latitude; } @@ -41,12 +41,12 @@ public class Point { /** * @return Longitude of this point (in degrees). */ - public float getLongitude() { return longitude; } + public double getLongitude() { return longitude; } /** * @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 @@ -58,5 +58,9 @@ public class Point { public double distanceTo(Point target) { return distance(this, target); } - + + @Override + public String toString() { + return String.format("Point(%f, %f)", getLongitude(), getLatitude()); + } } diff --git a/src/main/org/insa/graph/io/BinaryPathReader.java b/src/main/org/insa/graph/io/BinaryPathReader.java index 0819a51..6746014 100644 --- a/src/main/org/insa/graph/io/BinaryPathReader.java +++ b/src/main/org/insa/graph/io/BinaryPathReader.java @@ -65,7 +65,7 @@ public class BinaryPathReader extends BinaryReader implements AbstractPathReader current = node; } - return new Path(graph, nodes.get(0), arcs); + return new Path(graph, arcs); } /**