From eceaecd8133300a11cddd0e0fd94945e640ade0c Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 18 Feb 2018 23:08:02 +0100 Subject: [PATCH] Update. --- .classpath | 65 ++++-- .gitignore | 1 + .project | 6 + src/main/org/insa/base/MainWindow.java | 308 ++++++++++++++++--------- src/main/org/insa/drawing/Drawing.java | 222 ++++++++++++------ 5 files changed, 406 insertions(+), 196 deletions(-) diff --git a/.classpath b/.classpath index f7d8560..ed0e8f4 100644 --- a/.classpath +++ b/.classpath @@ -1,23 +1,42 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore index 3a5ba85..2c5af03 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store bin +/target/ diff --git a/.project b/.project index 9a8e431..87c2087 100644 --- a/.project +++ b/.project @@ -10,8 +10,14 @@ + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.m2e.core.maven2Nature org.eclipse.jdt.core.javanature diff --git a/src/main/org/insa/base/MainWindow.java b/src/main/org/insa/base/MainWindow.java index 4fc986e..d2e1399 100644 --- a/src/main/org/insa/base/MainWindow.java +++ b/src/main/org/insa/base/MainWindow.java @@ -11,10 +11,12 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; +import java.lang.reflect.Constructor; import java.util.ArrayList; import javax.swing.BorderFactory; import javax.swing.BoxLayout; +import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; @@ -28,13 +30,21 @@ import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.KeyStroke; import javax.swing.SwingConstants; +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; +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.DrawingVisible; +import org.insa.drawing.Drawing; import org.insa.drawing.graph.BlackAndWhiteGraphPalette; import org.insa.drawing.graph.GraphDrawing; import org.insa.drawing.graph.PathDrawing; @@ -48,71 +58,76 @@ import org.insa.graph.io.Openfile; import com.sun.glass.events.KeyEvent; public class MainWindow extends JFrame { - + public class JOutputStream extends OutputStream { - private JTextArea textArea; + private JTextArea textArea; - public JOutputStream(JTextArea textArea) { - this.textArea = textArea; - } + public JOutputStream(JTextArea textArea) { + this.textArea = textArea; + } - @Override - public void write(int b) throws IOException { - // redirects data to the text area - textArea.setText(textArea.getText() + String.valueOf((char)b)); - // scrolls the text area to the end of data - textArea.setCaretPosition(textArea.getDocument().getLength()); - // keeps the textArea up to date - textArea.update(textArea.getGraphics()); - } + @Override + public void write(int b) throws IOException { + // redirects data to the text area + textArea.setText(textArea.getText() + String.valueOf((char)b)); + // scrolls the text area to the end of data + textArea.setCaretPosition(textArea.getDocument().getLength()); + // keeps the textArea up to date + textArea.update(textArea.getGraphics()); + } } /** * */ private static final long serialVersionUID = -527660583705140687L; - + /** * */ private static final String WINDOW_TITLE = "BE Graphes INSA"; - + /** * */ private static final Dimension DEFAULT_DIMENSION = new Dimension(800, 600); - + // Current graph. private Graph graph; - + // Current loaded path. private Path currentPath; - + // List of item for the top menus. private JMenuItem openMapItem; - + // List of items that cannot be used without a graph private ArrayList graphItems = new ArrayList(); - + // Label containing the map ID of the current graph. private JLabel mapIdPanel; + private JPanel threadPanel; + // Log stream and print stream private JOutputStream logStream; private PrintStream printStream; + // Current running thread + private Thread currentThread; + /** * */ - private DrawingVisible drawing; - + 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() { public void windowClosing(WindowEvent e) { int confirmed = JOptionPane.showConfirmDialog(null, @@ -125,13 +140,13 @@ public class MainWindow extends JFrame { } } }); - + // Create graph area JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); - - drawing = new DrawingVisible(); + + drawing = new Drawing(); drawing.setBackground(Color.WHITE); - + JTextArea infoPanel = new JTextArea(); infoPanel.setMinimumSize(new Dimension(200, 50)); // infoPanel.setBorder(BorderFactory.createMatteBorder(0, 1, 0, 0, Color.GRAY)); @@ -140,36 +155,70 @@ public class MainWindow extends JFrame { infoPanel.setEditable(false); this.logStream = new JOutputStream(infoPanel); this.printStream = new PrintStream(this.logStream); - - sp.setResizeWeight(0.8); - // sp.setEnabled(false); - sp.setDividerSize(5); - - sp.setBackground(Color.WHITE); + + sp.setResizeWeight(0.8); + // sp.setEnabled(false); + sp.setDividerSize(5); + + sp.setBackground(Color.WHITE); sp.add(drawing); sp.add(new JScrollPane(infoPanel)); this.add(sp, BorderLayout.CENTER); - + 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); + } + }); + 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 launchShortestPathThread(ShortestPathAlgorithm spAlgorithm) { + spAlgorithm.addObserver(new ShortestPathGraphicObserver(drawing)); + // algo.addObserver(new ShortestPathTextObserver(printStream)); + launchThread(new Runnable() { + @Override + public void run() { + spAlgorithm.run(); + AbstractSolution solution = spAlgorithm.getLastSolution(); + if (solution != null && solution.isFeasible()) { + new PathDrawing(drawing).drawPath(((ShortestPathSolution)solution).getPath()); + } + } + }); + } + + @SuppressWarnings("restriction") private JMenuBar createMenuBar() { // Open Map item... openMapItem = new JMenuItem("Open Map... ", - KeyEvent.VK_O); + KeyEvent.VK_O); openMapItem.setAccelerator(KeyStroke.getKeyStroke( - KeyEvent.VK_O, ActionEvent.ALT_MASK)); + KeyEvent.VK_O, ActionEvent.ALT_MASK)); openMapItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JFileChooser chooser = new JFileChooser(); - FileNameExtensionFilter filter = new FileNameExtensionFilter( - "Map & compressed map files", "map", "map.gz"); - chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); - chooser.setFileFilter(filter); - if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) { - BinaryGraphReader reader; + FileNameExtensionFilter filter = new FileNameExtensionFilter( + "Map & compressed map files", "map", "map.gz"); + 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())); @@ -177,38 +226,38 @@ public class MainWindow extends JFrame { JOptionPane.showMessageDialog(MainWindow.this, "Cannot open the selected file."); return ; } - try { - graph = reader.read(); - } - catch (Exception exception) { + 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())); - } + } + drawing.clear(); + new GraphDrawing(drawing).drawGraph(graph); + + for (JMenuItem item: graphItems) { + item.setEnabled(true); + } + mapIdPanel.setText("Map ID: 0x" + Integer.toHexString(graph.getMapId())); + } } }); - + // Open Path item... JMenuItem openPathItem = new JMenuItem("Open Path... ", KeyEvent.VK_P); openPathItem.setAccelerator(KeyStroke.getKeyStroke( - KeyEvent.VK_P, ActionEvent.ALT_MASK)); + KeyEvent.VK_P, ActionEvent.ALT_MASK)); openPathItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JFileChooser chooser = new JFileChooser(); - FileNameExtensionFilter filter = new FileNameExtensionFilter( - "Path & compressed path files", "path", "path.gz"); - chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); - chooser.setFileFilter(filter); - if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) { - BinaryPathReader reader; + FileNameExtensionFilter filter = new FileNameExtensionFilter( + "Path & compressed path files", "path", "path.gz"); + chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); + chooser.setFileFilter(filter); + if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) { + BinaryPathReader reader; try { reader = new BinaryPathReader( Openfile.open(chooser.getSelectedFile().getAbsolutePath())); @@ -216,23 +265,23 @@ public class MainWindow extends JFrame { JOptionPane.showMessageDialog(MainWindow.this, "Cannot open the selected file."); return ; } - try { - currentPath = reader.readPath(graph); - } - catch (MapMismatchException exception) { - JOptionPane.showMessageDialog(MainWindow.this, "The selected file does not contain a path for the current graph."); - return; - } - catch (Exception exception) { + try { + currentPath = reader.readPath(graph); + } + catch (MapMismatchException exception) { + JOptionPane.showMessageDialog(MainWindow.this, "The selected file does not contain a path for the current graph."); + return; + } + catch (Exception exception) { JOptionPane.showMessageDialog(MainWindow.this, "Unable to read path from the selected file."); return ; - } - new PathDrawing(drawing).drawPath(currentPath); - } + } + new PathDrawing(drawing).drawPath(currentPath); + } } }); graphItems.add(openPathItem); - + // Close item JMenuItem closeItem = new JMenuItem("Quit", KeyEvent.VK_Q); closeItem.setAccelerator(KeyStroke.getKeyStroke( @@ -250,7 +299,7 @@ public class MainWindow extends JFrame { fileMenu.add(openPathItem); fileMenu.addSeparator(); fileMenu.add(closeItem); - + // Second menu JMenuItem drawGraphItem = new JMenuItem("Redraw", KeyEvent.VK_R); drawGraphItem.setAccelerator(KeyStroke.getKeyStroke( @@ -258,10 +307,14 @@ public class MainWindow extends JFrame { drawGraphItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - drawing.clear(); - drawing.setAutoRepaint(true); - new GraphDrawing(drawing).drawGraph(graph); - drawing.setAutoRepaint(false); + launchThread(new Runnable() { + @Override + public void run() { + drawing.clear(); + drawing.setAutoRepaint(true); + new GraphDrawing(drawing).drawGraph(graph); + } + }); } }); graphItems.add(drawGraphItem); @@ -271,40 +324,58 @@ public class MainWindow extends JFrame { drawGraphBWItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - drawing.clear(); - drawing.setAutoRepaint(true); - new GraphDrawing(drawing, new BlackAndWhiteGraphPalette()).drawGraph(graph); - drawing.setAutoRepaint(false); + launchThread(new Runnable() { + @Override + public void run() { + drawing.clear(); + drawing.setAutoRepaint(true); + new GraphDrawing(drawing, new BlackAndWhiteGraphPalette()).drawGraph(graph); + } + }); } }); graphItems.add(drawGraphBWItem); - + JMenu graphMenu = new JMenu("Graph"); graphMenu.add(drawGraphItem); graphMenu.add(drawGraphBWItem); - + // Algo menu JMenu algoMenu = new JMenu("Algorithms"); - + + // Weakly connected components JMenuItem wccItem = new JMenuItem("Weakly Connected Components"); wccItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - WeaklyConnectedComponentsInstance instance = new WeaklyConnectedComponentsInstance(graph); WeaklyConnectedComponentsAlgorithm algo = new WeaklyConnectedComponentsAlgorithm(instance); algo.addObserver(new WeaklyConnectedComponentGraphicObserver(drawing)); - - (new Thread(algo)).start(); + // algo.addObserver(new WeaklyConnectedComponentTextObserver(printStream)); + launchThread(algo); + } + }); + + // Shortest path + JMenuItem bellmanItem = new JMenuItem("Shortest Path (Bellman-Ford)"); + bellmanItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + launchShortestPathThread(new BellmanFordAlgorithm(getShortestPathParameters())); } }); graphItems.add(wccItem); - + graphItems.add(bellmanItem); + algoMenu.add(wccItem); - + algoMenu.addSeparator(); + algoMenu.add(bellmanItem); + // algoMenu.add(djikstraItem); + // algoMenu.add(aStarItem); + // Create the menu bar. JMenuBar menuBar = new JMenuBar(); - + menuBar.add(fileMenu); menuBar.add(graphMenu); menuBar.add(algoMenu); @@ -312,29 +383,60 @@ public class MainWindow extends JFrame { for (JMenuItem item: graphItems) { item.setEnabled(false); } - + return menuBar; } - + 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(), 20)); - statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.X_AXIS)); - + statusPanel.setPreferredSize(new Dimension(getWidth(), 34)); + statusPanel.setLayout(new BorderLayout()); + mapIdPanel = new JLabel(); mapIdPanel.setHorizontalAlignment(SwingConstants.LEFT); - statusPanel.add(mapIdPanel); + statusPanel.add(mapIdPanel, BorderLayout.WEST); + + JLabel threadInfo = new JLabel("Thread running... "); + JButton threadButton = new JButton("Stop"); + threadButton.addActionListener(new ActionListener() { + @SuppressWarnings("deprecation") + @Override + public void actionPerformed(ActionEvent e) { + if (currentThread != null && currentThread.isAlive()) { + int confirmed = JOptionPane.showConfirmDialog(null, + "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; + threadPanel.setVisible(false); + } + } + } + }); + threadPanel = new JPanel(); + threadPanel.add(threadInfo); + threadPanel.add(threadButton); + // threadPanel.setVisible(false); + statusPanel.add(threadPanel, BorderLayout.EAST); return statusPanel; } - + public static void main(final String[] args) { + + // Try to set system look and feel. + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) ; + } + catch (Exception e) { } + MainWindow w = new MainWindow(); w.setExtendedState(JFrame.MAXIMIZED_BOTH); w.setVisible(true); } - + } diff --git a/src/main/org/insa/drawing/Drawing.java b/src/main/org/insa/drawing/Drawing.java index e04d9fc..f19cbd2 100644 --- a/src/main/org/insa/drawing/Drawing.java +++ b/src/main/org/insa/drawing/Drawing.java @@ -1,88 +1,170 @@ -package org.insa.drawing ; +package org.insa.drawing; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.*; + +import javax.swing.JPanel; /** - * Classe abstraite pour dessiner a l'ecran. - * Deux implementations : une sous-classe DessinVisible qui dessine vraiment a l'ecran - * et une sous-classe DessinInvisible qui ne dessine rien (pour ne pas ralentir les tests avec l'affichage). + * Cette implementation de la classe Dessin produit vraiment un affichage + * (au contraire de la classe DessinInvisible). */ -import java.awt.* ; +public class Drawing extends JPanel { -public interface Drawing { - /** - * Enable auto-repaint mode - When this mode is enable, call to - * drawing function will automatically repaint the drawing, which - * may be very slow in some case. - * - * @param autoRepaint Use true to enable auto-repaint, false to disable. * */ - public void setAutoRepaint(boolean autoRepaint); + private static final long serialVersionUID = 96779785877771827L; + private final Graphics2D gr; + + private float long1; + private float long2; + private float lat1; + private float lat2; + + // Width and height of the image + private final int width, height; + + private boolean bb_is_set ; + + private Image image; + private ZoomAndPanListener zoomAndPanListener; + + public boolean autoRepaint = true; + /** - * Repaint the drawing. - * + * Cree et affiche une nouvelle fenetre de dessin. */ - public void repaint(); + public Drawing() { + + this.zoomAndPanListener = new ZoomAndPanListener(this, ZoomAndPanListener.DEFAULT_MIN_ZOOM_LEVEL, 20, 1.2); + this.addMouseListener(zoomAndPanListener); + this.addMouseMotionListener(zoomAndPanListener); + this.addMouseWheelListener(zoomAndPanListener); + + this.width = 2000; + this.height = 1600; + + BufferedImage img = new BufferedImage(this.width, this.height, BufferedImage.TYPE_3BYTE_BGR); + + this.image = img; + 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.clear(); + this.repaint(); + + } + + @Override + public void paintComponent(Graphics g1) { + Graphics2D g = (Graphics2D)g1; + g.clearRect(0, 0, getWidth(), getHeight()); + g.setTransform(zoomAndPanListener.getCoordTransform()); + g.drawImage(image, 0, 0, this); + } - /** - * Set the pencil width. - * - * @param width Width for the pencil. - * - */ - public void setWidth(int width); - - /** - * Set the pencil color. - * - * @param color Color for the pencil. - * - */ - public void setColor(Color col); - - /** - * Clear the drawing. - */ - public void clear(); + public void setAutoRepaint(boolean autoRepaint) { + this.autoRepaint = autoRepaint; + } + + protected void doAutoPaint() { + if (autoRepaint) { + this.repaint(); + } + } + + public void setWidth(int width) { + this.gr.setStroke(new BasicStroke(width)); + } - /** - * Indique les bornes de la fenetre graphique. - * Le calcul des coordonnees en pixel se fera automatiquement - * a l'appel des methodes drawLine et autres. - * - * @param long1 longitude du bord gauche - * @param long2 longitude du bord droit - * @param lat1 latitude du bord bas - * @param lat2 latitude du bord haut - * - */ - public void setBB(double long1, double long2, double lat1, double lat2); + public void setColor(Color col) { + this.gr.setColor(col); + } - /** - * Trace un segment. - * @param long1 longitude du premier point - * @param lat1 latitude du premier point - * @param long2 longitude du second point - * @param lat2 latitude du second point - */ - public void drawLine(float long1, float lat1, float long2, float lat2); + public void clear() { + this.gr.setColor(Color.WHITE); + this.gr.fillRect(0, 0, this.width, this.height); + } - /** - * Trace un point. - * @param lon longitude du point - * @param lat latitude du point - * @param width grosseur du point - */ - public void drawPoint(float lon, float lat, int width); + public void setBB(double long1, double long2, double lat1, double lat2) { - /** - * Ecrit du texte a la position indiquee. - * @param lon longitude du point ou positionner le texte. - * @param lat latitude du point ou positionner le texte. - * @param txt le texte a ecrire. - */ - public void putText(float lon, float lat, String txt); + if (long1 > long2 || lat1 > lat2) { + 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; + + double scale = 1 / Math.max(this.width / (double)this.getWidth(), this.height / (double)this.getHeight()); + + this.zoomAndPanListener.getCoordTransform().setToIdentity(); + this.zoomAndPanListener.getCoordTransform().translate((this.getWidth() - this.width * scale) / 2, + (this.getHeight() - this.height * scale) / 2); + this.zoomAndPanListener.getCoordTransform().scale(scale, scale); + this.zoomAndPanListener.setZoomLevel(0); + this.repaint(); + + } + + private int projx(float lon) { + return (int)(width * (lon - this.long1) / (this.long2 - this.long1)) ; + } + + private int projy(float 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.") ; + } + } + + 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) ; + + 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) ; + 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) ; + this.doAutoPaint(); + } }