From d146c68bab35b386522952176d0a0fc58277a4b7 Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 2 Nov 2018 15:43:23 +0100 Subject: [PATCH] Initial commit --- .gitignore | 2 + pom.xml | 84 ++++++++ .../java/fr/klemek/mapping/FileUtils.java | 42 ++++ .../fr/klemek/mapping/KeyEventListener.java | 102 +++++++++ src/main/java/fr/klemek/mapping/Launch.java | 13 ++ .../java/fr/klemek/mapping/MainPanel.java | 193 ++++++++++++++++++ .../java/fr/klemek/mapping/MainWindow.java | 98 +++++++++ src/main/java/fr/klemek/mapping/Map.java | 77 +++++++ .../java/fr/klemek/mapping/RefreshThread.java | 30 +++ src/main/resources/logging.properties | 6 + src/test/java/fr/klemek/mapping/MapTest.java | 20 ++ 11 files changed, 667 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/fr/klemek/mapping/FileUtils.java create mode 100644 src/main/java/fr/klemek/mapping/KeyEventListener.java create mode 100644 src/main/java/fr/klemek/mapping/Launch.java create mode 100644 src/main/java/fr/klemek/mapping/MainPanel.java create mode 100644 src/main/java/fr/klemek/mapping/MainWindow.java create mode 100644 src/main/java/fr/klemek/mapping/Map.java create mode 100644 src/main/java/fr/klemek/mapping/RefreshThread.java create mode 100644 src/main/resources/logging.properties create mode 100644 src/test/java/fr/klemek/mapping/MapTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b5cacdd --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/* +target/* \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..b94333a --- /dev/null +++ b/pom.xml @@ -0,0 +1,84 @@ + + + 4.0.0 + + fr.klemek + mapping + 1.0 + + + + fr.klemek + https://github.com/klemek/mvn-repo/raw/master + + + + + + fr.klemek + simple-logger + 1.3 + + + junit + junit + 4.12 + test + + + + + target/${project.artifactId}/WEB-INF/classes + src/main/java + src/test/java + + + src/main/resources + + **/*.java + + + + src/test/resources + + **/*.java + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + + + + maven-assembly-plugin + + + + fr.klemek.mapping.Launch + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + \ No newline at end of file diff --git a/src/main/java/fr/klemek/mapping/FileUtils.java b/src/main/java/fr/klemek/mapping/FileUtils.java new file mode 100644 index 0000000..2487bce --- /dev/null +++ b/src/main/java/fr/klemek/mapping/FileUtils.java @@ -0,0 +1,42 @@ +package fr.klemek.mapping; + +import fr.klemek.logger.Logger; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +final class FileUtils { + + private FileUtils() { + + } + + static boolean save(String fileName, String content) { + try (BufferedWriter bw = new BufferedWriter(new FileWriter(fileName))) { + bw.write(content); + return true; + } catch (IOException ex) { + Logger.log(ex); + return false; + } + } + + static String open(String fileName) { + try (BufferedReader bw = new BufferedReader(new FileReader(fileName))) { + StringBuilder output = new StringBuilder(); + String line; + while ((line = bw.readLine()) != null) { + output.append(line); + output.append('\n'); + } + return output.toString(); + } catch (IOException ex) { + Logger.log(ex); + return null; + } + } + +} diff --git a/src/main/java/fr/klemek/mapping/KeyEventListener.java b/src/main/java/fr/klemek/mapping/KeyEventListener.java new file mode 100644 index 0000000..d0ede56 --- /dev/null +++ b/src/main/java/fr/klemek/mapping/KeyEventListener.java @@ -0,0 +1,102 @@ +package fr.klemek.mapping; + +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +import javax.swing.*; + +public class KeyEventListener implements KeyListener { + + private static final float RATIO_STEP = 1.05f; + private static final float ANGLE_STEP = (float) (Math.PI / 64f); + + private MainPanel mp; + private JPanel infoPanel; + + KeyEventListener(MainPanel mp, JPanel infoPanel) { + this.mp = mp; + this.infoPanel = infoPanel; + } + + @Override + public void keyTyped(KeyEvent e) { + //ignored + } + + @Override + public void keyPressed(KeyEvent e) { + switch (e.getKeyCode()) { + case KeyEvent.VK_BACK_SPACE: + int n = JOptionPane.showConfirmDialog( + this.mp, + "Are you sure you want to erase everything ?", + "Warning", + JOptionPane.YES_NO_OPTION); + if (n == JOptionPane.YES_OPTION) { + this.mp.getMap().reset(); + this.mp.setRatio(MainPanel.DEFAULT_RATIO); + this.mp.setAngle(0f); + } + break; + case KeyEvent.VK_0: + case KeyEvent.VK_NUMPAD0: + this.infoPanel.setVisible(!this.infoPanel.isVisible()); + break; + case KeyEvent.VK_1: + case KeyEvent.VK_NUMPAD1: + this.mp.setShowGrid(!this.mp.isShowGrid()); + break; + case KeyEvent.VK_2: + case KeyEvent.VK_NUMPAD2: + this.mp.getMap().randomize(); + break; + case KeyEvent.VK_PAGE_UP: + this.mp.getMap().changeSize(this.mp.getMap().size() + 1); + break; + case KeyEvent.VK_PAGE_DOWN: + this.mp.getMap().changeSize(this.mp.getMap().size() - 1); + break; + case KeyEvent.VK_UP: + this.mp.setRatio(this.mp.getRatio() / RATIO_STEP); + break; + case KeyEvent.VK_DOWN: + this.mp.setRatio(this.mp.getRatio() * RATIO_STEP); + break; + case KeyEvent.VK_LEFT: + this.mp.setAngle(this.mp.getAngle() + ANGLE_STEP); + break; + case KeyEvent.VK_RIGHT: + this.mp.setAngle(this.mp.getAngle() - ANGLE_STEP); + break; + case KeyEvent.VK_S: + if (e.isControlDown()) { + int n2 = JOptionPane.showConfirmDialog( + this.mp, + "Do you want to save ?", + "Save", + JOptionPane.YES_NO_OPTION); + if (n2 == JOptionPane.YES_OPTION) { + if (FileUtils.save(MainWindow.FILE_NAME, this.mp.getMap().toString())) { + JOptionPane.showInternalMessageDialog(this.mp, + "Saved as '" + MainWindow.FILE_NAME + "'", + "Saved", + JOptionPane.INFORMATION_MESSAGE); + } else { + JOptionPane.showInternalMessageDialog(this.mp, + "Error while saving", + "Error", + JOptionPane.ERROR_MESSAGE); + } + } + } + break; + default: + break; + } + } + + @Override + public void keyReleased(KeyEvent e) { + //ignored + } +} diff --git a/src/main/java/fr/klemek/mapping/Launch.java b/src/main/java/fr/klemek/mapping/Launch.java new file mode 100644 index 0000000..c255cd3 --- /dev/null +++ b/src/main/java/fr/klemek/mapping/Launch.java @@ -0,0 +1,13 @@ +package fr.klemek.mapping; + +import fr.klemek.logger.Logger; + +import java.awt.*; + +public class Launch { + + public static void main(String... args) { + Logger.init("logging.properties"); + EventQueue.invokeLater(MainWindow::new); + } +} diff --git a/src/main/java/fr/klemek/mapping/MainPanel.java b/src/main/java/fr/klemek/mapping/MainPanel.java new file mode 100644 index 0000000..006ee5f --- /dev/null +++ b/src/main/java/fr/klemek/mapping/MainPanel.java @@ -0,0 +1,193 @@ +package fr.klemek.mapping; + +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; + +import javax.swing.*; + +class MainPanel extends JPanel { + + final static float DEFAULT_RATIO = .5f; + private final static float SIZE = 1.2f; + private float ratio = DEFAULT_RATIO; + + private Map map; + private int w, h; + private int cx, cy; + private float mw, mh; + private int sx = -1, sy = -1; + + private int[][] xs; + private int[][] ys; + + private boolean showGrid; + private float angle; + + MainPanel(Map map) { + this.map = map; + + this.addMouseMotionListener(new MouseMotionListener() { + @Override + public void mouseDragged(MouseEvent e) { + //ignored + } + + @Override + public void mouseMoved(MouseEvent e) { + sx = -1; + sy = -1; + if (MainPanel.this.xs != null) { + for (int x = 0; x < MainPanel.this.map.size(); x++) + for (int y = 0; y < MainPanel.this.map.size(); y++) + if (e.getPoint().distance(MainPanel.this.xs[x][y], MainPanel.this.ys[x][y]) < 6) { + sx = x; + sy = y; + return; + } + } + } + }); + this.addMouseWheelListener(e -> { + if (sx >= 0 && sy >= 0) { + if (e.isShiftDown()) { + for (int dx = -1; dx < 2; dx++) + for (int dy = -1; dy < 2; dy++) + if (sx + dx >= 0 && sx + dx < MainPanel.this.map.size() + && sy + dy >= 0 && sy + dy < MainPanel.this.map.size()) + MainPanel.this.map.set(sx + dx, sy + dy, MainPanel.this.map.get(sx + dx, sy + dy) + + (e.getPreciseWheelRotation() < 0 ? 1f : -1f) + * (e.isControlDown() ? 1f : .1f)); + } else { + MainPanel.this.map.set(sx, sy, MainPanel.this.map.get(sx, sy) + + (e.getPreciseWheelRotation() < 0 ? 1f : -1f) + * (e.isControlDown() ? 1f : .1f)); + } + } + }); + } + + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHints(new RenderingHints( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON)); + + this.w = this.getWidth(); + this.h = this.getHeight(); + this.cx = Math.round(this.w / 2f); + this.cy = Math.round(this.h / 2f); + if (this.w > this.h) { + this.mh = this.h * SIZE * ratio; + this.mw = mh / ratio; + } else { + this.mw = this.w * SIZE; + this.mh = mw * ratio; + } + + g2.clearRect(0, 0, this.w, this.h); + drawMap(g2); + } + + private void drawMap(Graphics2D g2) { + g2.setStroke(new BasicStroke(1f)); + + int x0 = Math.round(this.cx - this.mw / 2f); + + this.xs = new int[this.map.size()][this.map.size()]; + this.ys = new int[this.map.size()][this.map.size()]; + + float xstep = this.mw / (2 * (this.map.size() - 1)); + float ystep = this.mh / (2 * (this.map.size() - 1)); + + g2.setColor(Color.GRAY); + + for (int x = 0; x < this.map.size(); x++) { + for (int y = 0; y < this.map.size(); y++) { + Point p = getPoint(x0, xstep, ystep, x, y, this.map.get(x, y)); + + this.xs[x][y] = p.x; + this.ys[x][y] = p.y; + + if (this.showGrid) { + Point p0 = getPoint(x0, xstep, ystep, x, y, 0); + if (x > 0) { + Point p1 = getPoint(x0, xstep, ystep, x - 1, y, 0); + g2.drawLine(p0.x, p0.y, p1.x, p1.y); + } + if (y > 0) { + Point p1 = getPoint(x0, xstep, ystep, x, y - 1, 0); + g2.drawLine(p0.x, p0.y, p1.x, p1.y); + } + g2.drawLine(p0.x, p0.y, p.x, p.y); + } + } + } + + + g2.setColor(Color.BLACK); + + for (int x = 0; x < this.map.size(); x++) { + for (int y = 0; y < this.map.size(); y++) { + if (x > 0) + g2.drawLine(this.xs[x - 1][y], this.ys[x - 1][y], this.xs[x][y], this.ys[x][y]); + if (y > 0) + g2.drawLine(this.xs[x][y - 1], this.ys[x][y - 1], this.xs[x][y], this.ys[x][y]); + } + } + + g2.setColor(Color.RED); + + if (sx >= 0 && sy >= 0) { + if (sx > 0) + g2.drawLine(this.xs[sx - 1][sy], this.ys[sx - 1][sy], this.xs[sx][sy], this.ys[sx][sy]); + if (sy > 0) + g2.drawLine(this.xs[sx][sy - 1], this.ys[sx][sy - 1], this.xs[sx][sy], this.ys[sx][sy]); + if (sx < this.map.size() - 1) + g2.drawLine(this.xs[sx + 1][sy], this.ys[sx + 1][sy], this.xs[sx][sy], this.ys[sx][sy]); + if (sy < this.map.size() - 1) + g2.drawLine(this.xs[sx][sy + 1], this.ys[sx][sy + 1], this.xs[sx][sy], this.ys[sx][sy]); + } + } + + private Point getPoint(int x0, float xstep, float ystep, int x, int y, float value) { + float center = this.map.size() / 2f; + double r = Math.sqrt(Math.pow(x - center, 2) + Math.pow(y - center, 2)); + double a = Math.atan2(y - center, x - center); + double x1 = center + r * Math.cos(a + this.angle); + double y1 = center + r * Math.sin(a + this.angle); + return new Point( + Math.round(x0 + xstep * (float) x1 + xstep * (float) y1), + Math.round(this.cy - ystep * (float) x1 + ystep * (float) y1 - ystep * value) + ); + } + + float getRatio() { + return ratio; + } + + void setRatio(float ratio) { + this.ratio = ratio; + } + + boolean isShowGrid() { + return showGrid; + } + + void setShowGrid(boolean showGrid) { + this.showGrid = showGrid; + } + + float getAngle() { + return angle; + } + + void setAngle(float angle) { + this.angle = angle; + } + + Map getMap() { + return map; + } +} diff --git a/src/main/java/fr/klemek/mapping/MainWindow.java b/src/main/java/fr/klemek/mapping/MainWindow.java new file mode 100644 index 0000000..ee838e9 --- /dev/null +++ b/src/main/java/fr/klemek/mapping/MainWindow.java @@ -0,0 +1,98 @@ +package fr.klemek.mapping; + +import java.awt.*; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; + +import javax.swing.*; + +class MainWindow extends JFrame { + + static final String FILE_NAME = "mapping.csv"; + private static final String VERSION = "1.0"; + private static final int DEFAULT_SIZE = 17; + private static final String INFO_TEXT = "" + + "" + + "

INFO

" + + "[Mouse wheel] - Set height (small)
" + + "[CTRL+Mouse wheel] - Set height (big)
" + + "[SHIFT+Mouse wheel] - Set surrounding heights
" + + "[BACKSPACE] - Erase everything
" + + "[ARROWS] - Move view
" + + "[PAGE UP/DOWN] - Change size
" + + "[CTRL+S] - Save current work" + + "[0] - Show hide this info
" + + "[1] - Show grid
" + + "[2] - Add random
"; + private MainPanel mp; + private RefreshThread refresh; + + + MainWindow() { + Map m = new Map(DEFAULT_SIZE); + String saved = FileUtils.open(FILE_NAME); + if (saved != null) + m = new Map(saved); + this.mp = new MainPanel(m); + this.refresh = new RefreshThread(this.mp, 100); + this.initWindow(); + } + + private void initWindow() { + this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + this.setResizable(true); + this.setTitle("Mapping v" + VERSION); + this.setSize(new Dimension(800, 600)); + + this.getContentPane().setLayout(new BorderLayout(0, 0)); + + this.setLocationRelativeTo(null); + this.setVisible(true); + + JLayeredPane layers = new JLayeredPane(); + layers.setLayout(null); + layers.add(this.mp, (Integer) 0); + + JPanel infoLayer = new JPanel(new BorderLayout(0, 0)); + infoLayer.setBounds(10, 10, 800, 600); + infoLayer.setOpaque(false); + JLabel info = new JLabel(); + info.setText(INFO_TEXT); + infoLayer.add(info, BorderLayout.NORTH); + + layers.add(infoLayer, (Integer) 1); + + this.mp.setSize(new Dimension(800, 600)); + + this.getContentPane().add(layers, BorderLayout.CENTER); + + this.revalidate(); + this.repaint(); + + this.refresh.start(); + this.addKeyListener(new KeyEventListener(this.mp, infoLayer)); + + this.addComponentListener(new ComponentListener() { + @Override + public void componentResized(ComponentEvent e) { + MainWindow.this.mp.setSize(MainWindow.this.getSize()); + } + + @Override + public void componentMoved(ComponentEvent e) { + //ignored + } + + @Override + public void componentShown(ComponentEvent e) { + //ignored + } + + @Override + public void componentHidden(ComponentEvent e) { + //ignored + } + }); + } + +} diff --git a/src/main/java/fr/klemek/mapping/Map.java b/src/main/java/fr/klemek/mapping/Map.java new file mode 100644 index 0000000..24481e4 --- /dev/null +++ b/src/main/java/fr/klemek/mapping/Map.java @@ -0,0 +1,77 @@ +package fr.klemek.mapping; + +import java.util.Random; + +class Map { + + private float[][] m; + + Map(String input) { + String[] lines = input.split("\n"); + this.m = new float[lines.length][lines.length]; + for (int x = 0; x < lines.length; x++) { + String[] lineSplit = lines[x].split(";"); + for (int y = 0; y < Math.min(lines.length, lineSplit.length); y++) { + try { + this.m[x][y] = Float.parseFloat(lineSplit[y]); + } catch (NumberFormatException ignored) { + } + } + } + } + + Map(int size) { + this.m = new float[size][size]; + } + + int size() { + return this.m.length; + } + + float get(int x, int y) { + if (x < 0 || y < 0 || x >= this.m.length || y >= this.m.length) + return 0f; + return this.m[x][y]; + } + + void set(int x, int y, float value) { + if (x < 0 || y < 0 || x >= this.m.length || y >= this.m.length) + return; + this.m[x][y] = value; + } + + void reset() { + this.m = new float[this.m.length][this.m.length]; + } + + void changeSize(int newSize) { + if (newSize > 1) { + float[][] m2 = new float[newSize][newSize]; + for (int x = 0; x < newSize; x++) + for (int y = 0; y < newSize; y++) + if (x < this.m.length && y < this.m.length) + m2[x][y] = this.m[x][y]; + this.m = m2; + } + } + + void randomize() { + Random r = new Random(); + for (int x = 0; x < this.m.length; x++) + for (int y = 0; y < this.m.length; y++) + this.m[x][y] += r.nextFloat() - 0.5f; + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + for (int x = 0; x < this.size(); x++) { + for (int y = 0; y < this.size(); y++) { + output.append(this.m[x][y]); + output.append(';'); + } + output.append('\n'); + } + return output.toString(); + } +} diff --git a/src/main/java/fr/klemek/mapping/RefreshThread.java b/src/main/java/fr/klemek/mapping/RefreshThread.java new file mode 100644 index 0000000..d1007a1 --- /dev/null +++ b/src/main/java/fr/klemek/mapping/RefreshThread.java @@ -0,0 +1,30 @@ +package fr.klemek.mapping; + +import fr.klemek.logger.Logger; + +import java.awt.*; + +import javax.swing.*; + +class RefreshThread extends Thread { + + private JPanel panel; + private int time; + + public RefreshThread(JPanel panel, int time) { + this.panel = panel; + this.time = time; + } + + @Override + public void run() { + while (!this.isInterrupted()) { + try { + EventQueue.invokeLater(this.panel::repaint); + Thread.sleep(time); + } catch (Exception e) { + Logger.log(e); + } + } + } +} diff --git a/src/main/resources/logging.properties b/src/main/resources/logging.properties new file mode 100644 index 0000000..0b0e8d0 --- /dev/null +++ b/src/main/resources/logging.properties @@ -0,0 +1,6 @@ +handlers=java.util.logging.ConsoleHandler +.level=WARNING +java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +java.util.logging.SimpleFormatter.format=[%1$tF %1$tT][%4$s]%5$s %n +app_name=SortedGallery +default_package=fr.klemek.mapping \ No newline at end of file diff --git a/src/test/java/fr/klemek/mapping/MapTest.java b/src/test/java/fr/klemek/mapping/MapTest.java new file mode 100644 index 0000000..d7cd2c2 --- /dev/null +++ b/src/test/java/fr/klemek/mapping/MapTest.java @@ -0,0 +1,20 @@ +package fr.klemek.mapping; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class MapTest { + + @Test + public void testToString() { + Map m = new Map(15); + m.randomize(); + assertEquals(15, m.size()); + Map m2 = new Map(m.toString()); + assertEquals(m.size(), m2.size()); + for (int x = 0; x < m.size(); x++) + for (int y = 0; y < m.size(); y++) + assertEquals(m.get(x, y), m2.get(x, y), 0.001f); + } +} \ No newline at end of file