├── .gitignore ├── .settings ├── org.eclipse.m2e.core.prefs └── org.eclipse.jdt.core.prefs ├── src └── io │ └── x666c │ └── evoimg │ ├── internal │ ├── Vector.java │ ├── Polygon.java │ ├── Comparator.java │ ├── Population.java │ └── Polygonizer.java │ ├── gui │ ├── DisplayCanvas.java │ ├── ControlsPanel.java │ └── InfoPane.java │ ├── util │ └── SlideShowViewer.java │ ├── web │ └── WebStats.java │ ├── EvoGui.java │ └── EvoImage.java ├── .project ├── .classpath ├── LICENSE ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /target/ 3 | /dependency-reduced-pom.xml 4 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=false 4 | version=1 5 | -------------------------------------------------------------------------------- /src/io/x666c/evoimg/internal/Vector.java: -------------------------------------------------------------------------------- 1 | package io.x666c.evoimg.internal; 2 | 3 | public class Vector { 4 | 5 | public int x, y; 6 | 7 | public Vector(int x, int y) { 8 | this.x = x; 9 | this.y = y; 10 | } 11 | 12 | public static Vector create(int x, int y) { 13 | return new Vector(x, y); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | EvoImage 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.m2e.core.maven2Nature 21 | org.eclipse.jdt.core.javanature 22 | 23 | 24 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.8 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled 12 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 13 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 14 | org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning 15 | org.eclipse.jdt.core.compiler.release=enabled 16 | org.eclipse.jdt.core.compiler.source=1.8 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 0x666c 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/io/x666c/evoimg/gui/DisplayCanvas.java: -------------------------------------------------------------------------------- 1 | package io.x666c.evoimg.gui; 2 | 3 | import java.awt.Color; 4 | import java.awt.Graphics; 5 | import java.awt.Graphics2D; 6 | import java.awt.image.BufferedImage; 7 | 8 | import javax.swing.JPanel; 9 | 10 | public class DisplayCanvas extends JPanel { 11 | 12 | public static int DISPLAY_WIDTH = 300; 13 | public static int DISPLAY_HEIGHT = 300; 14 | 15 | public static final int DISPLAY_SIZE_LIMIT = 500; 16 | 17 | public DisplayCanvas() { 18 | setBounds(10, 10, DISPLAY_WIDTH, DISPLAY_HEIGHT); 19 | setBackground(new Color(0xd3d3d3)); 20 | 21 | img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB); 22 | Graphics2D g = img.createGraphics(); 23 | g.setColor(getBackground()); 24 | g.fillRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); 25 | g.dispose(); 26 | } 27 | 28 | protected void paintComponent(Graphics g) { 29 | super.paintComponent(g); 30 | g.drawImage(img, 0, 0, img.getWidth(), img.getHeight(), null); 31 | } 32 | 33 | private volatile BufferedImage img; 34 | 35 | public void drawImg(BufferedImage img) { 36 | this.img = img; 37 | repaint(); 38 | } 39 | 40 | public void resize() { 41 | setBounds(10, 10, DISPLAY_WIDTH, DISPLAY_HEIGHT); 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/io/x666c/evoimg/util/SlideShowViewer.java: -------------------------------------------------------------------------------- 1 | package io.x666c.evoimg.util; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Dimension; 5 | import java.awt.image.BufferedImage; 6 | import java.io.IOException; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.nio.file.Paths; 10 | import java.util.Comparator; 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | import javax.imageio.ImageIO; 15 | import javax.swing.ImageIcon; 16 | import javax.swing.JFrame; 17 | import javax.swing.JLabel; 18 | import javax.swing.SwingUtilities; 19 | 20 | public class SlideShowViewer { 21 | 22 | public static void main(String[] args) throws Exception { 23 | List files = Files.list(Paths.get("C:\\Users\\Jiftoo\\Desktop\\autosavedImages\\dio")).collect(Collectors.toList()); 24 | 25 | files.sort(new Comparator() { 26 | public int compare(Path o1, Path o2) { 27 | try { 28 | int n1 = Integer.parseInt(o1.getFileName().toString().split("\\.")[0]);//.replace("gen", "")); 29 | int n2 = Integer.parseInt(o2.getFileName().toString().split("\\.")[0]);//.replace("gen", "")); 30 | return n1 - n2; 31 | } catch (Exception e) { 32 | return 0; 33 | } 34 | }; 35 | }); 36 | 37 | List imgs = files.stream().map(p -> { 38 | try { 39 | return ImageIO.read(p.toFile()); 40 | } catch (IOException e) { 41 | e.printStackTrace(); 42 | } 43 | return null; 44 | }).collect(Collectors.toList()); 45 | 46 | JFrame f = new JFrame(); 47 | JLabel l = new JLabel(); 48 | l.setPreferredSize(new Dimension(imgs.get(0).getWidth(), imgs.get(0).getHeight())); 49 | f.setLayout(new BorderLayout()); 50 | f.add(l, BorderLayout.CENTER); 51 | f.pack(); 52 | f.setLocationRelativeTo(null); 53 | f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 54 | f.setVisible(true); 55 | for (BufferedImage bi : imgs) { 56 | SwingUtilities.invokeAndWait(() -> { 57 | l.setIcon(new ImageIcon(bi)); 58 | }); 59 | Thread.sleep(16); 60 | } 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | ImagePolygonizer 6 | ImagePolygonizer 7 | 1.4.5 8 | 9 | 10 | src 11 | 12 | 13 | 14 | maven-compiler-plugin 15 | 3.8.1 16 | 17 | 1.8 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-shade-plugin 25 | 3.2.1 26 | 27 | EvoImage-v${project.version} 28 | 29 | 31 | io.x666c.evoimg.EvoImage 32 | 33 | 34 | false 35 | 36 | 37 | 38 | package 39 | 40 | shade 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | de.androidpit 53 | color-thief 54 | 1.1.2 55 | 56 | 57 | 58 | org.jfree 59 | jfreesvg 60 | 3.4 61 | 62 | 63 | 64 | com.sparkjava 65 | spark-core 66 | 2.8.0 67 | 68 | 69 | 70 | io.0x666c.glib4j 71 | G-Lib4j 72 | [1,] 73 | 74 | 75 | 76 | li.flor 77 | native-j-file-chooser 78 | 1.6.4 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/io/x666c/evoimg/internal/Polygon.java: -------------------------------------------------------------------------------- 1 | package io.x666c.evoimg.internal; 2 | 3 | import java.awt.Color; 4 | 5 | import io.x666c.evoimg.gui.InfoPane; 6 | import io.x666c.glib4j.math.MathUtil; 7 | 8 | public class Polygon implements Cloneable { 9 | 10 | public Vector p1, p2, p3; 11 | public Color color; 12 | 13 | public Polygon() { 14 | p1 = Vector.create(MathUtil.random(Polygonizer.WIDTH), MathUtil.random(Polygonizer.HEIGHT)); 15 | p2 = Vector.create(MathUtil.random(Polygonizer.WIDTH), MathUtil.random(Polygonizer.HEIGHT)); 16 | p3 = Vector.create(MathUtil.random(Polygonizer.WIDTH), MathUtil.random(Polygonizer.HEIGHT)); 17 | 18 | if (InfoPane.colorInitMode() == 0) { 19 | color = new Color(0, 0, 0, MathUtil.random()); 20 | } else if (InfoPane.colorInitMode() == 1) { 21 | color = new Color(MathUtil.random(255), MathUtil.random(255), MathUtil.random(255), MathUtil.random(249)); 22 | } else if (InfoPane.colorInitMode() == 2) { 23 | int r = Polygonizer.IDEAL_MEAN.getRed(); 24 | int g = Polygonizer.IDEAL_MEAN.getGreen(); 25 | int b = Polygonizer.IDEAL_MEAN.getBlue(); 26 | color = new Color(r, g, b, MathUtil.random(249)); 27 | } 28 | } 29 | 30 | void mutate() { 31 | int rnd = MathUtil.random(16); 32 | 33 | if (rnd <= 2) 34 | mutateColor(rnd); 35 | else if (rnd <= 9) 36 | mutateCoordinate(rnd); 37 | else 38 | mutateAlpha(); 39 | 40 | } 41 | 42 | void mutateCoordinate(int rnd) { 43 | switch (rnd) { 44 | case 4: 45 | p1 = Vector.create(MathUtil.random(Polygonizer.WIDTH), p1.y); 46 | break; 47 | case 5: 48 | p2 = Vector.create(MathUtil.random(Polygonizer.WIDTH), p2.y); 49 | break; 50 | case 6: 51 | p3 = Vector.create(MathUtil.random(Polygonizer.WIDTH), p3.y); 52 | break; 53 | case 7: 54 | p1 = Vector.create(p1.x, MathUtil.random(Polygonizer.HEIGHT)); 55 | break; 56 | case 8: 57 | p2 = Vector.create(p1.x, MathUtil.random(Polygonizer.HEIGHT)); 58 | break; 59 | case 9: 60 | p3 = Vector.create(p1.x, MathUtil.random(Polygonizer.HEIGHT)); 61 | break; 62 | } 63 | } 64 | 65 | void mutateColor(int rnd) { 66 | if (rnd == 0) { 67 | color = new Color(MathUtil.random(255), color.getGreen(), color.getBlue(), color.getAlpha()); 68 | } else if (rnd == 1) { 69 | color = new Color(color.getRed(), MathUtil.random(255), color.getBlue(), color.getAlpha()); 70 | } else if (rnd == 2) { 71 | color = new Color(color.getRed(), color.getGreen(), MathUtil.random(255), color.getAlpha()); 72 | } 73 | } 74 | 75 | void mutateAlpha() { 76 | color = new Color(color.getRed(), color.getGreen(), color.getBlue(), MathUtil.random(249)); 77 | } 78 | 79 | Polygon copy() { 80 | try { 81 | return (Polygon) clone(); 82 | } catch (CloneNotSupportedException e) { 83 | e.printStackTrace(); 84 | } 85 | return null; 86 | } 87 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EvoImage 2 | This is a fast-_ish_ java hill-climber that can approximate images with triangles! 3 | 4 | Inspired by this video authored by the awesome Two Minute Papers 5 | 6 | I welcome _all_ pull requests: new image examples, typos, bug-fixes, functionality, layout, GUI or typo corrections will for sure be accepted! :) 7 | 8 |
9 | 10 | 11 | #### Contents: 12 | 1. [Examples](#Examples) 13 | 2. [Performance](#Performance) 14 | 15 | 16 | 17 | # Examples 18 | 19 | Here's some cool results this program can produce. 20 | 21 | 1. 60 vertices, 2 minutes 22 |

23 | 24 | 25 |

26 | 27 | 2. 650 vertices, 1 hour 28 |

29 | 30 | 31 |

32 | 33 |

34 | 35 |

36 | 37 | 38 | 39 | 40 |

41 | 42 | 43 | 44 |
45 | 46 | # Performance 47 | 48 | I worked hard on optimising the image comparator, so it should work pretty fast (_160 iterations/s at 50 triangles_), but there's stil a large handicap: 49 | ```java 50 | // Population.java, v1.4.4 51 | 52 | buffer = deepCopy(members); // < Copies the whole population array each time iterating 53 | 54 | buffer[MathUtil.random(buffer.length)].mutate(); // < Fine 55 | 56 | g.setColor(Color.WHITE); 57 | g.fillRect(0, 0, Polygonizer.WIDTH, Polygonizer.HEIGHT); // < Clear background, 58 | 59 | for (Polygon p : buffer) { // < Very, very bad! The speed of java graphics just isn't enough. 60 | g.setColor(p.color); // < Simply removing this line triples the speed 61 | g.fillPolygon(new int[] {(int) p.p1.x, (int) p.p2.x, (int) p.p3.x}, new int[] {(int) p.p1.y, (int) p.p2.y, (int) p.p3.y}, 3); // < Sloooooow 62 | } 63 | ``` 64 | I have no idea what to do here, since multithreading is not an option with the Graphics api. Hope i or somebody else will figure it out in the future. 65 | -------------------------------------------------------------------------------- /src/io/x666c/evoimg/web/WebStats.java: -------------------------------------------------------------------------------- 1 | package io.x666c.evoimg.web; 2 | 3 | import java.awt.Color; 4 | import java.awt.image.BufferedImage; 5 | import java.io.ByteArrayOutputStream; 6 | import java.util.Base64; 7 | 8 | import javax.imageio.ImageIO; 9 | 10 | import io.x666c.evoimg.EvoImage; 11 | import io.x666c.evoimg.gui.InfoPane; 12 | import spark.Request; 13 | import spark.Response; 14 | import spark.Spark; 15 | 16 | public class WebStats { 17 | 18 | private static boolean launched = false; 19 | 20 | private static String tss; 21 | private static String est; 22 | private static String usf; 23 | private static String usl; 24 | private static String pgr; 25 | private static String evr; 26 | private static String err; 27 | private static String gen; 28 | private static Color dcl; 29 | private static Color mcl; 30 | 31 | public static void launch() { 32 | Spark.port(80); 33 | Spark.get("/evo", WebStats::mainRoute); 34 | launched = true; 35 | } 36 | 37 | private static String mainRoute(Request req, Response resp) { 38 | String base64img = "ban"; 39 | final ByteArrayOutputStream out = new ByteArrayOutputStream(); 40 | try { 41 | BufferedImage copy = InfoPane.deepCopy(EvoImage.polygonizer.getImage()); 42 | ImageIO.write(copy, "png", out); 43 | } catch (Exception e) { 44 | e.printStackTrace(); 45 | } 46 | base64img = Base64.getEncoder().encodeToString(out.toByteArray()); 47 | 48 | String ret = "
" + 49 | "\"U" + 50 | "
"; 51 | 52 | ret += inDiv("Elapsed time: " + tss); 53 | ret += inDiv("Estimated time: " + est); 54 | ret += inDiv("Useful mutations: " + usf); 55 | ret += inDiv("Useless mutations: " + usl); 56 | ret += inDiv("Progress: " + pgr); 57 | ret += inDiv("Evolution rate: " + evr); 58 | ret += inDiv("Error: " + err); 59 | ret += inDiv("Generation: " + gen); 60 | 61 | ret += "
" + 62 | "Dominant color: " + 63 | "
" + 64 | "
"; 65 | 66 | ret += "
"; 67 | 68 | ret += "
" + 69 | "Mean color: " + 70 | "
" + 71 | "
"; 72 | 73 | return ret; 74 | } 75 | 76 | private static String inDiv(String content) { 77 | return "
" + content + "
"; 78 | } 79 | 80 | public static void updateInformation(long sinceStart, long estt, int usefulMutatuions, int uselessMutatuions, double progress, double error, Color dominantColor, Color averageColor, int generaions) { 81 | if(!launched) 82 | return; 83 | 84 | tss = InfoPane.convertSecondsToHMmSs(sinceStart); 85 | est = String.valueOf(estt); 86 | usf = String.valueOf(usefulMutatuions); 87 | usl = String.valueOf(uselessMutatuions); 88 | pgr = String.format("%.1f", progress) + "%"; 89 | evr = String.format("%.1f", ((double) usefulMutatuions) / (double) (uselessMutatuions) - 1); 90 | err = String.format("%.1f", error); 91 | dcl = dominantColor; 92 | mcl = averageColor; 93 | gen = String.valueOf(generaions); 94 | } 95 | 96 | } -------------------------------------------------------------------------------- /src/io/x666c/evoimg/EvoGui.java: -------------------------------------------------------------------------------- 1 | package io.x666c.evoimg; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Color; 5 | import java.awt.Dimension; 6 | import java.awt.image.BufferedImage; 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.util.function.Function; 10 | import java.util.function.Supplier; 11 | 12 | import javax.imageio.ImageIO; 13 | import javax.swing.JFrame; 14 | import javax.swing.JOptionPane; 15 | import javax.swing.JPanel; 16 | import javax.swing.SwingUtilities; 17 | import javax.swing.border.LineBorder; 18 | 19 | import io.x666c.evoimg.gui.ControlsPanel; 20 | import io.x666c.evoimg.gui.DisplayCanvas; 21 | import io.x666c.evoimg.gui.InfoPane; 22 | 23 | public class EvoGui extends JFrame { 24 | 25 | private DisplayCanvas canvas; 26 | public ControlsPanel controls; 27 | private InfoPane infoPane; 28 | 29 | public static boolean startedOnce = false; 30 | 31 | private JPanel canvasPane; 32 | 33 | public static final String VERSION = "1.4.5"; 34 | 35 | EvoGui() { 36 | setTitle("EvoGui " + VERSION); 37 | setDefaultCloseOperation(EXIT_ON_CLOSE); 38 | setResizable(false); 39 | setLayout(new BorderLayout()); 40 | 41 | canvas = new DisplayCanvas(); 42 | canvas.setBorder(new LineBorder(Color.BLACK, 1)); 43 | canvasPane = new JPanel(); 44 | canvasPane.setLayout(null); 45 | canvasPane.setPreferredSize(new Dimension(DisplayCanvas.DISPLAY_WIDTH + 20, DisplayCanvas.DISPLAY_HEIGHT + 20)); 46 | canvasPane.add(canvas); 47 | add(canvasPane); 48 | 49 | infoPane = new InfoPane(); 50 | add(infoPane, BorderLayout.WEST); 51 | 52 | controls = new ControlsPanel(infoPane, this); 53 | add(controls, BorderLayout.EAST); 54 | 55 | pack(); 56 | setLocationRelativeTo(null); 57 | } 58 | 59 | public InfoPane getInfoPane() { 60 | return infoPane; 61 | } 62 | 63 | public void packCanvas() { 64 | canvasPane.setPreferredSize(new Dimension(DisplayCanvas.DISPLAY_WIDTH + 20, DisplayCanvas.DISPLAY_HEIGHT + 20)); 65 | canvas.resize(); 66 | pack(); 67 | } 68 | 69 | public void setLoadImageCallback(Supplier cb) { 70 | controls.loadImage.addActionListener(ev -> { 71 | try { 72 | String path = cb.get(); 73 | if (path != null) { 74 | SwingUtilities.invokeLater(() -> { 75 | controls.start.setEnabled(true); 76 | controls.loadedImagePath.setText(path); 77 | try { 78 | setImageForPreview(ImageIO.read(new File(path))); 79 | } catch (IOException e) { 80 | e.printStackTrace(); 81 | } 82 | }); 83 | } 84 | } catch (Exception ex) { 85 | JOptionPane.showMessageDialog(null, ex); 86 | } 87 | }); 88 | } 89 | 90 | public void setStartCallback(Function cb) { 91 | controls.start.addActionListener(ev -> { 92 | controls.start.setText(cb.apply(controls)); 93 | infoPane.enableSaveButton(); 94 | }); 95 | } 96 | 97 | public void setImageForPreview(BufferedImage img) { 98 | controls.setImageForPreview(img); 99 | } 100 | 101 | public BufferedImage getSelectedImage() { 102 | return controls.getSelectedImage(); 103 | } 104 | 105 | public boolean isAutosave() { 106 | return controls.isAutosave(); 107 | } 108 | 109 | public boolean isNewFileEachTime() { 110 | return controls.isNewFileEachTime(); 111 | } 112 | 113 | public int getVertexAmount() { 114 | return controls.getVertexAmount(); 115 | } 116 | 117 | public void redrawCanvas(BufferedImage img) { 118 | canvas.drawImg(img); 119 | } 120 | 121 | public void enableAll(boolean should) { 122 | SwingUtilities.invokeLater(() -> { 123 | // controls.autosaveLocationSelector.setEnabled(should); 124 | controls.loadImage.setEnabled(should); 125 | // controls.autosave.setEnabled(should); 126 | controls.separateFile.setEnabled(should && controls.autosave.isSelected()); 127 | controls.vertexAmount.setEnabled(should); 128 | controls.seedField.setEnabled(should); 129 | }); 130 | } 131 | 132 | } -------------------------------------------------------------------------------- /src/io/x666c/evoimg/internal/Comparator.java: -------------------------------------------------------------------------------- 1 | package io.x666c.evoimg.internal; 2 | 3 | import java.awt.Color; 4 | import java.awt.image.BufferedImage; 5 | import java.awt.image.DataBufferByte; 6 | import java.awt.image.DataBufferInt; 7 | import java.awt.image.Raster; 8 | 9 | public class Comparator { 10 | 11 | private static int[] idealRaster; 12 | private static int WT2 = Polygonizer.WIDTH * 2; 13 | 14 | private static int[] idealDatai; 15 | 16 | private static byte[] idealDatab; 17 | 18 | public static int APPROXIMATION = 1; 19 | 20 | public static volatile int ALGO = 2; // 0 < 1 < 2 21 | 22 | // 29579073 23 | 24 | public static final void setIdeal(BufferedImage ideal) { 25 | if (ALGO == 0) { 26 | idealRaster = new int[ideal.getWidth() * ideal.getHeight() * 3]; 27 | for (int x = 0; x < ideal.getWidth(); x++) { 28 | for (int y = 0; y < ideal.getHeight(); y++) { 29 | final int[] raster = ideal.getRaster().getPixel(x, y, (int[]) null); 30 | for (int r = 0; r < 3; r++) { 31 | idealRaster[x + ideal.getHeight() * (y + ideal.getWidth() * r)] = raster[r]; 32 | } 33 | } 34 | } 35 | WT2 = Polygonizer.WIDTH * 2; 36 | } else if (ALGO == 1) { 37 | int[] rawData = ((DataBufferInt) ideal.getRaster().getDataBuffer()).getData(); 38 | idealDatai = new int[rawData.length * 3]; 39 | 40 | int counter = 0; 41 | for (int i = 0; i < rawData.length * 3; i += 3) { 42 | idealDatai[i] = (rawData[counter] & 0xFF); 43 | idealDatai[i + 1] = (rawData[counter] >> 8 & 0xFF); 44 | idealDatai[i + 2] = (rawData[counter] >> 16 & 0xFF); 45 | 46 | counter++; 47 | } 48 | } else if (ALGO >= 2) { 49 | idealDatab = ((DataBufferByte) ideal.getRaster().getDataBuffer()).getData(); 50 | } 51 | } 52 | 53 | @SuppressWarnings("unused") 54 | public static long calculateError(BufferedImage canvas) { 55 | if (ALGO == 0) { 56 | return alg0(canvas); 57 | } else if (ALGO == 1) { 58 | return alg1(canvas); 59 | } else if (ALGO == 2) { 60 | return alg2(canvas); 61 | } 62 | return -1; 63 | } 64 | 65 | private static long alg0(BufferedImage canvas) { 66 | long error = 0; 67 | 68 | final Raster r1 = canvas.getRaster(); 69 | 70 | final int[] raster1 = new int[3]; 71 | 72 | for (int x = 0; x < Polygonizer.WIDTH; x += APPROXIMATION) { 73 | for (int y = 0; y < Polygonizer.HEIGHT; y += APPROXIMATION) { 74 | r1.getPixel(x, y, raster1); 75 | 76 | error += Math.abs(raster1[0] - idealRaster[x + (Polygonizer.HEIGHT * y)]); 77 | error += Math.abs(raster1[1] - idealRaster[x + (Polygonizer.HEIGHT * (y + Polygonizer.WIDTH))]); 78 | error += Math.abs(raster1[2] - idealRaster[x + (Polygonizer.HEIGHT * (y + WT2))]); 79 | } 80 | } 81 | return error; 82 | } 83 | 84 | private static long alg1(BufferedImage canvas) { 85 | long error = 0; 86 | 87 | final int[] canvasData = ((DataBufferInt) canvas.getRaster().getDataBuffer()).getData(); 88 | 89 | for (int i = 0; i < canvasData.length; i += APPROXIMATION) { 90 | final int d1 = (canvasData[i] & 0xFF) - (idealDatai[(i * 3)]); 91 | final int d2 = (canvasData[i] >> 8 & 0xFF) - (idealDatai[(i * 3) + 1]); 92 | final int d3 = (canvasData[i] >> 16 & 0xFF) - (idealDatai[(i * 3) + 2]); 93 | 94 | error += (d1 * d1) + (d2 * d2) + (d3 * d3); 95 | } 96 | 97 | return error; 98 | } 99 | 100 | private static long alg2(BufferedImage canvas) { 101 | long error = 0; 102 | 103 | final byte[] canvasData = ((DataBufferByte) canvas.getRaster().getDataBuffer()).getData(); 104 | for (int i = 0; i < canvasData.length; i += APPROXIMATION) { 105 | final int d = (canvasData[i] & 0xff) - (idealDatab[i] & 0xff); 106 | error += d * d; 107 | } 108 | 109 | return error; 110 | } 111 | 112 | public static double colorDistance(Color c1, Color c2) { 113 | long l1 = System.nanoTime(); 114 | final double dist = (Math.sqrt(((c1.getRed() - c2.getRed()) * (c1.getRed() - c2.getRed())) 115 | + ((c1.getGreen() - c2.getGreen()) * (c1.getGreen() - c2.getGreen())) 116 | + ((c1.getBlue() - c2.getBlue()) * (c1.getBlue() - c2.getBlue())))); 117 | long l2 = System.nanoTime(); 118 | System.out.println(l2 - l1); 119 | return dist; 120 | } 121 | 122 | } -------------------------------------------------------------------------------- /src/io/x666c/evoimg/internal/Population.java: -------------------------------------------------------------------------------- 1 | package io.x666c.evoimg.internal; 2 | 3 | import java.awt.Color; 4 | import java.awt.Graphics2D; 5 | import java.awt.GraphicsConfiguration; 6 | import java.awt.GraphicsEnvironment; 7 | import java.awt.RenderingHints; 8 | import java.awt.image.BufferedImage; 9 | import java.util.Arrays; 10 | 11 | import de.androidpit.colorthief.ColorThief; 12 | import io.x666c.glib4j.graphics.PlainRenderer; 13 | import io.x666c.glib4j.math.MathUtil; 14 | import sun.awt.image.SunVolatileImage; 15 | 16 | public class Population { 17 | 18 | Polygon[] members; 19 | Polygon[] buffer; 20 | 21 | final BufferedImage canvas; 22 | final BufferedImage ideal; 23 | 24 | double mutatedError = Double.POSITIVE_INFINITY; 25 | double currentError = Double.POSITIVE_INFINITY; 26 | 27 | double completeness = 0; 28 | 29 | int goodMutations = 0; 30 | int badMutations = 0; 31 | 32 | int generations = 0; 33 | 34 | Color dominantColor = Color.BLACK; 35 | Color averageColor = Color.BLACK; 36 | 37 | Population(int amount, BufferedImage image) { 38 | ideal = image; 39 | Comparator.setIdeal(image); 40 | 41 | canvas = new BufferedImage(Polygonizer.WIDTH, Polygonizer.HEIGHT, 42 | Comparator.ALGO >= 2 ? BufferedImage.TYPE_3BYTE_BGR : BufferedImage.TYPE_INT_RGB); 43 | 44 | g = canvas.createGraphics(); 45 | g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 46 | g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); 47 | 48 | members = new Polygon[amount]; 49 | for (int i = 0; i < members.length; i++) { 50 | members[i] = new Polygon(); 51 | } 52 | buffer = new Polygon[amount]; 53 | } 54 | 55 | private Graphics2D g; 56 | 57 | void mutate() { 58 | g = canvas.createGraphics(); 59 | g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 60 | g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); 61 | 62 | generations++; 63 | 64 | buffer = deepCopy(members); 65 | 66 | buffer[MathUtil.random(buffer.length)].mutate(); 67 | 68 | // canvas = new BufferedImage(Polygonizer.WIDTH, Polygonizer.HEIGHT, 69 | // BufferedImage.TYPE_INT_RGB); 70 | // FIXME: g is a field and canvas never updates. can cause bugs 71 | 72 | g.setColor(Color.WHITE); 73 | g.fillRect(0, 0, Polygonizer.WIDTH, Polygonizer.HEIGHT); 74 | 75 | for (Polygon p : buffer) { 76 | g.setColor(p.color); 77 | g.fillPolygon(new int[] {(int) p.p1.x, (int) p.p2.x, (int) p.p3.x}, new int[] {(int) p.p1.y, (int) p.p2.y, (int) p.p3.y}, 3); 78 | } 79 | } 80 | 81 | void calculateError() { 82 | mutatedError = Comparator.calculateError(canvas); 83 | if (mutatedError < currentError) { 84 | members = buffer; 85 | currentError = mutatedError; 86 | 87 | goodMutations++; 88 | } else { 89 | badMutations++; 90 | } 91 | calculateCompleteness(); 92 | if (colorSwitch == 200) { 93 | calculateColors(); 94 | colorSwitch = 0; 95 | } 96 | colorSwitch++; 97 | } 98 | 99 | private int colorSwitch = 0; 100 | private double firstCompleteness = -100; 101 | 102 | private void calculateCompleteness() { 103 | final double rawVal = (double) (100 * (1 - currentError / (canvas.getWidth() * canvas.getHeight() * 3 * 255))); 104 | if (firstCompleteness == -100) { 105 | firstCompleteness = rawVal; 106 | } 107 | completeness = map(rawVal, firstCompleteness, 100, 0, 100); 108 | } 109 | 110 | // shit 111 | public static final double map(double value, double istart, double istop, double ostart, double ostop) { 112 | return ostart + (ostop - ostart) * ((value - istart) / (istop - istart)); 113 | } 114 | 115 | private void calculateColors() { 116 | final int[] rgb = ColorThief.getColorMap(canvas, 256, 5, true).vboxes.get(0).avg(false); 117 | dominantColor = new Color(rgb[0] << 16 | rgb[1] << 8 | rgb[2]); 118 | 119 | int ared = 0; 120 | int agreen = 0; 121 | int ablue = 0; 122 | for (Polygon p : members) { 123 | ared += p.color.getRed(); 124 | agreen += p.color.getGreen(); 125 | ablue += p.color.getBlue(); 126 | } 127 | averageColor = new Color(ared / members.length, agreen / members.length, ablue / members.length); 128 | } 129 | 130 | void draw(PlainRenderer r) { 131 | r.fill(); 132 | for (Polygon p : members) { 133 | r.color(p.color); 134 | r.polygon(p.p1.x, p.p1.y, p.p2.x, p.p2.y, p.p3.x, p.p3.y); 135 | } 136 | } 137 | 138 | public Polygon[] getPopulation() { 139 | return members; 140 | } 141 | 142 | Polygon[] deepCopy(Polygon[] src) { 143 | Polygon[] ret = new Polygon[src.length]; 144 | for (int i = 0; i < src.length; i++) { 145 | ret[i] = src[i].copy(); 146 | } 147 | return ret; 148 | } 149 | } -------------------------------------------------------------------------------- /src/io/x666c/evoimg/internal/Polygonizer.java: -------------------------------------------------------------------------------- 1 | package io.x666c.evoimg.internal; 2 | 3 | import java.awt.Color; 4 | import java.awt.Graphics2D; 5 | import java.awt.image.BufferedImage; 6 | 7 | import de.androidpit.colorthief.ColorThief; 8 | import io.x666c.evoimg.gui.InfoPane; 9 | import io.x666c.glib4j.graphics.PlainRenderer; 10 | 11 | public class Polygonizer { 12 | 13 | private static volatile boolean hasOneInstance = false; 14 | { 15 | if (hasOneInstance) 16 | throw new RuntimeException("Only one instance is allowed per program."); 17 | else 18 | hasOneInstance = true; 19 | } 20 | 21 | private volatile Thread workerThread; 22 | private volatile Thread drawCallbackThread; 23 | private volatile boolean loop = false; 24 | 25 | public static int WIDTH; 26 | public static int HEIGHT; 27 | 28 | Population population; 29 | 30 | int seed; 31 | 32 | private boolean broken = false; 33 | 34 | static Color IDEAL_DOMINANT; 35 | static Color IDEAL_MEAN; 36 | 37 | public Polygonizer(BufferedImage goal, int vertices, int width, int height) { 38 | WIDTH = width; 39 | HEIGHT = height; 40 | 41 | picture = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); 42 | r = new PlainRenderer(picture.createGraphics()); 43 | 44 | BufferedImage scaled = new BufferedImage(Polygonizer.WIDTH, Polygonizer.HEIGHT, 45 | Comparator.ALGO >= 2 ? BufferedImage.TYPE_3BYTE_BGR : BufferedImage.TYPE_INT_RGB); 46 | Graphics2D g = scaled.createGraphics(); 47 | g.drawImage(goal, 0, 0, Polygonizer.WIDTH, Polygonizer.HEIGHT, null); 48 | g.dispose(); 49 | 50 | int[] rgb = ColorThief.getColorMap(scaled, 256, 1, true).vboxes.get(0).avg(false); 51 | IDEAL_DOMINANT = new Color(rgb[0] << 16 | rgb[1] << 8 | rgb[2]); 52 | 53 | int ared = 0; 54 | int agreen = 0; 55 | int ablue = 0; 56 | final int linearSize = scaled.getWidth() * scaled.getHeight(); 57 | for (int x = 0; x < scaled.getWidth(); x++) { 58 | for (int y = 0; y < scaled.getHeight(); y++) { 59 | Color c = new Color(scaled.getRGB(x, y)); 60 | 61 | ared += c.getRed(); 62 | agreen += c.getGreen(); 63 | ablue += c.getBlue(); 64 | } 65 | } 66 | IDEAL_MEAN = new Color(ared / linearSize, agreen / linearSize, ablue / linearSize); 67 | 68 | population = new Population(vertices, scaled); 69 | } 70 | 71 | public void start() { 72 | if (broken) 73 | throw new RuntimeException("Broken"); 74 | 75 | loop = true; 76 | 77 | workerThread = new Thread(this::update); 78 | drawCallbackThread = new Thread(this::draw); 79 | 80 | workerThread.start(); 81 | drawCallbackThread.start(); 82 | } 83 | 84 | public void stop() { 85 | loop = false; 86 | try { 87 | workerThread.join(); 88 | drawCallbackThread.join(); 89 | } catch (Exception e) { 90 | } 91 | workerThread = null; 92 | r = null; 93 | } 94 | 95 | private void update() { 96 | while (loop) { 97 | population.mutate(); 98 | 99 | population.calculateError(); 100 | 101 | InfoPane.mutationsIncrement += 1 * 2; 102 | } 103 | } 104 | 105 | private final Object IMAGE_LOCK = new Object(); 106 | 107 | private volatile BufferedImage picture; 108 | private volatile PlainRenderer r; 109 | 110 | private volatile Runnable drawThreadCallback = null; 111 | 112 | public void draw() { 113 | while (loop) { 114 | if (broken) 115 | throw new RuntimeException("This instance is broken"); 116 | 117 | synchronized (IMAGE_LOCK) { 118 | r.fill(); 119 | r.color(0xd3d3d3); 120 | r.rectangle(0, 0, picture.getWidth(), picture.getHeight()); 121 | 122 | population.draw(r); 123 | } 124 | 125 | if (drawThreadCallback != null) 126 | drawThreadCallback.run(); 127 | 128 | try { 129 | Thread.sleep(30); 130 | } catch (Exception e) { 131 | e.printStackTrace(); 132 | } 133 | } 134 | } 135 | 136 | public void setDrawCallback(Runnable cb) { 137 | drawThreadCallback = cb; 138 | } 139 | 140 | public BufferedImage getImage() { 141 | synchronized (IMAGE_LOCK) { 142 | return picture; 143 | } 144 | } 145 | 146 | public BufferedImage getImageNoSync() { 147 | return picture; 148 | } 149 | 150 | public void release() { 151 | stop(); 152 | broken = true; 153 | 154 | hasOneInstance = false; 155 | } 156 | 157 | public int getGoodMutations() { 158 | return population.goodMutations; 159 | } 160 | 161 | public int getBadMutations() { 162 | return population.badMutations; 163 | } 164 | 165 | public int getGenerations() { 166 | return population.generations; 167 | } 168 | 169 | public double getError() { 170 | return population.currentError; 171 | } 172 | 173 | public double getCompleteness() { 174 | return population.completeness; 175 | } 176 | 177 | public Color getDominantColor() { 178 | return population.dominantColor; 179 | } 180 | 181 | public Color getAverageColor() { 182 | return population.averageColor; 183 | } 184 | 185 | public boolean isBroken() { 186 | return broken; 187 | } 188 | 189 | public Population getPopulation() { 190 | return population; 191 | } 192 | 193 | } -------------------------------------------------------------------------------- /src/io/x666c/evoimg/EvoImage.java: -------------------------------------------------------------------------------- 1 | package io.x666c.evoimg; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Insets; 5 | import java.awt.image.BufferedImage; 6 | 7 | import javax.swing.BoxLayout; 8 | import javax.swing.ButtonGroup; 9 | import javax.swing.JButton; 10 | import javax.swing.JDialog; 11 | import javax.swing.JFileChooser; 12 | import javax.swing.JFrame; 13 | import javax.swing.JLabel; 14 | import javax.swing.JOptionPane; 15 | import javax.swing.JPanel; 16 | import javax.swing.JRadioButton; 17 | import javax.swing.UIManager; 18 | import javax.swing.border.EmptyBorder; 19 | import javax.swing.filechooser.FileNameExtensionFilter; 20 | 21 | import io.x666c.evoimg.gui.ControlsPanel; 22 | import io.x666c.evoimg.gui.DisplayCanvas; 23 | import io.x666c.evoimg.internal.Comparator; 24 | import io.x666c.evoimg.internal.Polygonizer; 25 | import io.x666c.glib4j.math.MathUtil; 26 | import li.flor.nativejfilechooser.NativeJFileChooser; 27 | 28 | // USE *TitledBorder* IT'S AWESOME!!11! 29 | 30 | public class EvoImage { 31 | 32 | static { 33 | // System.setProperty("sun.java2d.opengl", "true"); 34 | } 35 | 36 | public static void main(String[] args) throws Exception { 37 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 38 | 39 | JFrame dummy = new JFrame("Approximation"); 40 | dummy.setUndecorated(true); 41 | dummy.setVisible(true); 42 | dummy.setLocationRelativeTo(null); 43 | JDialog f = new JDialog(dummy, true); 44 | f.setTitle("Approximation"); 45 | f.setDefaultCloseOperation(0); 46 | JPanel p2 = new JPanel(); 47 | p2.setBorder(new EmptyBorder(20,10,20,10)); 48 | p2.setLayout(new BorderLayout()); 49 | f.setContentPane(p2); 50 | JPanel p = new JPanel(); 51 | p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 52 | p2.add(p, BorderLayout.CENTER); 53 | JLabel txt = new JLabel("Please select an approximation level:"); 54 | txt.setBorder(new EmptyBorder(1,0,25,0)); 55 | ButtonGroup gr = new ButtonGroup(); 56 | JRadioButton none = new JRadioButton("None (every pixel)"); 57 | JRadioButton low = new JRadioButton("Low (every second pixel)"); 58 | JRadioButton high = new JRadioButton("High (every third pixel)"); 59 | JRadioButton extreme = new JRadioButton("Extreme (every forth pixel)"); 60 | JButton ok = new JButton("Ok"); 61 | ok.setMargin(new Insets(4, 25, 4, 25)); 62 | gr.add(none); 63 | gr.add(low); 64 | gr.add(high); 65 | gr.add(extreme); 66 | gr.setSelected(low.getModel(), true); 67 | p.add(txt); 68 | p.add(none); 69 | p.add(low); 70 | p.add(high); 71 | p.add(extreme); 72 | p.add(ok); 73 | ok.addActionListener(ev -> { 74 | f.setVisible(false); 75 | f.dispose(); 76 | dummy.setVisible(false); 77 | dummy.dispose(); 78 | if(none.isSelected()) { 79 | Comparator.APPROXIMATION = 1; 80 | } else if(low.isSelected()) { 81 | Comparator.APPROXIMATION = 2; 82 | } else if(high.isSelected()) { 83 | Comparator.APPROXIMATION = 3; 84 | } else if(extreme.isSelected()) { 85 | Comparator.APPROXIMATION = 4; 86 | } 87 | }); 88 | 89 | f.pack(); 90 | f.setLocationRelativeTo(null); 91 | 92 | f.setVisible(true); 93 | 94 | new EvoImage().start(); 95 | } 96 | 97 | public static Polygonizer polygonizer; 98 | 99 | private Thread infoPanelUpdater; 100 | private Thread infoPanelFPSUpdater; 101 | 102 | private EvoGui gui; 103 | 104 | private void start() throws Exception { 105 | gui = new EvoGui(); 106 | 107 | MathUtil.randomSeed(gui.controls.seedField.getText().hashCode()); 108 | 109 | gui.setLoadImageCallback(this::loadImage); 110 | gui.setStartCallback(this::startCb); 111 | 112 | gui.setVisible(true); 113 | } 114 | 115 | private String loadImage() { 116 | JFileChooser chooser = new JFileChooser(); 117 | // JFileChooser chooser = new NativeJFileChooser(); 118 | chooser.setMultiSelectionEnabled(false); 119 | chooser.setFileFilter(new FileNameExtensionFilter("Images (png, jpg, gif, bmp)", "png", "jpg", "jpeg", "gif", "bmp")); 120 | if(chooser.showDialog(gui, "Open") == JFileChooser.APPROVE_OPTION) { 121 | return chooser.getSelectedFile().getAbsolutePath(); 122 | } 123 | return null; 124 | } 125 | 126 | private String startCb(ControlsPanel p) { 127 | String retString = ""; 128 | 129 | MathUtil.randomSeed(gui.controls.seedField.getText().hashCode()); 130 | 131 | gui.controls.start.setEnabled(false); 132 | 133 | if(polygonizer == null || polygonizer.isBroken()) { 134 | final BufferedImage sel = p.getSelectedImage(); 135 | startPolygonizer(sel, p.getVertexAmount(), DisplayCanvas.DISPLAY_WIDTH, DisplayCanvas.DISPLAY_HEIGHT); 136 | polygonizer.setDrawCallback(() -> { 137 | gui.redrawCanvas(polygonizer.getImage()); 138 | }); 139 | gui.enableAll(false); 140 | 141 | loop = true; 142 | 143 | infoPanelUpdater = new Thread(this::infoUpdate); 144 | infoPanelFPSUpdater = new Thread(this::infoFPSUpdate); 145 | infoPanelUpdater.start(); 146 | infoPanelFPSUpdater.start(); 147 | 148 | retString = "Stop"; 149 | } else { 150 | stopPolygonizerAndInfoThread(); 151 | 152 | gui.enableAll(true); 153 | retString = "Start"; 154 | } 155 | 156 | gui.controls.start.setEnabled(true); 157 | 158 | return retString; 159 | } 160 | 161 | private volatile boolean loop = true; 162 | private void infoUpdate() { 163 | long startTime = System.nanoTime(); 164 | while(loop) { 165 | gui.getInfoPane().updateInformation(System.nanoTime() - startTime, 0, polygonizer.getGoodMutations(), polygonizer.getBadMutations(), polygonizer.getCompleteness(), polygonizer.getError(), polygonizer.getDominantColor(), polygonizer.getAverageColor(), polygonizer.getGenerations()); 166 | try { 167 | Thread.sleep(20); 168 | } catch (Exception e) {} 169 | } 170 | } 171 | 172 | private void infoFPSUpdate() { 173 | while(loop) { 174 | try { 175 | Thread.sleep(500); 176 | gui.getInfoPane().updateFPSInformation(); 177 | } catch (Exception e) {} 178 | 179 | } 180 | } 181 | 182 | private void startPolygonizer(BufferedImage goal, int vertices, int width, int height) { 183 | polygonizer = new Polygonizer(goal, vertices, width, height); 184 | polygonizer.start(); 185 | } 186 | 187 | private void stopPolygonizerAndInfoThread() { 188 | polygonizer.release(); 189 | loop = false; 190 | try { 191 | infoPanelUpdater.join(); 192 | infoPanelFPSUpdater.join(); 193 | } catch (Exception e) { 194 | e.printStackTrace(); 195 | } 196 | } 197 | } -------------------------------------------------------------------------------- /src/io/x666c/evoimg/gui/ControlsPanel.java: -------------------------------------------------------------------------------- 1 | package io.x666c.evoimg.gui; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.Font; 5 | import java.awt.Graphics2D; 6 | import java.awt.Insets; 7 | import java.awt.image.BufferedImage; 8 | 9 | import javax.swing.ImageIcon; 10 | import javax.swing.JButton; 11 | import javax.swing.JCheckBox; 12 | import javax.swing.JLabel; 13 | import javax.swing.JPanel; 14 | import javax.swing.JSeparator; 15 | import javax.swing.JSpinner; 16 | import javax.swing.JTextField; 17 | import javax.swing.SpinnerNumberModel; 18 | import javax.swing.SwingConstants; 19 | import javax.swing.border.CompoundBorder; 20 | import javax.swing.border.EmptyBorder; 21 | import javax.swing.border.EtchedBorder; 22 | import javax.swing.text.DefaultFormatter; 23 | 24 | import io.x666c.evoimg.EvoGui; 25 | 26 | public class ControlsPanel extends JPanel { 27 | 28 | public JTextField autosaveLocation; 29 | public JTextField loadedImagePath; 30 | public JCheckBox autosave; 31 | public JCheckBox separateFile; 32 | public JButton autosaveLocationSelector; 33 | public JButton loadImage; 34 | public JSpinner vertexAmount; 35 | public JButton start; 36 | private JLabel imagePreview; 37 | public JTextField seedField; 38 | 39 | private EvoGui gui; 40 | 41 | public ControlsPanel(InfoPane p, EvoGui gui) { 42 | this.gui = gui; 43 | 44 | setPreferredSize(new Dimension(200, 500)); 45 | setBorder(new CompoundBorder(new EmptyBorder(1, 0, 0, 0), new EtchedBorder(EtchedBorder.LOWERED))); 46 | setLayout(null); 47 | 48 | start = new JButton("Start"); 49 | start.setEnabled(false); 50 | start.setFont(new Font("Tahoma", Font.PLAIN, 25)); 51 | start.setBounds(10, 11, 180, 47); 52 | add(start); 53 | 54 | JSeparator separator = new JSeparator(); 55 | separator.setBounds(10, 69, 180, 2); 56 | add(separator); 57 | 58 | vertexAmount = new JSpinner(new SpinnerNumberModel(50, 5, 10000, 1)); 59 | ((DefaultFormatter) ((JSpinner.NumberEditor) vertexAmount.getEditor()).getTextField().getFormatter()) 60 | .setCommitsOnValidEdit(true); 61 | vertexAmount.addChangeListener(ev -> { 62 | if (start.getText().equals("Start")) 63 | p.setVertexCount(Integer.parseInt(vertexAmount.getValue().toString())); 64 | }); 65 | vertexAmount.setBounds(110, 82, 80, 20); 66 | add(vertexAmount); 67 | 68 | JLabel vertexAmountL = new JLabel("Vertex amount"); 69 | vertexAmountL.setBounds(10, 82, 80, 20); 70 | add(vertexAmountL); 71 | 72 | JSeparator separator_1 = new JSeparator(); 73 | separator_1.setBounds(10, 138, 180, 2); 74 | add(separator_1); 75 | 76 | autosave = new JCheckBox("Autosave"); 77 | autosave.setEnabled(false); 78 | autosave.setHorizontalAlignment(SwingConstants.RIGHT); 79 | autosave.setHorizontalTextPosition(SwingConstants.LEFT); 80 | autosave.setBounds(119, 147, 71, 23); 81 | add(autosave); 82 | 83 | separateFile = new JCheckBox("Create new file each time?"); 84 | separateFile.setEnabled(false); 85 | separateFile.setHorizontalAlignment(SwingConstants.RIGHT); 86 | separateFile.setHorizontalTextPosition(SwingConstants.LEFT); 87 | separateFile.setBounds(37, 175, 153, 23); 88 | add(separateFile); 89 | 90 | autosaveLocationSelector = new JButton("Browse..."); 91 | autosaveLocationSelector.setEnabled(false); 92 | autosaveLocationSelector.setMargin(new Insets(0, 0, 0, 0)); 93 | autosaveLocationSelector.setBounds(10, 205, 60, 23); 94 | add(autosaveLocationSelector); 95 | 96 | autosaveLocation = new JTextField(); 97 | autosaveLocation.setHorizontalAlignment(SwingConstants.LEFT); 98 | autosaveLocation.setEditable(false); 99 | autosaveLocation.setBounds(80, 206, 110, 20); 100 | add(autosaveLocation); 101 | 102 | JSeparator separator_2 = new JSeparator(); 103 | separator_2.setBounds(10, 237, 180, 2); 104 | add(separator_2); 105 | 106 | imagePreview = new JLabel(); 107 | imagePreview.setHorizontalAlignment(SwingConstants.CENTER); 108 | imagePreview.setBounds(10, 250, 180, 180); 109 | imagePreview.setBorder(new EtchedBorder(EtchedBorder.RAISED)); 110 | add(imagePreview); 111 | 112 | loadImage = new JButton("Open..."); 113 | loadImage.setBounds(94, 441, 96, 23); 114 | add(loadImage); 115 | 116 | loadedImagePath = new JTextField(); 117 | loadedImagePath.setEditable(false); 118 | loadedImagePath.setHorizontalAlignment(SwingConstants.LEFT); 119 | loadedImagePath.setBounds(10, 469, 180, 20); 120 | add(loadedImagePath); 121 | 122 | JLabel seedL = new JLabel("Seed"); 123 | seedL.setBounds(10, 113, 46, 14); 124 | add(seedL); 125 | 126 | seedField = new JTextField(); 127 | seedField.setText("seed"); 128 | seedField.setHorizontalAlignment(JTextField.RIGHT); 129 | seedField.setBounds(110, 110, 80, 20); 130 | add(seedField); 131 | } 132 | 133 | private BufferedImage notPreview; 134 | 135 | public void setImageForPreview(BufferedImage img) { 136 | notPreview = img; 137 | 138 | BufferedImage resize = new BufferedImage(imagePreview.getWidth(), imagePreview.getHeight(), 139 | BufferedImage.TYPE_INT_RGB); 140 | Graphics2D g = resize.createGraphics(); 141 | g.drawImage(img, 0, 0, imagePreview.getWidth(), imagePreview.getHeight(), null); 142 | 143 | imagePreview.setIcon(new ImageIcon(resize)); 144 | 145 | final BufferedImage sel = getSelectedImage(); 146 | int tw = sel.getWidth(); 147 | int th = sel.getHeight(); 148 | // This is utterly dumb 149 | // int b = tw < th ? 1 : 2; 150 | // if(b == 1) { 151 | // while (tw > DisplayCanvas.DISPLAY_SIZE_LIMIT) { 152 | // tw--; 153 | // th--; 154 | // } 155 | // } else { 156 | // while (th > DisplayCanvas.DISPLAY_SIZE_LIMIT) { 157 | // tw--; 158 | // th--; 159 | // } 160 | // } 161 | final Dimension scaled = getScaledDimension(new Dimension(tw, th), new Dimension(DisplayCanvas.DISPLAY_SIZE_LIMIT, DisplayCanvas.DISPLAY_SIZE_LIMIT)); 162 | 163 | DisplayCanvas.DISPLAY_WIDTH = scaled.width; 164 | DisplayCanvas.DISPLAY_HEIGHT = scaled.height; 165 | gui.packCanvas(); 166 | } 167 | 168 | // Am i stupid? 169 | // -- yes i am heck 170 | public static Dimension getScaledDimension(Dimension imgSize, Dimension boundary) { 171 | int original_width = imgSize.width; 172 | int original_height = imgSize.height; 173 | int bound_width = boundary.width; 174 | int bound_height = boundary.height; 175 | int new_width = original_width; 176 | int new_height = original_height; 177 | 178 | if (original_width > bound_width) { 179 | new_width = bound_width; 180 | new_height = (new_width * original_height) / original_width; 181 | } 182 | 183 | if (new_height > bound_height) { 184 | new_height = bound_height; 185 | new_width = (new_height * original_width) / original_height; 186 | } 187 | 188 | return new Dimension(new_width, new_height); 189 | } 190 | 191 | public BufferedImage getSelectedImage() { 192 | return notPreview;// (BufferedImage)((ImageIcon)imagePreview.getIcon()).getImage(); 193 | } 194 | 195 | public boolean isAutosave() { 196 | return autosave.isSelected(); 197 | } 198 | 199 | public boolean isNewFileEachTime() { 200 | return separateFile.isSelected(); 201 | } 202 | 203 | public int getVertexAmount() { 204 | return (int) vertexAmount.getValue(); 205 | } 206 | } -------------------------------------------------------------------------------- /src/io/x666c/evoimg/gui/InfoPane.java: -------------------------------------------------------------------------------- 1 | package io.x666c.evoimg.gui; 2 | 3 | import java.awt.Color; 4 | import java.awt.Dimension; 5 | import java.awt.Insets; 6 | import java.awt.image.BufferedImage; 7 | import java.awt.image.ColorModel; 8 | import java.awt.image.WritableRaster; 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.util.Timer; 12 | import java.util.TimerTask; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | import javax.imageio.ImageIO; 16 | import javax.swing.ButtonGroup; 17 | import javax.swing.JButton; 18 | import javax.swing.JFileChooser; 19 | import javax.swing.JLabel; 20 | import javax.swing.JOptionPane; 21 | import javax.swing.JPanel; 22 | import javax.swing.JRadioButton; 23 | import javax.swing.JSeparator; 24 | import javax.swing.JTextField; 25 | import javax.swing.SwingConstants; 26 | import javax.swing.border.CompoundBorder; 27 | import javax.swing.border.EmptyBorder; 28 | import javax.swing.border.EtchedBorder; 29 | 30 | import org.jfree.graphics2d.svg.SVGGraphics2D; 31 | import org.jfree.graphics2d.svg.SVGUtils; 32 | 33 | import io.x666c.evoimg.EvoImage; 34 | import io.x666c.evoimg.internal.Polygon; 35 | import io.x666c.evoimg.web.WebStats; 36 | 37 | public class InfoPane extends JPanel { 38 | 39 | private JLabel timeSinceStart; 40 | private JLabel usefulMutatuions; 41 | private JLabel uselessMutatuions; 42 | private JLabel progress; 43 | private JLabel progressLog; 44 | private JLabel estTime; 45 | private JLabel error; 46 | private JLabel dominantColorIcon; 47 | private JLabel averageColorIcon; 48 | private JLabel verticesAmt; 49 | private JLabel evolutionRate; 50 | 51 | private JButton savePng; 52 | private JLabel evolutionsPerSecond; 53 | 54 | private static JRadioButton colorInitBlack; 55 | private static JRadioButton colorInitRandom; 56 | private static JRadioButton colorInitMean; 57 | private static JRadioButton colorInitDominant; 58 | 59 | public volatile static int mutationsIncrement = 0; 60 | private JLabel genL; 61 | private JLabel generations; 62 | private JButton saveSvg; 63 | 64 | public InfoPane() { 65 | setPreferredSize(new Dimension(200, 500)); 66 | setBorder(new CompoundBorder(new EmptyBorder(1,1,0,0), new EtchedBorder(EtchedBorder.LOWERED))); 67 | setLayout(null); 68 | 69 | JLabel sinceStartL = new JLabel("Elapsed time"); 70 | sinceStartL.setHorizontalAlignment(SwingConstants.LEFT); 71 | sinceStartL.setBounds(10, 11, 79, 14); 72 | add(sinceStartL); 73 | 74 | timeSinceStart = new JLabel("0:00:00"); 75 | timeSinceStart.setHorizontalAlignment(SwingConstants.RIGHT); 76 | timeSinceStart.setBounds(138, 11, 54, 14); 77 | add(timeSinceStart); 78 | 79 | JLabel lblUsefulMutations = new JLabel("Good mutations"); 80 | lblUsefulMutations.setHorizontalAlignment(SwingConstants.LEFT); 81 | lblUsefulMutations.setBounds(8, 124, 85, 14); 82 | add(lblUsefulMutations); 83 | 84 | usefulMutatuions = new JLabel("0"); 85 | usefulMutatuions.setHorizontalAlignment(SwingConstants.RIGHT); 86 | usefulMutatuions.setBounds(136, 124, 56, 14); 87 | add(usefulMutatuions); 88 | 89 | uselessMutatuions = new JLabel("0"); 90 | uselessMutatuions.setHorizontalAlignment(SwingConstants.RIGHT); 91 | uselessMutatuions.setBounds(136, 149, 56, 14); 92 | add(uselessMutatuions); 93 | 94 | JLabel lblVainMutations = new JLabel("Bad mutations"); 95 | lblVainMutations.setHorizontalAlignment(SwingConstants.LEFT); 96 | lblVainMutations.setBounds(8, 149, 85, 14); 97 | add(lblVainMutations); 98 | 99 | JLabel lblNewLabel = new JLabel("Progress"); 100 | lblNewLabel.setHorizontalAlignment(SwingConstants.LEFT); 101 | lblNewLabel.setBounds(8, 174, 46, 14); 102 | add(lblNewLabel); 103 | 104 | progress = new JLabel("0.0%"); 105 | progress.setHorizontalAlignment(SwingConstants.RIGHT); 106 | progress.setBounds(136, 174, 56, 14); 107 | add(progress); 108 | 109 | JLabel lblNewLabel2 = new JLabel("Progress Log"); 110 | lblNewLabel2.setHorizontalAlignment(SwingConstants.LEFT); 111 | lblNewLabel2.setBounds(8, 185, 70, 14); 112 | add(lblNewLabel2); 113 | 114 | progressLog = new JLabel("0.0%"); 115 | progressLog.setHorizontalAlignment(SwingConstants.RIGHT); 116 | progressLog.setBounds(136, 185, 56, 14); 117 | add(progressLog); 118 | 119 | JLabel estL = new JLabel("Est. time"); 120 | estL.setHorizontalAlignment(SwingConstants.LEFT); 121 | estL.setBounds(10, 36, 46, 14); 122 | add(estL); 123 | 124 | estTime = new JLabel("0:00:00.00"); 125 | estTime.setHorizontalAlignment(SwingConstants.RIGHT); 126 | estTime.setBounds(138, 36, 54, 14); 127 | add(estTime); 128 | 129 | JSeparator separator = new JSeparator(); 130 | separator.setBounds(10, 86, 182, 2); 131 | add(separator); 132 | 133 | JLabel lblError = new JLabel("Error"); 134 | lblError.setHorizontalAlignment(SwingConstants.LEFT); 135 | lblError.setBounds(8, 249, 46, 14); 136 | add(lblError); 137 | 138 | error = new JLabel("0"); 139 | error.setHorizontalAlignment(SwingConstants.RIGHT); 140 | error.setBounds(116, 249, 76, 14); 141 | add(error); 142 | 143 | JLabel lblDominantColor = new JLabel("Dominant color"); 144 | lblDominantColor.setBounds(8, 274, 85, 14); 145 | add(lblDominantColor); 146 | 147 | JLabel lblMeanColor = new JLabel("Mean color"); 148 | lblMeanColor.setBounds(8, 299, 85, 14); 149 | add(lblMeanColor); 150 | 151 | dominantColorIcon = new JLabel(""); 152 | dominantColorIcon.setOpaque(true); 153 | dominantColorIcon.setBorder(new EtchedBorder(Color.BLACK, Color.WHITE)); 154 | dominantColorIcon.setBackground(Color.WHITE); 155 | dominantColorIcon.setHorizontalAlignment(SwingConstants.RIGHT); 156 | dominantColorIcon.setBounds(170, 270, 22, 22); 157 | add(dominantColorIcon); 158 | 159 | averageColorIcon = new JLabel(""); 160 | averageColorIcon.setOpaque(true); 161 | averageColorIcon.setBorder(new EtchedBorder(Color.BLACK, Color.WHITE)); 162 | averageColorIcon.setBackground(Color.WHITE); 163 | averageColorIcon.setHorizontalAlignment(SwingConstants.RIGHT); 164 | averageColorIcon.setBounds(170, 295, 22, 22); 165 | add(averageColorIcon); 166 | 167 | JLabel verticesL = new JLabel("Vertices"); 168 | verticesL.setHorizontalAlignment(SwingConstants.LEFT); 169 | verticesL.setBounds(10, 61, 46, 14); 170 | add(verticesL); 171 | 172 | verticesAmt = new JLabel("50"); 173 | verticesAmt.setHorizontalAlignment(SwingConstants.RIGHT); 174 | verticesAmt.setBounds(138, 61, 54, 14); 175 | add(verticesAmt); 176 | 177 | JLabel evoRateL = new JLabel("Evolution rate"); 178 | evoRateL.setHorizontalAlignment(SwingConstants.LEFT); 179 | evoRateL.setBounds(8, 199, 79, 14); 180 | add(evoRateL); 181 | 182 | evolutionRate = new JLabel("0.0"); 183 | evolutionRate.setHorizontalAlignment(SwingConstants.RIGHT); 184 | evolutionRate.setBounds(136, 199, 56, 14); 185 | add(evolutionRate); 186 | 187 | JSeparator separator_1 = new JSeparator(); 188 | separator_1.setBounds(8, 324, 182, 2); 189 | add(separator_1); 190 | 191 | savePng = new JButton("Save PNG"); 192 | savePng.setEnabled(false); 193 | savePng.setBounds(8, 337, 85, 23); 194 | add(savePng); 195 | 196 | JLabel evoPerSecL = new JLabel("Evolutions per second"); 197 | evoPerSecL.setHorizontalAlignment(SwingConstants.LEFT); 198 | evoPerSecL.setBounds(8, 224, 116, 14); 199 | add(evoPerSecL); 200 | 201 | evolutionsPerSecond = new JLabel("0"); 202 | evolutionsPerSecond.setHorizontalAlignment(SwingConstants.RIGHT); 203 | evolutionsPerSecond.setBounds(136, 224, 56, 14); 204 | add(evolutionsPerSecond); 205 | 206 | colorInitBlack = new JRadioButton("Black"); 207 | colorInitBlack.setSelected(false); 208 | colorInitBlack.setHorizontalAlignment(SwingConstants.RIGHT); 209 | colorInitBlack.setFocusable(false); 210 | colorInitBlack.setHorizontalTextPosition(JRadioButton.LEFT); 211 | colorInitBlack.setBounds(116, 367, 76, 23); 212 | add(colorInitBlack); 213 | 214 | colorInitRandom = new JRadioButton("Random color"); 215 | colorInitRandom.setSelected(true); 216 | colorInitRandom.setHorizontalAlignment(SwingConstants.RIGHT); 217 | colorInitRandom.setFocusable(false); 218 | colorInitRandom.setHorizontalTextPosition(JRadioButton.LEFT); 219 | colorInitRandom.setBounds(83, 393, 109, 23); 220 | add(colorInitRandom); 221 | 222 | colorInitMean = new JRadioButton("Image mean"); 223 | colorInitMean.setHorizontalAlignment(SwingConstants.RIGHT); 224 | colorInitMean.setFocusable(false); 225 | colorInitMean.setHorizontalTextPosition(JRadioButton.LEFT); 226 | colorInitMean.setBounds(83, 419, 109, 23); 227 | add(colorInitMean); 228 | 229 | colorInitDominant = new JRadioButton("Image dominant"); 230 | colorInitDominant.setHorizontalAlignment(SwingConstants.RIGHT); 231 | colorInitDominant.setFocusable(false); 232 | colorInitDominant.setHorizontalTextPosition(JRadioButton.LEFT); 233 | colorInitDominant.setBounds(83, 445, 109, 23); 234 | add(colorInitDominant); 235 | 236 | JLabel colorInitL = new JLabel("Color initialization"); 237 | colorInitL.setHorizontalAlignment(SwingConstants.LEFT); 238 | colorInitL.setBounds(10, 371, 100, 14); 239 | add(colorInitL); 240 | 241 | ButtonGroup g = new ButtonGroup(); 242 | g.add(colorInitBlack); 243 | g.add(colorInitRandom); 244 | g.add(colorInitMean); 245 | g.add(colorInitDominant); 246 | 247 | genL = new JLabel("Generations"); 248 | genL.setHorizontalAlignment(SwingConstants.LEFT); 249 | genL.setBounds(8, 99, 85, 14); 250 | add(genL); 251 | 252 | generations = new JLabel("0"); 253 | generations.setHorizontalAlignment(SwingConstants.RIGHT); 254 | generations.setBounds(136, 99, 56, 14); 255 | add(generations); 256 | 257 | saveSvg = new JButton("Save SVG"); 258 | saveSvg.setEnabled(false); 259 | saveSvg.setBounds(105, 337, 85, 23); 260 | add(saveSvg); 261 | 262 | JButton startWebGui = new JButton("WEB"); 263 | startWebGui.setBounds(8, 419, 30, 30); 264 | startWebGui.setMargin(new Insets(0, 0, 0, 0)); 265 | add(startWebGui); 266 | 267 | startWebGui.addActionListener(ev -> { 268 | startWebGui.setEnabled(false); 269 | WebStats.launch(); 270 | }); 271 | 272 | 273 | Timer autoSaver = new Timer(); 274 | 275 | JButton crappyAutosaveButton = new JButton("CA"); 276 | crappyAutosaveButton.setBounds(8, 456, 30, 30); 277 | crappyAutosaveButton.setMargin(new Insets(0, 0, 0, 0)); 278 | add(crappyAutosaveButton); 279 | 280 | crappyAutosaveButton.addActionListener(ev -> { 281 | crappyAutosaveButton.setEnabled(false); 282 | 283 | JTextField pngPath = new JTextField(); 284 | JTextField svgPath = new JTextField(); 285 | Object[] message = { 286 | "Save images to:", null, 287 | "png:", pngPath, 288 | "svg:", svgPath 289 | }; 290 | 291 | int option = JOptionPane.showConfirmDialog(null, message, "Input", JOptionPane.OK_CANCEL_OPTION); 292 | if (option == JOptionPane.CANCEL_OPTION) { 293 | crappyAutosaveButton.setEnabled(true); 294 | return; 295 | } else { 296 | if(pngPath.getText().isEmpty() || svgPath.getText().isEmpty()) { 297 | JOptionPane.showMessageDialog(null, "ля у тебя пусто"); 298 | crappyAutosaveButton.setEnabled(true); 299 | return; 300 | } 301 | 302 | File f1 = new File(pngPath.getText()); 303 | if(!f1.exists() && !f1.mkdir()) { 304 | JOptionPane.showMessageDialog(null, "тупа неправильный путь: png"); 305 | crappyAutosaveButton.setEnabled(true); 306 | return; 307 | } 308 | File f2 = new File(pngPath.getText()); 309 | if(!f2.exists() && !f2.mkdir()) { 310 | JOptionPane.showMessageDialog(null, "тупа неправильный путь: svg"); 311 | crappyAutosaveButton.setEnabled(true); 312 | return; 313 | } 314 | } 315 | 316 | System.out.println("SVG save is disabled"); 317 | 318 | autoSaver.scheduleAtFixedRate(new TimerTask() { 319 | public void run() { 320 | File f1 = new File(pngPath.getText() + generations.getText() + ".png"); 321 | //File f2 = new File(pngPath.getText() + generations.getText() + ".svg"); 322 | 323 | BufferedImage copy = null; 324 | try { 325 | f1.createNewFile(); 326 | //f2.createNewFile(); 327 | 328 | copy = deepCopy(EvoImage.polygonizer.getImage()); 329 | ImageIO.write(copy, "png", f1); 330 | 331 | /*SVGGraphics2D g2 = new SVGGraphics2D(copy.getWidth() * 3, copy.getHeight() * 3); 332 | Polygon[] polygons = EvoImage.polygonizer.getPopulation().getPopulation(); 333 | g2.scale(3, 3); 334 | for (Polygon p : polygons) { 335 | g2.setColor(p.color); 336 | g2.fillPolygon(new int[] { (int) p.p1.x, (int) p.p2.x, (int) p.p3.x }, 337 | new int[] { (int) p.p1.y, (int) p.p2.y, (int) p.p3.y }, 3); 338 | } 339 | SVGUtils.writeToSVG(f2, g2.getSVGElement()); 340 | g2.dispose(); 341 | copy.flush();*/ 342 | } catch (IOException e) { 343 | e.printStackTrace(); 344 | } 345 | } 346 | }, 5000, TimeUnit.MILLISECONDS.convert(180, TimeUnit.SECONDS)); 347 | }); 348 | 349 | savePng.addActionListener(ev -> { 350 | JFileChooser chooser = new JFileChooser(){ 351 | @Override 352 | public void approveSelection(){ 353 | File f = getSelectedFile(); 354 | if(f.exists() && getDialogType() == SAVE_DIALOG){ 355 | int result = JOptionPane.showConfirmDialog(this, "The file exists, overwrite?", "Existing file", JOptionPane.YES_NO_OPTION); 356 | switch(result){ 357 | case JOptionPane.YES_OPTION: 358 | super.approveSelection(); 359 | return; 360 | case JOptionPane.NO_OPTION: 361 | return; 362 | case JOptionPane.CLOSED_OPTION: 363 | return; 364 | case JOptionPane.CANCEL_OPTION: 365 | cancelSelection(); 366 | return; 367 | } 368 | } 369 | super.approveSelection(); 370 | } 371 | }; 372 | chooser.setAcceptAllFileFilterUsed(true); 373 | if(chooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { 374 | File f = chooser.getSelectedFile(); 375 | if(!f.exists()) { 376 | try { 377 | f.createNewFile(); 378 | } catch (IOException e) { 379 | e.printStackTrace(); 380 | } 381 | } 382 | try { 383 | BufferedImage copy = deepCopy(EvoImage.polygonizer.getImage()); 384 | ImageIO.write(copy, "png", f); 385 | } catch (IOException e) { 386 | e.printStackTrace(); 387 | } 388 | } 389 | }); 390 | 391 | saveSvg.addActionListener(ev -> { 392 | JFileChooser chooser = new JFileChooser(){ 393 | @Override 394 | public void approveSelection(){ 395 | File f = getSelectedFile(); 396 | if(f.exists() && getDialogType() == SAVE_DIALOG){ 397 | int result = JOptionPane.showConfirmDialog(this, "The file exists, overwrite?", "Existing file", JOptionPane.YES_NO_OPTION); 398 | switch(result){ 399 | case JOptionPane.YES_OPTION: 400 | super.approveSelection(); 401 | return; 402 | case JOptionPane.NO_OPTION: 403 | return; 404 | case JOptionPane.CLOSED_OPTION: 405 | return; 406 | case JOptionPane.CANCEL_OPTION: 407 | cancelSelection(); 408 | return; 409 | } 410 | } 411 | super.approveSelection(); 412 | } 413 | }; 414 | chooser.setAcceptAllFileFilterUsed(true); 415 | if(chooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { 416 | File f = chooser.getSelectedFile(); 417 | if(!f.exists()) { 418 | try { 419 | f.createNewFile(); 420 | } catch (IOException e) { 421 | e.printStackTrace(); 422 | } 423 | } 424 | try { 425 | BufferedImage notcopy = EvoImage.polygonizer.getImage(); 426 | 427 | SVGGraphics2D g2 = new SVGGraphics2D(notcopy.getWidth()*3, notcopy.getHeight()*3); 428 | Polygon[] polygons = EvoImage.polygonizer.getPopulation().getPopulation(); 429 | g2.scale(3, 3); 430 | for (Polygon p : polygons) { 431 | g2.setColor(p.color); 432 | g2.fillPolygon(new int[] {(int) p.p1.x, (int) p.p2.x, (int) p.p3.x}, new int[] {(int) p.p1.y, (int) p.p2.y, (int) p.p3.y}, 3); 433 | } 434 | SVGUtils.writeToSVG(f, g2.getSVGElement()); 435 | g2.dispose(); 436 | } catch (IOException e) { 437 | e.printStackTrace(); 438 | } 439 | } 440 | }); 441 | } 442 | 443 | public static BufferedImage deepCopy(BufferedImage bi) { 444 | ColorModel cm = bi.getColorModel(); 445 | boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); 446 | System.out.println(isAlphaPremultiplied); 447 | WritableRaster raster = bi.copyData(null); 448 | return new BufferedImage(cm, raster, isAlphaPremultiplied, null); 449 | } 450 | 451 | public static int colorInitMode() { 452 | if(colorInitBlack.isSelected()) 453 | return 0; 454 | if(colorInitRandom.isSelected()) 455 | return 1; 456 | if(colorInitMean.isSelected()) 457 | return 2; 458 | if(colorInitDominant.isSelected()) 459 | return 3; 460 | 461 | throw new RuntimeException("No init mode"); 462 | } 463 | 464 | public void enableSaveButton() { 465 | savePng.setEnabled(true); 466 | saveSvg.setEnabled(true); 467 | } 468 | 469 | public void updateInformation(long sinceStart, long est, int usefulMutatuions, int uselessMutatuions, double progress, double error, Color dominantColor, Color averageColor, int generaions) { 470 | this.timeSinceStart.setText(convertSecondsToHMmSs(sinceStart)); 471 | // this.estTime.setText("lol idk"); 472 | this.usefulMutatuions.setText(String.valueOf(usefulMutatuions)); 473 | this.uselessMutatuions.setText(String.valueOf(uselessMutatuions)); 474 | this.progress.setText(String.format("%.1f", progress) + "%"); 475 | this.progressLog.setText(String.format("%.1f", Math.log(progress)) + "%"); 476 | this.evolutionRate.setText(String.format("%.1f", ((double)usefulMutatuions) / (double)(uselessMutatuions) - 1)); 477 | this.error.setText(String.format("%.1f", error)); 478 | this.dominantColorIcon.setBackground(dominantColor); 479 | this.dominantColorIcon.setToolTipText(dominantColor.toString()); 480 | this.averageColorIcon.setBackground(averageColor); 481 | this.averageColorIcon.setToolTipText(averageColor.toString()); 482 | this.generations.setText(String.valueOf(generaions)); 483 | 484 | WebStats.updateInformation(sinceStart, est, usefulMutatuions, uselessMutatuions, progress, error, dominantColor, averageColor, generaions); 485 | } 486 | 487 | public void updateFPSInformation() { 488 | this.evolutionsPerSecond.setText(String.valueOf(mutationsIncrement)); 489 | mutationsIncrement = 0; 490 | } 491 | 492 | public void setVertexCount(int count) { 493 | verticesAmt.setText(String.valueOf(count)); 494 | } 495 | 496 | public static String convertSecondsToHMmSs(long nanos) { 497 | long millis = nanos / 1000000; 498 | long seconds = millis / 1000; 499 | long s = seconds % 60; 500 | long m = (seconds / 60) % 60; 501 | long h = (seconds / (60 * 60)) % 24; 502 | return String.format("%d:%02d:%02d", h,m,s); 503 | } 504 | } --------------------------------------------------------------------------------