Regoranized package.
This commit is contained in:
625
src/main/org/insa/graphics/MainWindow.java
Normal file
625
src/main/org/insa/graphics/MainWindow.java
Normal file
@@ -0,0 +1,625 @@
|
||||
package org.insa.graphics;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
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.shortestpath.BellmanFordAlgorithm;
|
||||
import org.insa.algo.shortestpath.ShortestPathAlgorithm;
|
||||
import org.insa.algo.shortestpath.ShortestPathData;
|
||||
import org.insa.algo.shortestpath.ShortestPathData.Mode;
|
||||
import org.insa.algo.shortestpath.ShortestPathGraphicObserver;
|
||||
import org.insa.algo.shortestpath.ShortestPathSolution;
|
||||
import org.insa.algo.weakconnectivity.WeaklyConnectedComponentGraphicObserver;
|
||||
import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsAlgorithm;
|
||||
import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsData;
|
||||
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.AbstractGraphReader;
|
||||
import org.insa.graph.io.BinaryGraphReader;
|
||||
import org.insa.graph.io.BinaryGraphReaderV2;
|
||||
import org.insa.graph.io.BinaryPathReader;
|
||||
import org.insa.graph.io.MapMismatchException;
|
||||
import org.insa.graph.io.Openfile;
|
||||
import org.insa.graphics.drawing.BasicDrawing;
|
||||
import org.insa.graphics.drawing.BlackAndWhiteGraphPalette;
|
||||
import org.insa.graphics.drawing.Drawing;
|
||||
import org.insa.graphics.drawing.DrawingClickListener;
|
||||
import org.insa.graphics.drawing.MapViewDrawing;
|
||||
|
||||
public class MainWindow extends JFrame {
|
||||
|
||||
protected class JOutputStream extends OutputStream {
|
||||
private JTextArea 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());
|
||||
}
|
||||
}
|
||||
|
||||
protected interface CallableWithNodes {
|
||||
|
||||
void call(ArrayList<Node> nodes);
|
||||
|
||||
};
|
||||
|
||||
protected class MultiPointsClickListener implements DrawingClickListener {
|
||||
|
||||
// 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;
|
||||
this.nTargetPoints = nTargetPoints;
|
||||
this.points.clear();
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable this listener.
|
||||
*/
|
||||
public void disable() {
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(Point lonlat) {
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
Node node = graph.findClosestNode(lonlat);
|
||||
drawing.drawMarker(node.getPoint(), Color.BLUE);
|
||||
points.add(node);
|
||||
System.out.println("Click at " + lonlat + ", " + points.size() + "/" + nTargetPoints + " in array.");
|
||||
if (points.size() == nTargetPoints) {
|
||||
callable.call(points);
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -527660583705140687L;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final String WINDOW_TITLE = "BE Graphes INSA";
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final int THREAD_TIMER_DELAY = 1000; // in milliseconds
|
||||
|
||||
// Current graph.
|
||||
private Graph graph;
|
||||
|
||||
// Current loaded path.
|
||||
private Path currentPath;
|
||||
|
||||
// Drawing and click adapter.
|
||||
private Drawing drawing;
|
||||
private MultiPointsClickListener clickAdapter;
|
||||
|
||||
// Main panel.
|
||||
private JSplitPane mainPanel;
|
||||
|
||||
// List of item for the top menus.
|
||||
private JMenuItem openMapItem;
|
||||
|
||||
// List of items that cannot be used without a graph
|
||||
private ArrayList<JMenuItem> graphLockItems = new ArrayList<JMenuItem>();
|
||||
|
||||
// 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;
|
||||
|
||||
public MainWindow() {
|
||||
super(WINDOW_TITLE);
|
||||
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||
setLayout(new BorderLayout());
|
||||
setJMenuBar(createMenuBar());
|
||||
|
||||
addWindowListener(new WindowAdapter() {
|
||||
public void windowClosing(WindowEvent e) {
|
||||
int confirmed = JOptionPane.showConfirmDialog(null, "Are you sure you want to close the application?",
|
||||
"Exit Confirmation", JOptionPane.YES_NO_OPTION);
|
||||
|
||||
if (confirmed == JOptionPane.YES_OPTION) {
|
||||
dispose();
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Create graph area
|
||||
mainPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
|
||||
|
||||
this.drawing = new BasicDrawing();
|
||||
|
||||
// Click adapter
|
||||
this.clickAdapter = new MultiPointsClickListener();
|
||||
this.drawing.addDrawingClickListener(this.clickAdapter);
|
||||
|
||||
JTextArea infoPanel = new JTextArea();
|
||||
infoPanel.setMinimumSize(new Dimension(200, 50));
|
||||
infoPanel.setBackground(Color.WHITE);
|
||||
infoPanel.setLineWrap(true);
|
||||
infoPanel.setEditable(false);
|
||||
this.logStream = new JOutputStream(infoPanel);
|
||||
this.printStream = new PrintStream(this.logStream);
|
||||
|
||||
mainPanel.setResizeWeight(0.8);
|
||||
// sp.setEnabled(false);
|
||||
mainPanel.setDividerSize(5);
|
||||
|
||||
mainPanel.setBackground(Color.WHITE);
|
||||
mainPanel.setLeftComponent((Component) this.drawing);
|
||||
mainPanel.setRightComponent(new JScrollPane(infoPanel));
|
||||
this.add(mainPanel, BorderLayout.CENTER);
|
||||
|
||||
// Top Panel
|
||||
this.add(createTopPanel(), BorderLayout.NORTH);
|
||||
this.add(createStatusBar(), BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
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 void launchThread(Runnable runnable) {
|
||||
launchThread(runnable, true);
|
||||
}
|
||||
|
||||
private void clearCurrentThread() {
|
||||
stopThreadTimer();
|
||||
threadPanel.setVisible(false);
|
||||
currentThread = null;
|
||||
}
|
||||
|
||||
private void launchShortestPathThread(ShortestPathAlgorithm spAlgorithm) {
|
||||
spAlgorithm.addObserver(new ShortestPathGraphicObserver(drawing));
|
||||
// algo.addObserver(new ShortestPathTextObserver(printStream));
|
||||
launchThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ShortestPathSolution solution = spAlgorithm.run();
|
||||
if (solution != null && solution.isFeasible()) {
|
||||
drawing.drawPath(solution.getPath());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateDrawing(Class<? extends Drawing> newClass) {
|
||||
|
||||
drawing.clear();
|
||||
if (drawing == null || !newClass.isInstance(drawing)) {
|
||||
try {
|
||||
drawing = newClass.newInstance();
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
drawing.addDrawingClickListener(this.clickAdapter);
|
||||
}
|
||||
mainPanel.setLeftComponent((Component) drawing);
|
||||
}
|
||||
|
||||
private JMenuBar createMenuBar() {
|
||||
|
||||
// Open Map item...
|
||||
openMapItem = new JMenuItem("Open Map... ", KeyEvent.VK_O);
|
||||
openMapItem.setAccelerator(KeyStroke.getKeyStroke(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",
|
||||
"map2", "mapgr", "map.gz");
|
||||
chooser.setCurrentDirectory(new File(System.getProperty("user.dir")));
|
||||
chooser.setFileFilter(filter);
|
||||
if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) {
|
||||
launchThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String path = chooser.getSelectedFile().getAbsolutePath();
|
||||
DataInputStream stream;
|
||||
try {
|
||||
stream = Openfile.open(path);
|
||||
}
|
||||
catch (IOException e1) {
|
||||
JOptionPane.showMessageDialog(MainWindow.this, "Cannot open the selected file.");
|
||||
return;
|
||||
}
|
||||
AbstractGraphReader reader;
|
||||
if (path.endsWith(".map2") || path.endsWith("mapgr")) {
|
||||
reader = new BinaryGraphReaderV2(stream);
|
||||
}
|
||||
else {
|
||||
reader = new BinaryGraphReader(stream);
|
||||
}
|
||||
try {
|
||||
graph = reader.read();
|
||||
}
|
||||
catch (Exception exception) {
|
||||
JOptionPane.showMessageDialog(MainWindow.this,
|
||||
"Unable to read graph from the selected file.");
|
||||
exception.printStackTrace(System.out);
|
||||
return;
|
||||
}
|
||||
drawing.clear();
|
||||
drawing.drawGraph(graph);
|
||||
|
||||
for (JMenuItem item: graphLockItems) {
|
||||
item.setEnabled(true);
|
||||
}
|
||||
mapIdPanel.setText("Map ID: 0x" + Integer.toHexString(graph.getMapId()));
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Open Path item...
|
||||
JMenuItem openPathItem = new JMenuItem("Open Path... ", KeyEvent.VK_P);
|
||||
openPathItem.setAccelerator(KeyStroke.getKeyStroke(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;
|
||||
try {
|
||||
reader = new BinaryPathReader(Openfile.open(chooser.getSelectedFile().getAbsolutePath()));
|
||||
}
|
||||
catch (IOException e1) {
|
||||
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) {
|
||||
JOptionPane.showMessageDialog(MainWindow.this, "Unable to read path from the selected file.");
|
||||
return;
|
||||
}
|
||||
drawing.drawPath(currentPath);
|
||||
}
|
||||
}
|
||||
});
|
||||
graphLockItems.add(openPathItem);
|
||||
|
||||
// Close item
|
||||
JMenuItem closeItem = new JMenuItem("Quit", KeyEvent.VK_Q);
|
||||
closeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.ALT_MASK));
|
||||
closeItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
MainWindow.this.dispatchEvent(new WindowEvent(MainWindow.this, WindowEvent.WINDOW_CLOSING));
|
||||
}
|
||||
});
|
||||
|
||||
// Build the first menu.
|
||||
JMenu fileMenu = new JMenu("File");
|
||||
fileMenu.add(openMapItem);
|
||||
fileMenu.add(openPathItem);
|
||||
fileMenu.addSeparator();
|
||||
fileMenu.add(closeItem);
|
||||
|
||||
// Second menu
|
||||
JMenuItem drawGraphItem = new JMenuItem("Redraw", KeyEvent.VK_R);
|
||||
drawGraphItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.ALT_MASK));
|
||||
drawGraphItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
launchThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateDrawing(BasicDrawing.class);
|
||||
drawing.drawGraph(graph);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
graphLockItems.add(drawGraphItem);
|
||||
JMenuItem drawGraphBWItem = new JMenuItem("Redraw (B&W)", KeyEvent.VK_B);
|
||||
drawGraphBWItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, ActionEvent.ALT_MASK));
|
||||
drawGraphBWItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
launchThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateDrawing(BasicDrawing.class);
|
||||
drawing.drawGraph(graph, new BlackAndWhiteGraphPalette());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
graphLockItems.add(drawGraphBWItem);
|
||||
JMenuItem drawGraphMapsforgeItem = new JMenuItem("Redraw (Map)", KeyEvent.VK_M);
|
||||
drawGraphMapsforgeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, ActionEvent.ALT_MASK));
|
||||
drawGraphMapsforgeItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
launchThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateDrawing(MapViewDrawing.class);
|
||||
drawing.drawGraph(graph);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
graphLockItems.add(drawGraphMapsforgeItem);
|
||||
|
||||
JMenu graphMenu = new JMenu("Graph");
|
||||
graphMenu.add(drawGraphItem);
|
||||
graphMenu.add(drawGraphBWItem);
|
||||
graphMenu.addSeparator();
|
||||
graphMenu.add(drawGraphMapsforgeItem);
|
||||
|
||||
// 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) {
|
||||
WeaklyConnectedComponentsData instance = new WeaklyConnectedComponentsData(graph);
|
||||
WeaklyConnectedComponentsAlgorithm algo = new WeaklyConnectedComponentsAlgorithm(instance);
|
||||
algo.addObserver(new WeaklyConnectedComponentGraphicObserver(drawing));
|
||||
// algo.addObserver(new WeaklyConnectedComponentTextObserver(printStream));
|
||||
launchThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
algo.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Shortest path
|
||||
JMenuItem bellmanItem = new JMenuItem("Shortest Path (Bellman-Ford)");
|
||||
bellmanItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
int idx = JOptionPane.showOptionDialog(MainWindow.this, "Which mode do you want?", "Mode selection",
|
||||
JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, Mode.values(), Mode.LENGTH);
|
||||
|
||||
if (idx != -1) {
|
||||
Mode mode = Mode.values()[idx];
|
||||
clickAdapter.enable(2, new CallableWithNodes() {
|
||||
@Override
|
||||
public void call(ArrayList<Node> nodes) {
|
||||
launchShortestPathThread(new BellmanFordAlgorithm(
|
||||
new ShortestPathData(graph, nodes.get(0), nodes.get(1), mode)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
graphLockItems.add(wccItem);
|
||||
graphLockItems.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);
|
||||
|
||||
for (JMenuItem item: graphLockItems) {
|
||||
item.setEnabled(false);
|
||||
}
|
||||
|
||||
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(), 38));
|
||||
statusPanel.setLayout(new BorderLayout());
|
||||
|
||||
mapIdPanel = new JLabel();
|
||||
mapIdPanel.setHorizontalAlignment(SwingConstants.LEFT);
|
||||
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() {
|
||||
|
||||
@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) {
|
||||
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);
|
||||
statusPanel.add(threadPanel, BorderLayout.EAST);
|
||||
|
||||
return statusPanel;
|
||||
}
|
||||
|
||||
protected JPanel createTopPanel() {
|
||||
JPanel topPanel = new JPanel();
|
||||
|
||||
return topPanel;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
345
src/main/org/insa/graphics/drawing/BasicDrawing.java
Normal file
345
src/main/org/insa/graphics/drawing/BasicDrawing.java
Normal file
@@ -0,0 +1,345 @@
|
||||
package org.insa.graphics.drawing;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.geom.NoninvertibleTransformException;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import org.insa.graph.Arc;
|
||||
import org.insa.graph.Graph;
|
||||
import org.insa.graph.Node;
|
||||
import org.insa.graph.Path;
|
||||
import org.insa.graph.Point;
|
||||
|
||||
/**
|
||||
* Cette implementation de la classe Dessin produit vraiment un affichage (au
|
||||
* contraire de la classe DessinInvisible).
|
||||
*/
|
||||
|
||||
public class BasicDrawing extends JPanel implements Drawing {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 96779785877771827L;
|
||||
|
||||
// Default path color.
|
||||
public static final Color DEFAULT_PATH_COLOR = new Color(255, 0, 255);
|
||||
|
||||
// Default palette.
|
||||
public static final GraphPalette DEFAULT_PALETTE = new BasicGraphPalette();
|
||||
|
||||
// Default marker width
|
||||
private static final int DEFAULT_MARKER_WIDTH = 10;
|
||||
|
||||
//
|
||||
private final Graphics2D gr;
|
||||
|
||||
private double long1, long2, lat1, lat2;
|
||||
|
||||
// Width and height of the image
|
||||
private final int width, height;
|
||||
|
||||
//
|
||||
private Image image;
|
||||
private ZoomAndPanListener zoomAndPanListener;
|
||||
|
||||
// Mapping DrawingClickListener -> MouseEventListener
|
||||
private Map<DrawingClickListener, MouseListener> listenerMapping = new IdentityHashMap<>();
|
||||
|
||||
/**
|
||||
* Create a new BasicDrawing.
|
||||
*
|
||||
*/
|
||||
public BasicDrawing() {
|
||||
|
||||
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.long1 = -180;
|
||||
this.long2 = 180;
|
||||
this.lat1 = -90;
|
||||
this.lat2 = 90;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
protected void setBB(double long1, double long2, double lat1, double lat2) {
|
||||
|
||||
if (long1 > long2 || lat1 > lat2) {
|
||||
throw new Error("DessinVisible.setBB : mauvaises coordonnees.");
|
||||
}
|
||||
|
||||
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();
|
||||
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(double lon) {
|
||||
return (int) (width * (lon - this.long1) / (this.long2 - this.long1));
|
||||
}
|
||||
|
||||
private int projy(double lat) {
|
||||
return (int) (height * (1 - (lat - this.lat1) / (this.lat2 - this.lat1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDrawingClickListener(DrawingClickListener listener) {
|
||||
MouseListener mListener = new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent evt) {
|
||||
System.out.println(evt);
|
||||
try {
|
||||
listener.mouseClicked(getLongitudeLatitude(evt));
|
||||
}
|
||||
catch (NoninvertibleTransformException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
this.addMouseListener(mListener);
|
||||
this.listenerMapping.put(listener, mListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDrawingClickListener(DrawingClickListener listener) {
|
||||
this.removeMouseListener(this.listenerMapping.get(listener));
|
||||
}
|
||||
|
||||
protected void setWidth(int width) {
|
||||
this.gr.setStroke(new BasicStroke(width));
|
||||
}
|
||||
|
||||
protected void setColor(Color col) {
|
||||
this.gr.setColor(col);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.gr.setColor(Color.WHITE);
|
||||
this.gr.fillRect(0, 0, this.width, this.height);
|
||||
}
|
||||
|
||||
@Override
|
||||
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.repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLine(Point from, Point to, int width) {
|
||||
setWidth(width);
|
||||
drawLine(from, to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLine(Point from, Point to, int width, Color color) {
|
||||
setWidth(width);
|
||||
setColor(color);
|
||||
drawLine(from, to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawMarker(Point point) {
|
||||
drawPoint(point, DEFAULT_MARKER_WIDTH, this.gr.getColor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawMarker(Point point, Color color) {
|
||||
setColor(color);
|
||||
drawMarker(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPoint(Point point, int width, Color color) {
|
||||
setWidth(width);
|
||||
setColor(color);
|
||||
int x = this.projx(point.getLongitude()) - DEFAULT_MARKER_WIDTH / 2;
|
||||
int y = this.projy(point.getLatitude()) - DEFAULT_MARKER_WIDTH / 2;
|
||||
gr.fillOval(x, y, DEFAULT_MARKER_WIDTH, DEFAULT_MARKER_WIDTH);
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the given arc.
|
||||
*
|
||||
* @param arc
|
||||
* Arc to draw.
|
||||
* @param palette
|
||||
* Palette to use to retrieve color and width for arc, or null to use
|
||||
* current settings.
|
||||
*/
|
||||
public void drawArc(Arc arc, GraphPalette palette) {
|
||||
ArrayList<Point> pts = arc.getPoints();
|
||||
if (!pts.isEmpty()) {
|
||||
if (palette != null) {
|
||||
setColor(palette.getColorForType(arc.getInfo().getType()));
|
||||
setWidth(palette.getWidthForType(arc.getInfo().getType()));
|
||||
}
|
||||
Iterator<Point> it1 = pts.iterator();
|
||||
Point prev = it1.next();
|
||||
while (it1.hasNext()) {
|
||||
Point curr = it1.next();
|
||||
drawLine(prev, curr);
|
||||
prev = curr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the drawing for the given graph.
|
||||
*
|
||||
* @param graph
|
||||
*/
|
||||
public void initialize(Graph graph) {
|
||||
double minLon = Double.POSITIVE_INFINITY, minLat = Double.POSITIVE_INFINITY, maxLon = Double.NEGATIVE_INFINITY,
|
||||
maxLat = Double.NEGATIVE_INFINITY;
|
||||
for (Node node: graph.getNodes()) {
|
||||
Point pt = node.getPoint();
|
||||
if (pt.getLatitude() < minLat) {
|
||||
minLat = pt.getLatitude();
|
||||
}
|
||||
if (pt.getLatitude() > maxLat) {
|
||||
maxLat = pt.getLatitude();
|
||||
}
|
||||
if (pt.getLongitude() < minLon) {
|
||||
minLon = pt.getLongitude();
|
||||
}
|
||||
if (pt.getLongitude() > maxLon) {
|
||||
maxLon = pt.getLongitude();
|
||||
}
|
||||
}
|
||||
|
||||
double deltaLon = 0.02 * (maxLon - minLon), deltaLat = 0.02 * (maxLat - minLat);
|
||||
|
||||
setBB(minLon - deltaLon, maxLon + deltaLon, minLat - deltaLat, maxLat + deltaLat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGraph(Graph graph, GraphPalette palette) {
|
||||
clear();
|
||||
initialize(graph);
|
||||
for (Node node: graph.getNodes()) {
|
||||
for (Arc arc: node.getSuccessors()) {
|
||||
drawArc(arc, palette);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGraph(Graph graph) {
|
||||
drawGraph(graph, DEFAULT_PALETTE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPath(Path path, Color color, boolean markers) {
|
||||
setColor(color);
|
||||
setWidth(2);
|
||||
for (Arc arc: path.getArcs()) {
|
||||
drawArc(arc, null);
|
||||
}
|
||||
if (markers) {
|
||||
drawMarker(path.getOrigin().getPoint(), color);
|
||||
drawMarker(path.getDestination().getPoint(), color);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPath(Path path, Color color) {
|
||||
drawPath(path, color, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPath(Path path) {
|
||||
drawPath(path, DEFAULT_PATH_COLOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPath(Path path, boolean markers) {
|
||||
drawPath(path, DEFAULT_PATH_COLOR, markers);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void putText(Point point, String txt) {
|
||||
int x = this.projx(point.getLongitude());
|
||||
int y = this.projy(point.getLatitude());
|
||||
gr.drawString(txt, x, y);
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
}
|
97
src/main/org/insa/graphics/drawing/BasicGraphPalette.java
Normal file
97
src/main/org/insa/graphics/drawing/BasicGraphPalette.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package org.insa.graphics.drawing;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import org.insa.graph.RoadInformation.RoadType;
|
||||
|
||||
public class BasicGraphPalette implements GraphPalette {
|
||||
|
||||
// Color types for arc.
|
||||
static final Color motorway = Color.RED;
|
||||
static final Color bigroad = new Color(255, 105, 0);
|
||||
static final Color smallroad = new Color(255, 234, 0);
|
||||
static final Color coastline = Color.BLUE;
|
||||
|
||||
// Default point width
|
||||
static final int DEFAULT_POINT_WIDTH = 1;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public BasicGraphPalette() { }
|
||||
|
||||
@Override
|
||||
public int getDefaultPointWidth() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getDefaultPointColor() {
|
||||
return Color.GREEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColorForType(RoadType type) {
|
||||
Color color = Color.BLACK;
|
||||
switch (type) {
|
||||
case MOTORWAY:
|
||||
color = motorway;
|
||||
break;
|
||||
case TRUNK:
|
||||
case PRIMARY:
|
||||
case SECONDARY:
|
||||
case MOTORWAY_LINK:
|
||||
case TRUNK_LINK:
|
||||
case PRIMARY_LINK:
|
||||
color = bigroad;
|
||||
break;
|
||||
case SECONDARY_LINK:
|
||||
case TERTIARY:
|
||||
case RESIDENTIAL:
|
||||
case UNCLASSIFIED:
|
||||
case ROAD:
|
||||
case LIVING_STREET:
|
||||
case SERVICE:
|
||||
case ROUNDABOUT:
|
||||
color = smallroad;
|
||||
break;
|
||||
case COASTLINE:
|
||||
color = coastline;
|
||||
break;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidthForType(RoadType type) {
|
||||
int width = 1;
|
||||
switch (type) {
|
||||
case MOTORWAY:
|
||||
width = 2;
|
||||
break;
|
||||
case TRUNK:
|
||||
case PRIMARY:
|
||||
case SECONDARY:
|
||||
case MOTORWAY_LINK:
|
||||
case TRUNK_LINK:
|
||||
case PRIMARY_LINK:
|
||||
width = 1;
|
||||
break;
|
||||
case SECONDARY_LINK:
|
||||
case TERTIARY:
|
||||
case RESIDENTIAL:
|
||||
case UNCLASSIFIED:
|
||||
case ROAD:
|
||||
case LIVING_STREET:
|
||||
case SERVICE:
|
||||
case ROUNDABOUT:
|
||||
width = 1;
|
||||
break;
|
||||
case COASTLINE:
|
||||
width = 4;
|
||||
break;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package org.insa.graphics.drawing;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import org.insa.graph.RoadInformation.RoadType;
|
||||
|
||||
public class BlackAndWhiteGraphPalette extends BasicGraphPalette {
|
||||
|
||||
// Road colors (index
|
||||
private final static Color[] ROAD_COLOR_FROM_WIDTH = {
|
||||
null, new Color(140, 140, 140), new Color(80, 80, 80), new Color(40, 40, 40), new Color(30, 30, 30)
|
||||
};
|
||||
|
||||
@Override
|
||||
public Color getDefaultPointColor() {
|
||||
return Color.BLACK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColorForType(RoadType type) {
|
||||
int width = getWidthForType(type);
|
||||
return ROAD_COLOR_FROM_WIDTH[width];
|
||||
}
|
||||
|
||||
}
|
134
src/main/org/insa/graphics/drawing/Drawing.java
Normal file
134
src/main/org/insa/graphics/drawing/Drawing.java
Normal file
@@ -0,0 +1,134 @@
|
||||
package org.insa.graphics.drawing;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import org.insa.graph.Graph;
|
||||
import org.insa.graph.Path;
|
||||
import org.insa.graph.Point;
|
||||
|
||||
public interface Drawing {
|
||||
|
||||
/**
|
||||
* Add a listener to click to this drawing.
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public void addDrawingClickListener(DrawingClickListener listener);
|
||||
|
||||
/**
|
||||
* Remove the given listener from the drawing.
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public void removeDrawingClickListener(DrawingClickListener listener);
|
||||
|
||||
/**
|
||||
* Clear the drawing.
|
||||
*/
|
||||
public void clear();
|
||||
|
||||
/**
|
||||
* Draw a line between the two given points with the default color and width.
|
||||
*
|
||||
* @param from
|
||||
* @param to
|
||||
*/
|
||||
public void drawLine(Point from, Point to);
|
||||
|
||||
/**
|
||||
* Draw a line between the two given points with the default color and the given
|
||||
* width.
|
||||
*
|
||||
* @param from
|
||||
* @param to
|
||||
* @param width
|
||||
*/
|
||||
public void drawLine(Point from, Point to, int width);
|
||||
|
||||
/**
|
||||
* Draw a line between the two given points with the given color and the given
|
||||
* width.
|
||||
*
|
||||
* @param from
|
||||
* @param to
|
||||
* @param width
|
||||
* @param color
|
||||
*/
|
||||
public void drawLine(Point from, Point to, int width, Color color);
|
||||
|
||||
/**
|
||||
* Draw a marker at the given point with the default color.
|
||||
*
|
||||
* @param point
|
||||
*/
|
||||
public void drawMarker(Point point);
|
||||
|
||||
/**
|
||||
* Draw the given point with the given color.
|
||||
*
|
||||
* @param point
|
||||
*/
|
||||
public void drawMarker(Point point, Color color);
|
||||
|
||||
/**
|
||||
* Draw a point width the given width and color. Do not use this to mark
|
||||
* location, use drawMarker.
|
||||
*
|
||||
* @param point
|
||||
* @param width
|
||||
* @param color
|
||||
*/
|
||||
public void drawPoint(Point point, int width, Color color);
|
||||
|
||||
/**
|
||||
* Draw the given graph using the given palette.
|
||||
*
|
||||
* @param graph
|
||||
* @param palette
|
||||
*/
|
||||
public void drawGraph(Graph graph, GraphPalette palette);
|
||||
|
||||
/**
|
||||
* Draw the given graph using a default palette specific to the implementation.
|
||||
*
|
||||
* @param graph
|
||||
*/
|
||||
public void drawGraph(Graph graph);
|
||||
|
||||
/**
|
||||
* Draw a path using the given color.
|
||||
*
|
||||
* @param path
|
||||
* @param color
|
||||
* @param markers
|
||||
* Show origin and destination markers.
|
||||
*/
|
||||
public void drawPath(Path path, Color color, boolean markers);
|
||||
|
||||
/**
|
||||
* Draw a path using the given color with markers.
|
||||
*
|
||||
* @param path
|
||||
* @param color
|
||||
*/
|
||||
public void drawPath(Path path, Color color);
|
||||
|
||||
/**
|
||||
* Draw a path using a default color specific to the implementation
|
||||
*
|
||||
*
|
||||
* @param path
|
||||
* @param markers
|
||||
* Show origin and destination markers.
|
||||
*/
|
||||
public void drawPath(Path path, boolean markers);
|
||||
|
||||
/**
|
||||
* Draw a path using a default color specific to the implementation
|
||||
*
|
||||
*
|
||||
* @param path
|
||||
*/
|
||||
public void drawPath(Path path);
|
||||
|
||||
}
|
14
src/main/org/insa/graphics/drawing/DrawingClickListener.java
Normal file
14
src/main/org/insa/graphics/drawing/DrawingClickListener.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package org.insa.graphics.drawing;
|
||||
|
||||
import org.insa.graph.Point;
|
||||
|
||||
public interface DrawingClickListener {
|
||||
|
||||
/**
|
||||
* Event triggered when a click is made on the map.
|
||||
*
|
||||
* @param point
|
||||
*/
|
||||
public void mouseClicked(Point point);
|
||||
|
||||
}
|
33
src/main/org/insa/graphics/drawing/GraphPalette.java
Normal file
33
src/main/org/insa/graphics/drawing/GraphPalette.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package org.insa.graphics.drawing;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import org.insa.graph.RoadInformation.RoadType;
|
||||
|
||||
public interface GraphPalette {
|
||||
|
||||
/**
|
||||
* @return The default point width for this palette.
|
||||
*/
|
||||
public int getDefaultPointWidth();
|
||||
|
||||
/**
|
||||
* @return The default point color for this palette.
|
||||
*/
|
||||
public Color getDefaultPointColor();
|
||||
|
||||
/**
|
||||
* @param type Type of the road.
|
||||
*
|
||||
* @return Color associated to the given type of road.
|
||||
*/
|
||||
public Color getColorForType(RoadType type);
|
||||
|
||||
/**
|
||||
* @param type Type of the road.
|
||||
*
|
||||
* @return Width associated to the given type of road.
|
||||
*/
|
||||
public int getWidthForType(RoadType type);
|
||||
|
||||
}
|
315
src/main/org/insa/graphics/drawing/MapViewDrawing.java
Normal file
315
src/main/org/insa/graphics/drawing/MapViewDrawing.java
Normal file
@@ -0,0 +1,315 @@
|
||||
package org.insa.graphics.drawing;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
|
||||
import org.insa.graph.Arc;
|
||||
import org.insa.graph.Graph;
|
||||
import org.insa.graph.Path;
|
||||
import org.insa.graph.Point;
|
||||
import org.insa.graphics.drawing.utils.MarkerUtils;
|
||||
import org.mapsforge.core.graphics.Bitmap;
|
||||
import org.mapsforge.core.graphics.GraphicFactory;
|
||||
import org.mapsforge.core.graphics.Paint;
|
||||
import org.mapsforge.core.graphics.Style;
|
||||
import org.mapsforge.core.model.BoundingBox;
|
||||
import org.mapsforge.core.model.LatLong;
|
||||
import org.mapsforge.core.model.MapPosition;
|
||||
import org.mapsforge.core.util.LatLongUtils;
|
||||
import org.mapsforge.map.awt.graphics.AwtGraphicFactory;
|
||||
import org.mapsforge.map.awt.util.AwtUtil;
|
||||
import org.mapsforge.map.awt.view.MapView;
|
||||
import org.mapsforge.map.datastore.MapDataStore;
|
||||
import org.mapsforge.map.layer.Layers;
|
||||
import org.mapsforge.map.layer.cache.TileCache;
|
||||
import org.mapsforge.map.layer.hills.HillsRenderConfig;
|
||||
import org.mapsforge.map.layer.overlay.Marker;
|
||||
import org.mapsforge.map.layer.overlay.Polyline;
|
||||
import org.mapsforge.map.layer.renderer.TileRendererLayer;
|
||||
import org.mapsforge.map.model.DisplayModel;
|
||||
import org.mapsforge.map.model.MapViewPosition;
|
||||
import org.mapsforge.map.model.Model;
|
||||
import org.mapsforge.map.reader.MapFile;
|
||||
import org.mapsforge.map.rendertheme.InternalRenderTheme;
|
||||
|
||||
public class MapViewDrawing extends MapView implements Drawing {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 8606967833704938092L;
|
||||
|
||||
// Default path color.
|
||||
public static final Color DEFAULT_PATH_COLOR = new Color(66, 134, 244);
|
||||
|
||||
// Graphic factory.
|
||||
private static final GraphicFactory GRAPHIC_FACTORY = AwtGraphicFactory.INSTANCE;
|
||||
|
||||
// Default tile size.
|
||||
private static final int DEFAULT_TILE_SIZE = 512;
|
||||
|
||||
// List of listeners.
|
||||
private ArrayList<DrawingClickListener> drawingClickListeners = new ArrayList<>();
|
||||
|
||||
// Tile size.
|
||||
int tileSize;
|
||||
|
||||
// Extra layers...
|
||||
private static class FixedStrokeWidthLayer {
|
||||
public Paint paint;
|
||||
public int width;
|
||||
|
||||
public FixedStrokeWidthLayer(Paint paint, int width) {
|
||||
this.paint = paint;
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ArrayList<FixedStrokeWidthLayer> extraLayers = new ArrayList<>();
|
||||
|
||||
public MapViewDrawing() {
|
||||
getMapScaleBar().setVisible(true);
|
||||
this.tileSize = DEFAULT_TILE_SIZE;
|
||||
DisplayModel model = getModel().displayModel;
|
||||
model.setFixedTileSize(tileSize);
|
||||
|
||||
addMouseWheelListener(new MouseAdapter() {
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(MouseWheelEvent e) {
|
||||
for (FixedStrokeWidthLayer f: extraLayers) {
|
||||
f.paint.setStrokeWidth(getStrokeWidth(f.width));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
protected int convertColor(Color color) {
|
||||
return GRAPHIC_FACTORY.createColor(color.getAlpha(), color.getRed(), color.getGreen(), color.getBlue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param width
|
||||
* @return
|
||||
*/
|
||||
private int getStrokeWidth(int width) {
|
||||
byte zoomLevel = getModel().mapViewPosition.getZoomLevel();
|
||||
int mul = 2;
|
||||
if (zoomLevel < 8) {
|
||||
mul = 1;
|
||||
}
|
||||
else {
|
||||
mul += 2 * (zoomLevel - 8) / 3;
|
||||
}
|
||||
return width * mul;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param width
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
private Paint createPaintStroke(int width, Color color) {
|
||||
Paint paintStroke = AwtGraphicFactory.INSTANCE.createPaint();
|
||||
paintStroke.setStyle(Style.STROKE);
|
||||
if (width != 0) {
|
||||
paintStroke.setStrokeWidth(getStrokeWidth(width));
|
||||
}
|
||||
if (color != null) {
|
||||
paintStroke.setColor(convertColor(color));
|
||||
}
|
||||
return paintStroke;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
private File getMapsforgeFileFromGraph(Graph graph) {
|
||||
// TODO: Find a way to change this...
|
||||
Map<Integer, String> idToNames = new HashMap<Integer, String>();
|
||||
idToNames.put(0x100, "insa");
|
||||
idToNames.put(0x110, "paris");
|
||||
idToNames.put(0x200, "mayotte");
|
||||
idToNames.put(0x250, "newzealand");
|
||||
idToNames.put(0x300, "reunion");
|
||||
idToNames.put(0x400, "midip");
|
||||
idToNames.put(0x410, "morbihan");
|
||||
|
||||
File file = null;
|
||||
if (idToNames.containsKey(graph.getMapId())) {
|
||||
file = new File("Maps/" + idToNames.get(graph.getMapId()) + ".mapfg");
|
||||
if (!file.exists()) {
|
||||
file = new File("Maps/new/" + idToNames.get(graph.getMapId()) + ".mapfg");
|
||||
}
|
||||
}
|
||||
|
||||
if (file == null || !file.exists()) {
|
||||
JFileChooser fileChooser = new JFileChooser("Maps/");
|
||||
fileChooser.setFileFilter(new FileNameExtensionFilter("mapsforge files", "" + "mapfg"));
|
||||
if (fileChooser.showOpenDialog(this.getParent()) == JFileChooser.APPROVE_OPTION) {
|
||||
file = fileChooser.getSelectedFile();
|
||||
}
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
protected LatLong convertPoint(Point point) {
|
||||
return new LatLong(point.getLatitude(), point.getLongitude());
|
||||
}
|
||||
|
||||
private TileRendererLayer createTileRendererLayer(TileCache tileCache, MapDataStore mapDataStore,
|
||||
MapViewPosition mapViewPosition, HillsRenderConfig hillsRenderConfig) {
|
||||
TileRendererLayer tileRendererLayer = new TileRendererLayer(tileCache, mapDataStore, mapViewPosition, false,
|
||||
true, false, GRAPHIC_FACTORY, hillsRenderConfig) {
|
||||
@Override
|
||||
public boolean onTap(LatLong tapLatLong, org.mapsforge.core.model.Point layerXY,
|
||||
org.mapsforge.core.model.Point tapXY) {
|
||||
System.out.println("Tap on: " + tapLatLong);
|
||||
Point pt = new Point(tapLatLong.getLongitude(), tapLatLong.getLatitude());
|
||||
for (DrawingClickListener listener: MapViewDrawing.this.drawingClickListeners) {
|
||||
listener.mouseClicked(pt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
tileRendererLayer.setXmlRenderTheme(InternalRenderTheme.DEFAULT);
|
||||
return tileRendererLayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDrawingClickListener(DrawingClickListener listener) {
|
||||
this.drawingClickListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDrawingClickListener(DrawingClickListener listener) {
|
||||
this.drawingClickListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
getLayerManager().getLayers().clear();
|
||||
extraLayers.clear();
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLine(Point from, Point to) {
|
||||
drawLine(from, to, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLine(Point from, Point to, int width) {
|
||||
drawLine(from, to, width, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLine(Point from, Point to, int width, Color color) {
|
||||
Paint paintStroke = createPaintStroke(width, color);
|
||||
Polyline line = new Polyline(paintStroke, AwtGraphicFactory.INSTANCE);
|
||||
line.getLatLongs().add(convertPoint(from));
|
||||
line.getLatLongs().add(convertPoint(to));
|
||||
getLayerManager().getLayers().add(line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawMarker(Point point) {
|
||||
drawMarker(point, Color.GREEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawMarker(Point point, Color color) {
|
||||
Bitmap bitmap = MarkerUtils.getMarkerForColor(color);
|
||||
Marker marker = new Marker(convertPoint(point), bitmap, 0, -bitmap.getHeight() / 2);
|
||||
getLayerManager().getLayers().add(marker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPoint(Point point, int width, Color color) {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGraph(Graph graph, GraphPalette palette) {
|
||||
|
||||
File graphFile = getMapsforgeFileFromGraph(graph);
|
||||
|
||||
// Tile cache
|
||||
TileCache tileCache = AwtUtil.createTileCache(tileSize, getModel().frameBufferModel.getOverdrawFactor(), 1024,
|
||||
new File(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()));
|
||||
|
||||
// Layers
|
||||
Layers layers = getLayerManager().getLayers();
|
||||
|
||||
MapDataStore mapDataStore = new MapFile(graphFile);
|
||||
TileRendererLayer tileRendererLayer = createTileRendererLayer(tileCache, mapDataStore,
|
||||
getModel().mapViewPosition, null);
|
||||
layers.add(tileRendererLayer);
|
||||
BoundingBox boundingBox = mapDataStore.boundingBox();
|
||||
|
||||
final Model model = getModel();
|
||||
if (model.mapViewPosition.getZoomLevel() == 0 || !boundingBox.contains(model.mapViewPosition.getCenter())) {
|
||||
byte zoomLevel = LatLongUtils.zoomForBounds(model.mapViewDimension.getDimension(), boundingBox,
|
||||
model.displayModel.getTileSize());
|
||||
model.mapViewPosition.setMapPosition(new MapPosition(boundingBox.getCenterPoint(), zoomLevel));
|
||||
model.mapViewPosition.setZoomLevelMin(zoomLevel);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGraph(Graph graph) {
|
||||
drawGraph(graph, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPath(Path path, Color color, boolean markers) {
|
||||
Paint paintStroke = createPaintStroke(1, DEFAULT_PATH_COLOR);
|
||||
Polyline line = new Polyline(paintStroke, AwtGraphicFactory.INSTANCE);
|
||||
for (Arc arc: path.getArcs()) {
|
||||
ArrayList<Point> points = arc.getPoints();
|
||||
for (int i = 0; i < points.size(); ++i) {
|
||||
line.getLatLongs().add(new LatLong(points.get(i).getLatitude(), points.get(i).getLongitude()));
|
||||
}
|
||||
}
|
||||
getLayerManager().getLayers().add(line);
|
||||
extraLayers.add(new FixedStrokeWidthLayer(paintStroke, 1));
|
||||
if (markers) {
|
||||
drawMarker(path.getOrigin().getPoint(), DEFAULT_PATH_COLOR);
|
||||
drawMarker(path.getDestination().getPoint(), DEFAULT_PATH_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPath(Path path, Color color) {
|
||||
drawPath(path, color, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPath(Path path) {
|
||||
drawPath(path, DEFAULT_PATH_COLOR, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPath(Path path, boolean markers) {
|
||||
drawPath(path, DEFAULT_PATH_COLOR, markers);
|
||||
}
|
||||
|
||||
}
|
141
src/main/org/insa/graphics/drawing/ZoomAndPanListener.java
Normal file
141
src/main/org/insa/graphics/drawing/ZoomAndPanListener.java
Normal file
@@ -0,0 +1,141 @@
|
||||
package org.insa.graphics.drawing;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.NoninvertibleTransformException;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
public class ZoomAndPanListener implements MouseListener, MouseMotionListener, MouseWheelListener {
|
||||
public static final int DEFAULT_MIN_ZOOM_LEVEL = -20;
|
||||
public static final int DEFAULT_MAX_ZOOM_LEVEL = 10;
|
||||
public static final double DEFAULT_ZOOM_MULTIPLICATION_FACTOR = 1.2;
|
||||
|
||||
private Component targetComponent;
|
||||
|
||||
private int zoomLevel = 0;
|
||||
private int minZoomLevel = DEFAULT_MIN_ZOOM_LEVEL;
|
||||
private int maxZoomLevel = DEFAULT_MAX_ZOOM_LEVEL;
|
||||
private double zoomMultiplicationFactor = DEFAULT_ZOOM_MULTIPLICATION_FACTOR;
|
||||
|
||||
private Point dragStartScreen;
|
||||
private Point dragEndScreen;
|
||||
private AffineTransform coordTransform = new AffineTransform();
|
||||
|
||||
public ZoomAndPanListener(Component targetComponent) {
|
||||
this.targetComponent = targetComponent;
|
||||
}
|
||||
|
||||
public ZoomAndPanListener(Component targetComponent, int minZoomLevel, int maxZoomLevel, double zoomMultiplicationFactor) {
|
||||
this.targetComponent = targetComponent;
|
||||
this.minZoomLevel = minZoomLevel;
|
||||
this.maxZoomLevel = maxZoomLevel;
|
||||
this.zoomMultiplicationFactor = zoomMultiplicationFactor;
|
||||
}
|
||||
|
||||
public void translate(double dx, double dy) {
|
||||
coordTransform.translate(dx, dy);
|
||||
targetComponent.repaint();
|
||||
}
|
||||
|
||||
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void mousePressed(MouseEvent e) {
|
||||
dragStartScreen = e.getPoint();
|
||||
dragEndScreen = null;
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
// moveCamera(e);
|
||||
}
|
||||
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void mouseExited(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
moveCamera(e);
|
||||
}
|
||||
|
||||
public void mouseWheelMoved(MouseWheelEvent e) {
|
||||
zoomCamera(e);
|
||||
}
|
||||
|
||||
private void moveCamera(MouseEvent e) {
|
||||
try {
|
||||
dragEndScreen = e.getPoint();
|
||||
Point2D.Float dragStart = transformPoint(dragStartScreen);
|
||||
Point2D.Float dragEnd = transformPoint(dragEndScreen);
|
||||
double dx = dragEnd.getX() - dragStart.getX();
|
||||
double dy = dragEnd.getY() - dragStart.getY();
|
||||
coordTransform.translate(dx, dy);
|
||||
dragStartScreen = dragEndScreen;
|
||||
dragEndScreen = null;
|
||||
targetComponent.repaint();
|
||||
} catch (NoninvertibleTransformException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void zoomCamera(MouseWheelEvent e) {
|
||||
try {
|
||||
int wheelRotation = e.getWheelRotation();
|
||||
Point p = e.getPoint();
|
||||
if (wheelRotation > 0) {
|
||||
if (zoomLevel < maxZoomLevel) {
|
||||
zoomLevel++;
|
||||
Point2D p1 = transformPoint(p);
|
||||
coordTransform.scale(1 / zoomMultiplicationFactor, 1 / zoomMultiplicationFactor);
|
||||
Point2D p2 = transformPoint(p);
|
||||
coordTransform.translate(p2.getX() - p1.getX(), p2.getY() - p1.getY());
|
||||
targetComponent.repaint();
|
||||
}
|
||||
} else {
|
||||
if (zoomLevel > minZoomLevel) {
|
||||
zoomLevel--;
|
||||
Point2D p1 = transformPoint(p);
|
||||
coordTransform.scale(zoomMultiplicationFactor, zoomMultiplicationFactor);
|
||||
Point2D p2 = transformPoint(p);
|
||||
coordTransform.translate(p2.getX() - p1.getX(), p2.getY() - p1.getY());
|
||||
targetComponent.repaint();
|
||||
}
|
||||
}
|
||||
} catch (NoninvertibleTransformException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private Point2D.Float transformPoint(Point p1) throws NoninvertibleTransformException {
|
||||
|
||||
AffineTransform inverse = coordTransform.createInverse();
|
||||
|
||||
Point2D.Float p2 = new Point2D.Float();
|
||||
inverse.transform(p1, p2);
|
||||
return p2;
|
||||
}
|
||||
|
||||
public int getZoomLevel() {
|
||||
return zoomLevel;
|
||||
}
|
||||
|
||||
|
||||
public void setZoomLevel(int zoomLevel) {
|
||||
this.zoomLevel = zoomLevel;
|
||||
}
|
||||
|
||||
|
||||
public AffineTransform getCoordTransform() {
|
||||
return coordTransform;
|
||||
}
|
||||
|
||||
public void setCoordTransform(AffineTransform coordTransform) {
|
||||
this.coordTransform = coordTransform;
|
||||
}
|
||||
}
|
107
src/main/org/insa/graphics/drawing/utils/MarkerUtils.java
Normal file
107
src/main/org/insa/graphics/drawing/utils/MarkerUtils.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package org.insa.graphics.drawing.utils;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import org.mapsforge.core.graphics.Bitmap;
|
||||
import org.mapsforge.map.awt.graphics.AwtBitmap;
|
||||
|
||||
public class MarkerUtils {
|
||||
|
||||
/**
|
||||
* Return a color mapping with the given color as the main color.
|
||||
*
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
protected static Color[] getColorMapping(Color color) {
|
||||
return new Color[] { new Color(0, 0, 0, 0), color, Color.BLACK };
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Bitmap representing a marker of the given color.
|
||||
*
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
public static Bitmap getMarkerForColor(Color color) {
|
||||
// create image
|
||||
BufferedImage image = new BufferedImage(MARKER_MASK[0].length, MARKER_MASK.length, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
Color[] map = getColorMapping(color);
|
||||
for (int i = 0; i < image.getHeight(); ++i) {
|
||||
for (int j = 0; j < image.getWidth(); ++j) {
|
||||
image.setRGB(j, i, map[MARKER_MASK[i][j]].getRGB());
|
||||
}
|
||||
}
|
||||
|
||||
// Create Bitmap and return it.
|
||||
return new AwtBitmap(image);
|
||||
}
|
||||
|
||||
// Mask
|
||||
private static byte[][] MARKER_MASK = new byte[][] {
|
||||
// @formatter:off
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0},
|
||||
{0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0},
|
||||
{0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0},
|
||||
{0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0},
|
||||
{0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0},
|
||||
{0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0},
|
||||
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
|
||||
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
|
||||
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
|
||||
{1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1},
|
||||
{1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1},
|
||||
{1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1},
|
||||
{1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1},
|
||||
{1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1},
|
||||
{1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1},
|
||||
{1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1},
|
||||
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
|
||||
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
|
||||
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
|
||||
{0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0},
|
||||
{0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0},
|
||||
{0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0},
|
||||
{0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0},
|
||||
{0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0},
|
||||
{0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0},
|
||||
{0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user