From a2d61249cc1db16f62abad98ea777b168f187b99 Mon Sep 17 00:00:00 2001 From: dfsek Date: Tue, 16 Mar 2021 18:22:39 -0700 Subject: [PATCH 01/12] begin work on ide type thing --- build.gradle.kts | 6 + src/main/java/com/dfsek/noise/NoiseTool.java | 381 ++++++++++-------- .../com/dfsek/noise/swing/GoToLineAction.java | 47 +++ .../dfsek/noise/swing/LookAndFeelAction.java | 38 ++ .../noise/swing/MutableBooleanAction.java | 20 + .../com/dfsek/noise/swing/NoisePanel.java | 173 ++++++++ .../noise/swing/ShowFindDialogAction.java | 31 ++ .../noise/swing/ShowReplaceDialogAction.java | 31 ++ .../java/com/dfsek/noise/swing/StatusBar.java | 26 ++ .../dfsek/noise/swing/UpdateNoiseAction.java | 18 + 10 files changed, 609 insertions(+), 162 deletions(-) create mode 100644 src/main/java/com/dfsek/noise/swing/GoToLineAction.java create mode 100644 src/main/java/com/dfsek/noise/swing/LookAndFeelAction.java create mode 100644 src/main/java/com/dfsek/noise/swing/MutableBooleanAction.java create mode 100644 src/main/java/com/dfsek/noise/swing/NoisePanel.java create mode 100644 src/main/java/com/dfsek/noise/swing/ShowFindDialogAction.java create mode 100644 src/main/java/com/dfsek/noise/swing/ShowReplaceDialogAction.java create mode 100644 src/main/java/com/dfsek/noise/swing/StatusBar.java create mode 100644 src/main/java/com/dfsek/noise/swing/UpdateNoiseAction.java diff --git a/build.gradle.kts b/build.gradle.kts index 623f238..9bb3480 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,6 +20,12 @@ dependencies { implementation("commons-io:commons-io:2.8.0") implementation("net.jafama:jafama:2.3.2") implementation("org.ow2.asm:asm:9.0") + + implementation("com.fifesoft:rstaui:3.1.1") + implementation("com.fifesoft:rsyntaxtextarea:3.1.2") + + + implementation("com.formdev:flatlaf:1.0") } diff --git a/src/main/java/com/dfsek/noise/NoiseTool.java b/src/main/java/com/dfsek/noise/NoiseTool.java index deaa144..73b7308 100644 --- a/src/main/java/com/dfsek/noise/NoiseTool.java +++ b/src/main/java/com/dfsek/noise/NoiseTool.java @@ -1,205 +1,262 @@ package com.dfsek.noise; -import com.dfsek.tectonic.exception.ConfigException; -import com.dfsek.tectonic.loading.ConfigLoader; -import com.dfsek.terra.api.math.ProbabilityCollection; -import com.dfsek.terra.api.math.noise.NoiseSampler; -import com.dfsek.terra.api.util.seeded.NoiseSeeded; -import com.dfsek.terra.config.GenericLoaders; -import com.dfsek.terra.config.fileloaders.FolderLoader; -import com.dfsek.terra.config.loaders.ProbabilityCollectionLoader; -import com.dfsek.terra.config.loaders.config.BufferedImageLoader; -import com.dfsek.terra.config.loaders.config.sampler.NoiseSamplerBuilderLoader; -import com.dfsek.terra.registry.config.NoiseRegistry; -import org.apache.commons.io.FileUtils; - -import javax.swing.*; import java.awt.*; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.FileInputStream; +import java.awt.event.*; import java.io.IOException; -import java.nio.file.Paths; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.atomic.AtomicInteger; - -public class NoiseTool { - public static void main(String... args) throws ConfigException, IOException { - JFrame frame = new JFrame("Noise Viewer"); - - AtomicInteger seed = new AtomicInteger(2403); - JLabel label = new JLabel(new ImageIcon(load(seed.get(), false, false))); - frame.add(label); - frame.pack(); - frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - - - frame.addKeyListener(new KeyListener() { - @Override - public void keyTyped(KeyEvent e) { - if(e.getKeyChar() == 'r') { - try { - label.setIcon(new ImageIcon(load(seed.get(), false, false))); - } catch(ConfigException | IOException configException) { - configException.printStackTrace(); - } - } else if(e.getKeyChar() == 's') { - try { - seed.set(ThreadLocalRandom.current().nextInt()); - label.setIcon(new ImageIcon(load(seed.get(), false, false))); - } catch(ConfigException | IOException configException) { - configException.printStackTrace(); - } - } else if(e.getKeyChar() == 'd') { - try { - label.setIcon(new ImageIcon(load(seed.get(), true, false))); - } catch(ConfigException | IOException configException) { - configException.printStackTrace(); - } - } else if(e.getKeyChar() == 'c') { - try { - label.setIcon(new ImageIcon(load(seed.get(), false, true))); - } catch(ConfigException | IOException configException) { - configException.printStackTrace(); - } - } - } +import java.nio.charset.StandardCharsets; - @Override - public void keyPressed(KeyEvent e) { - } +import javax.swing.*; +import javax.swing.UIManager.LookAndFeelInfo; - @Override - public void keyReleased(KeyEvent e) { - } - }); +import com.dfsek.noise.swing.*; +import com.formdev.flatlaf.FlatDarculaLaf; +import com.formdev.flatlaf.FlatDarkLaf; +import com.formdev.flatlaf.FlatLightLaf; +import org.apache.commons.io.IOUtils; +import org.fife.rsta.ui.CollapsibleSectionPanel; +import org.fife.rsta.ui.search.FindDialog; +import org.fife.rsta.ui.search.ReplaceDialog; +import org.fife.rsta.ui.search.ReplaceToolBar; +import org.fife.rsta.ui.search.SearchEvent; +import org.fife.rsta.ui.search.SearchListener; +import org.fife.rsta.ui.search.FindToolBar; +import org.fife.ui.rsyntaxtextarea.ErrorStrip; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rsyntaxtextarea.SyntaxConstants; +import org.fife.ui.rtextarea.RTextScrollPane; +import org.fife.ui.rtextarea.SearchContext; +import org.fife.ui.rtextarea.SearchEngine; +import org.fife.ui.rtextarea.SearchResult; - frame.setVisible(true); - } - private static int normal(double in, double out, double min, double max) { - double range = max - min; - return (int) (((in - min) * out) / range); - } +public final class NoiseTool extends JFrame implements SearchListener { - private static int buildRGBA(int in) { - return (255 << 24) - + (in << 16) - + (in << 8) - + in; - } + private final CollapsibleSectionPanel csp; + private final RSyntaxTextArea textArea; + private FindDialog findDialog; + private ReplaceDialog replaceDialog; + private FindToolBar findToolBar; + private ReplaceToolBar replaceToolBar; + private final StatusBar statusBar; - private static BufferedImage load(int seed, boolean distribution, boolean chunk) throws ConfigException, IOException { - long s = System.nanoTime(); + private final NoisePanel noise; - FolderLoader folderLoader = new FolderLoader(Paths.get("./")); - ConfigLoader loader = new ConfigLoader(); - loader.registerLoader(NoiseSeeded.class, new NoiseSamplerBuilderLoader(new NoiseRegistry())) - .registerLoader(BufferedImage.class, new BufferedImageLoader(folderLoader)) - .registerLoader(ProbabilityCollection.class, new ProbabilityCollectionLoader()); + private NoiseTool() { + initSearchDialogs(); - new GenericLoaders(null).register(loader); - NoiseConfigTemplate template = new NoiseConfigTemplate(); + JPanel contentPane = new JPanel(new BorderLayout()); - File file = new File("./config.yml"); + GridLayout layout = new GridLayout(1, 2); + setLayout(layout); - System.out.println(file.getAbsolutePath()); + textArea = new RSyntaxTextArea(25, 80); + textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_YAML); + textArea.setCodeFoldingEnabled(true); + textArea.setMarkOccurrences(true); - File colorFile = new File("./color.yml"); + this.noise = new NoisePanel(textArea); - System.out.println(file.getAbsolutePath()); - if(!file.exists()) { - file.getParentFile().mkdirs(); - FileUtils.copyInputStreamToFile(NoiseTool.class.getResourceAsStream("/config.yml"), file); - } + add(contentPane); + add(noise); + csp = new CollapsibleSectionPanel(); + contentPane.add(csp); - boolean colors = false; - ColorConfigTemplate color = new ColorConfigTemplate(); - if(colorFile.exists()) { - loader.load(color, new FileInputStream(colorFile)); - colors = color.enable(); - } - ProbabilityCollection colorCollection = color.getColors(); + setJMenuBar(createMenuBar()); - loader.load(template, new FileInputStream(file)); - System.out.println(template.getBuilder().getDimensions()); - NoiseSampler noise = template.getBuilder().apply((long) seed); + try { + textArea.setText(IOUtils.toString(NoiseTool.class.getResourceAsStream("/config.yml"), StandardCharsets.UTF_8)); + } catch (IOException e) { + e.printStackTrace(); + } - int size = 1024; + RTextScrollPane sp = new RTextScrollPane(textArea); + csp.add(sp); - BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); + ErrorStrip errorStrip = new ErrorStrip(textArea); + contentPane.add(errorStrip, BorderLayout.LINE_END); - double[][] noiseVals = new double[size][size]; - int[][] rgbVals = new int[size][size]; - double max = Double.MIN_VALUE; - double min = Double.MAX_VALUE; + statusBar = new StatusBar(); + contentPane.add(statusBar, BorderLayout.SOUTH); - int[] buckets = new int[1024]; + setTitle("Noise Tool"); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - for(int x = 0; x < noiseVals.length; x++) { - for(int z = 0; z < noiseVals[x].length; z++) { - double n = noise.getNoise(x, z); - noiseVals[x][z] = n; - max = Math.max(n, max); - min = Math.min(n, min); - if(colors) rgbVals[x][z] = colorCollection.get(noise, x, z); - } + FlatDarculaLaf.install(); + + SwingUtilities.updateComponentTreeUI(NoiseTool.this); + if (findDialog!=null) { + findDialog.updateUI(); + replaceDialog.updateUI(); } + pack(); - for(int x = 0; x < noiseVals.length; x++) { - for(int z = 0; z < noiseVals[x].length; z++) { - if(colors) image.setRGB(x, z, rgbVals[x][z] + (255 << 24)); - else image.setRGB(x, z, buildRGBA(normal(noiseVals[x][z], 255, min, max))); - buckets[normal(noiseVals[x][z], size - 1, min, max)]++; - } + pack(); + setLocationRelativeTo(null); + + } + + + private void addItem(Action a, ButtonGroup bg, JMenu menu) { + JRadioButtonMenuItem item = new JRadioButtonMenuItem(a); + bg.add(item); + menu.add(item); + } + + + private JMenuBar createMenuBar() { + + JMenuBar mb = new JMenuBar(); + JMenu menu = new JMenu("Search"); + menu.add(new JMenuItem(new ShowFindDialogAction(this))); + menu.add(new JMenuItem(new ShowReplaceDialogAction(this))); + menu.add(new JMenuItem(new GoToLineAction(this))); + menu.addSeparator(); + + int ctrl = getToolkit().getMenuShortcutKeyMask(); + int shift = InputEvent.SHIFT_MASK; + KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F, ctrl|shift); + Action a = csp.addBottomComponent(ks, findToolBar); + a.putValue(Action.NAME, "Show Find Search Bar"); + menu.add(new JMenuItem(a)); + ks = KeyStroke.getKeyStroke(KeyEvent.VK_H, ctrl|shift); + a = csp.addBottomComponent(ks, replaceToolBar); + a.putValue(Action.NAME, "Show Replace Search Bar"); + menu.add(new JMenuItem(a)); + + mb.add(menu); + + menu = new JMenu("Theme"); + ButtonGroup bg = new ButtonGroup(); + FlatLightLaf.installLafInfo(); + FlatDarculaLaf.installLafInfo(); + FlatDarkLaf.installLafInfo(); + LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels(); + for (LookAndFeelInfo info : infos) { + addItem(new LookAndFeelAction(this, info), bg, menu); } + mb.add(menu); + + menu = new JMenu("Noise"); + menu.add(new UpdateNoiseAction(noise)); + menu.add(new MutableBooleanAction(noise.getChunk(), "Toggle Chunk Borders")); + menu.add(new MutableBooleanAction(noise.getDistribution(), "Toggle Distribution Plot")); + mb.add(menu); + + return mb; + + } + + + @Override + public String getSelectedText() { + return textArea.getSelectedText(); + } + + + /** + * Creates our Find and Replace dialogs. + */ + private void initSearchDialogs() { + + findDialog = new FindDialog(this, this); + replaceDialog = new ReplaceDialog(this, this); - long time = System.nanoTime() - s; + // This ties the properties of the two dialogs together (match case, + // regex, etc.). + SearchContext context = findDialog.getSearchContext(); + replaceDialog.setSearchContext(context); - double ms = time / 1000000d; + // Create tool bars and tie their search contexts together also. + findToolBar = new FindToolBar(this); + findToolBar.setSearchContext(context); + replaceToolBar = new ReplaceToolBar(this); + replaceToolBar.setSearchContext(context); + + } - if(chunk) { - for(int x = 0; x < image.getWidth(); x += 16) { - for(int y = 0; y < image.getHeight(); y++) image.setRGB(x, y, buildRGBA(0)); + /** + * Listens for events from our search dialogs and actually does the dirty + * work. + */ + @Override + public void searchEvent(SearchEvent e) { + + SearchEvent.Type type = e.getType(); + SearchContext context = e.getSearchContext(); + SearchResult result; + + switch (type) { + default: // Prevent FindBugs warning later + case MARK_ALL: + result = SearchEngine.markAll(textArea, context); + break; + case FIND: + result = SearchEngine.find(textArea, context); + if (!result.wasFound() || result.isWrapped()) { + UIManager.getLookAndFeel().provideErrorFeedback(textArea); + } + break; + case REPLACE: + result = SearchEngine.replace(textArea, context); + if (!result.wasFound() || result.isWrapped()) { + UIManager.getLookAndFeel().provideErrorFeedback(textArea); + } + break; + case REPLACE_ALL: + result = SearchEngine.replaceAll(textArea, context); + JOptionPane.showMessageDialog(null, result.getCount() + + " occurrences replaced."); + break; + } + + String text; + if (result.wasFound()) { + text = "Text found; occurrences marked: " + result.getMarkedCount(); + } + else if (type==SearchEvent.Type.MARK_ALL) { + if (result.getMarkedCount()>0) { + text = "Occurrences marked: " + result.getMarkedCount(); } - for(int y = 0; y < image.getWidth(); y += 16) { - for(int x = 0; x < image.getHeight(); x++) image.setRGB(x, y, buildRGBA(0)); + else { + text = ""; } } + else { + text = "Text not found"; + } + statusBar.setLabel(text); - Graphics graphics = image.getGraphics(); - graphics.setColor(Color.WHITE); - graphics.fillRect(0, 0, 325, 90); - graphics.setColor(Color.BLACK); - graphics.setFont(new Font("Monospace", Font.BOLD, 20)); - graphics.drawString("min: " + min, 0, 20); - graphics.drawString("max: " + max, 0, 40); - graphics.drawString("seed: " + seed, 0, 60); - graphics.drawString("time: " + ms + "ms", 0, 80); - - if(distribution) { - graphics.setColor(Color.WHITE); - graphics.fillRect(0, size - (size / 4) - 1, size, (size / 4) - 1); - int highestBucket = Integer.MIN_VALUE; - for(int i : buckets) highestBucket = Math.max(highestBucket, i); - graphics.setColor(Color.BLACK); - graphics.drawString("" + highestBucket, 0, size - (size / 4) - 1 + 20); - - for(int x = 0; x < size; x++) { - for(int y = 0; y < ((double) buckets[x] / highestBucket) * ((double) size / 4); y++) { - image.setRGB(x, size - y - 1, buildRGBA(0)); - } + } + + + public static void main(String[] args) { + SwingUtilities.invokeLater(() -> { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); +// UIManager.setLookAndFeel("org.pushingpixels.substance.api.skin.SubstanceGraphiteAquaLookAndFeel"); + } catch (Exception e) { + e.printStackTrace(); } + new NoiseTool().setVisible(true); + }); + } - } + public ReplaceDialog getReplaceDialog() { + return replaceDialog; + } + + public FindDialog getFindDialog() { + return findDialog; + } - return image; + public RSyntaxTextArea getTextArea() { + return textArea; } + + } diff --git a/src/main/java/com/dfsek/noise/swing/GoToLineAction.java b/src/main/java/com/dfsek/noise/swing/GoToLineAction.java new file mode 100644 index 0000000..b760dcf --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/GoToLineAction.java @@ -0,0 +1,47 @@ +package com.dfsek.noise.swing; + +import com.dfsek.noise.NoiseTool; +import org.fife.rsta.ui.GoToDialog; + +import javax.swing.*; +import javax.swing.text.BadLocationException; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +/** + * Opens the "Go to Line" dialog. + */ +public class GoToLineAction extends AbstractAction { + + private final NoiseTool noiseTool; + + public GoToLineAction(NoiseTool noiseTool) { + super("Go To Line..."); + this.noiseTool = noiseTool; + int c = noiseTool.getToolkit().getMenuShortcutKeyMask(); + putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_L, c)); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (noiseTool.getFindDialog().isVisible()) { + noiseTool.getFindDialog().setVisible(false); + } + if (noiseTool.getReplaceDialog().isVisible()) { + noiseTool.getReplaceDialog().setVisible(false); + } + GoToDialog dialog = new GoToDialog(noiseTool); + dialog.setMaxLineNumberAllowed(noiseTool.getTextArea().getLineCount()); + dialog.setVisible(true); + int line = dialog.getLineNumber(); + if (line > 0) { + try { + noiseTool.getTextArea().setCaretPosition(noiseTool.getTextArea().getLineStartOffset(line - 1)); + } catch (BadLocationException ble) { // Never happens + UIManager.getLookAndFeel().provideErrorFeedback(noiseTool.getFindDialog()); + ble.printStackTrace(); + } + } + } + +} diff --git a/src/main/java/com/dfsek/noise/swing/LookAndFeelAction.java b/src/main/java/com/dfsek/noise/swing/LookAndFeelAction.java new file mode 100644 index 0000000..4b0e795 --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/LookAndFeelAction.java @@ -0,0 +1,38 @@ +package com.dfsek.noise.swing; + +import com.dfsek.noise.NoiseTool; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +/** + * Changes the Look and Feel. + */ +public class LookAndFeelAction extends AbstractAction { + + private final NoiseTool noiseTool; + private final UIManager.LookAndFeelInfo info; + + public LookAndFeelAction(NoiseTool noiseTool, UIManager.LookAndFeelInfo info) { + this.noiseTool = noiseTool; + putValue(NAME, info.getName()); + this.info = info; + } + + @Override + public void actionPerformed(ActionEvent e) { + try { + UIManager.setLookAndFeel(info.getClassName()); + SwingUtilities.updateComponentTreeUI(noiseTool); + if (noiseTool.getFindDialog() != null) { + noiseTool.getFindDialog().updateUI(); + noiseTool.getReplaceDialog().updateUI(); + } + noiseTool.pack(); + } catch (RuntimeException re) { + throw re; // FindBugs + } catch (Exception ex) { + ex.printStackTrace(); + } + } +} diff --git a/src/main/java/com/dfsek/noise/swing/MutableBooleanAction.java b/src/main/java/com/dfsek/noise/swing/MutableBooleanAction.java new file mode 100644 index 0000000..3778ef5 --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/MutableBooleanAction.java @@ -0,0 +1,20 @@ +package com.dfsek.noise.swing; + +import com.dfsek.terra.api.util.mutable.MutableBoolean; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class MutableBooleanAction extends AbstractAction { + private final MutableBoolean mutableBoolean; + + public MutableBooleanAction(MutableBoolean mutableBoolean, String action) { + super(action); + this.mutableBoolean = mutableBoolean; + } + + @Override + public void actionPerformed(ActionEvent actionEvent) { + mutableBoolean.invert(); + } +} diff --git a/src/main/java/com/dfsek/noise/swing/NoisePanel.java b/src/main/java/com/dfsek/noise/swing/NoisePanel.java new file mode 100644 index 0000000..d6c4910 --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/NoisePanel.java @@ -0,0 +1,173 @@ +package com.dfsek.noise.swing; + +import com.dfsek.noise.ColorConfigTemplate; +import com.dfsek.noise.NoiseConfigTemplate; +import com.dfsek.tectonic.exception.ConfigException; +import com.dfsek.tectonic.loading.ConfigLoader; +import com.dfsek.terra.api.math.ProbabilityCollection; +import com.dfsek.terra.api.math.noise.NoiseSampler; +import com.dfsek.terra.api.util.mutable.MutableBoolean; +import com.dfsek.terra.api.util.seeded.NoiseSeeded; +import com.dfsek.terra.config.GenericLoaders; +import com.dfsek.terra.config.fileloaders.FolderLoader; +import com.dfsek.terra.config.loaders.ProbabilityCollectionLoader; +import com.dfsek.terra.config.loaders.config.BufferedImageLoader; +import com.dfsek.terra.config.loaders.config.sampler.NoiseSamplerBuilderLoader; +import com.dfsek.terra.registry.config.NoiseRegistry; +import net.jafama.FastMath; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.nio.file.Paths; + +public class NoisePanel extends JPanel { + private final RSyntaxTextArea textArea; + private final JLabel image; + private MutableBoolean chunk = new MutableBoolean(); + private MutableBoolean distribution = new MutableBoolean(); + + public NoisePanel(RSyntaxTextArea textArea) { + this.textArea = textArea; + this.image = new JLabel(); + add(image); + } + + public void update() { + try { + image.setIcon(new ImageIcon(getImage(2403))); + } catch (ConfigException | FileNotFoundException e) { + e.printStackTrace(); + } + } + + public MutableBoolean getChunk() { + return chunk; + } + + public MutableBoolean getDistribution() { + return distribution; + } + + private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundException { + long s = System.nanoTime(); + + FolderLoader folderLoader = new FolderLoader(Paths.get("./")); + + ConfigLoader loader = new ConfigLoader(); + loader.registerLoader(NoiseSeeded.class, new NoiseSamplerBuilderLoader(new NoiseRegistry())) + .registerLoader(BufferedImage.class, new BufferedImageLoader(folderLoader)) + .registerLoader(ProbabilityCollection.class, new ProbabilityCollectionLoader()); + + new GenericLoaders(null).register(loader); + NoiseConfigTemplate template = new NoiseConfigTemplate(); + + File file = new File("./config.yml"); + + System.out.println(file.getAbsolutePath()); + + File colorFile = new File("./color.yml"); + + + boolean colors = false; + ColorConfigTemplate color = new ColorConfigTemplate(); + if(colorFile.exists()) { + loader.load(color, new FileInputStream(colorFile)); + colors = color.enable(); + } + ProbabilityCollection colorCollection = color.getColors(); + + loader.load(template, textArea.getText()); + System.out.println(template.getBuilder().getDimensions()); + NoiseSampler noise = template.getBuilder().apply((long) seed); + + + int sizeX = getWidth(); + int sizeY = getHeight(); + + BufferedImage image = new BufferedImage(sizeX, sizeY, BufferedImage.TYPE_INT_ARGB); + + double[][] noiseVals = new double[sizeX][sizeY]; + int[][] rgbVals = new int[sizeX][sizeY]; + double max = Double.MIN_VALUE; + double min = Double.MAX_VALUE; + + int[] buckets = new int[1024]; + + for(int x = 0; x < noiseVals.length; x++) { + for(int z = 0; z < noiseVals[x].length; z++) { + double n = noise.getNoise(x, z); + noiseVals[x][z] = n; + max = Math.max(n, max); + min = Math.min(n, min); + if(colors) rgbVals[x][z] = colorCollection.get(noise, x, z); + } + } + + for(int x = 0; x < noiseVals.length; x++) { + for(int z = 0; z < noiseVals[x].length; z++) { + if(colors) image.setRGB(x, z, rgbVals[x][z] + (255 << 24)); + else image.setRGB(x, z, buildRGBA(normal(noiseVals[x][z], 255, min, max))); + buckets[normal(noiseVals[x][z], sizeX - 1, min, max)]++; + } + } + + long time = System.nanoTime() - s; + + double ms = time / 1000000d; + + + if(chunk.get()) { + for(int x = 0; x < FastMath.floorDiv(image.getWidth(), 16); x++) { + for(int y = 0; y < image.getHeight(); y++) image.setRGB(x*16, y, buildRGBA(0)); + } + for(int y = 0; y < FastMath.floorDiv(image.getHeight(), 16); y++) { + for(int x = 0; x < image.getWidth(); x++) image.setRGB(x, y*16, buildRGBA(0)); + } + } + + Graphics graphics = image.getGraphics(); + graphics.setColor(Color.WHITE); + graphics.fillRect(0, 0, 325, 90); + graphics.setColor(Color.BLACK); + graphics.setFont(new Font("Monospace", Font.BOLD, 20)); + graphics.drawString("min: " + min, 0, 20); + graphics.drawString("max: " + max, 0, 40); + graphics.drawString("seed: " + seed, 0, 60); + graphics.drawString("time: " + ms + "ms", 0, 80); + + if(distribution.get()) { + graphics.setColor(Color.WHITE); + graphics.fillRect(0, sizeY - (sizeY / 4) - 1, sizeX, (sizeY / 4) - 1); + int highestBucket = Integer.MIN_VALUE; + for(int i : buckets) highestBucket = Math.max(highestBucket, i); + graphics.setColor(Color.BLACK); + graphics.drawString("" + highestBucket, 0, sizeY - (sizeY / 4) - 1 + 20); + + for(int x = 0; x < sizeX; x++) { + for(int y = 0; y < ((double) buckets[x] / highestBucket) * ((double) sizeY / 4); y++) { + image.setRGB(x, sizeY - y - 1, buildRGBA(0)); + } + } + + } + + return image; + } + + private static int normal(double in, double out, double min, double max) { + double range = max - min; + return (int) (((in - min) * out) / range); + } + + private static int buildRGBA(int in) { + return (255 << 24) + + (in << 16) + + (in << 8) + + in; + } +} diff --git a/src/main/java/com/dfsek/noise/swing/ShowFindDialogAction.java b/src/main/java/com/dfsek/noise/swing/ShowFindDialogAction.java new file mode 100644 index 0000000..e135ed0 --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/ShowFindDialogAction.java @@ -0,0 +1,31 @@ +package com.dfsek.noise.swing; + +import com.dfsek.noise.NoiseTool; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +/** + * Shows the Find dialog. + */ +public class ShowFindDialogAction extends AbstractAction { + + private final NoiseTool noiseTool; + + public ShowFindDialogAction(NoiseTool noiseTool) { + super("Find..."); + this.noiseTool = noiseTool; + int c = noiseTool.getToolkit().getMenuShortcutKeyMask(); + putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F, c)); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (noiseTool.getReplaceDialog().isVisible()) { + noiseTool.getReplaceDialog().setVisible(false); + } + noiseTool.getFindDialog().setVisible(true); + } + +} diff --git a/src/main/java/com/dfsek/noise/swing/ShowReplaceDialogAction.java b/src/main/java/com/dfsek/noise/swing/ShowReplaceDialogAction.java new file mode 100644 index 0000000..8ef233f --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/ShowReplaceDialogAction.java @@ -0,0 +1,31 @@ +package com.dfsek.noise.swing; + +import com.dfsek.noise.NoiseTool; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +/** + * Shows the Replace dialog. + */ +public class ShowReplaceDialogAction extends AbstractAction { + + private final NoiseTool noiseTool; + + public ShowReplaceDialogAction(NoiseTool noiseTool) { + super("Replace..."); + this.noiseTool = noiseTool; + int c = noiseTool.getToolkit().getMenuShortcutKeyMask(); + putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_H, c)); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (noiseTool.getFindDialog().isVisible()) { + noiseTool.getFindDialog().setVisible(false); + } + noiseTool.getReplaceDialog().setVisible(true); + } + +} diff --git a/src/main/java/com/dfsek/noise/swing/StatusBar.java b/src/main/java/com/dfsek/noise/swing/StatusBar.java new file mode 100644 index 0000000..ca30716 --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/StatusBar.java @@ -0,0 +1,26 @@ +package com.dfsek.noise.swing; + +import org.fife.rsta.ui.SizeGripIcon; + +import javax.swing.*; +import java.awt.*; + +/** + * The status bar for this application. + */ +public class StatusBar extends JPanel { + + private final JLabel label; + + public StatusBar() { + label = new JLabel("Ready"); + setLayout(new BorderLayout()); + add(label, BorderLayout.LINE_START); + add(new JLabel(new SizeGripIcon()), BorderLayout.LINE_END); + } + + public void setLabel(String label) { + this.label.setText(label); + } + +} diff --git a/src/main/java/com/dfsek/noise/swing/UpdateNoiseAction.java b/src/main/java/com/dfsek/noise/swing/UpdateNoiseAction.java new file mode 100644 index 0000000..b0a0be5 --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/UpdateNoiseAction.java @@ -0,0 +1,18 @@ +package com.dfsek.noise.swing; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class UpdateNoiseAction extends AbstractAction { + private final NoisePanel noisePanel; + + public UpdateNoiseAction(NoisePanel noisePanel) { + super("Render"); + this.noisePanel = noisePanel; + } + + @Override + public void actionPerformed(ActionEvent actionEvent) { + noisePanel.update(); + } +} From 62496c3331c53c1ccee5a4990e5a5e7be5f4eaaa Mon Sep 17 00:00:00 2001 From: dfsek Date: Tue, 16 Mar 2021 19:47:52 -0700 Subject: [PATCH 02/12] autocompletion --- build.gradle.kts | 3 +- .../com/dfsek/noise/ColorConfigTemplate.java | 2 +- src/main/java/com/dfsek/noise/NoiseTool.java | 40 +++++++++++++++++++ .../com/dfsek/noise/swing/NoisePanel.java | 2 +- .../dfsek/noise/swing/UpdateNoiseAction.java | 1 + src/main/resources/config.yml | 19 +++++---- 6 files changed, 57 insertions(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 9bb3480..4f102d9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,7 +14,7 @@ repositories { dependencies { testCompile("junit", "junit", "4.12") implementation("com.dfsek:Tectonic:1.2.3") - implementation("com.dfsek.terra.common:common:4.3.0-BETA+6d51da31") + implementation("com.dfsek.terra.common:common:5.0.0-BETA+7f11373f") implementation("org.yaml:snakeyaml:1.27") implementation("com.dfsek:Paralithic:0.3.2") implementation("commons-io:commons-io:2.8.0") @@ -23,6 +23,7 @@ dependencies { implementation("com.fifesoft:rstaui:3.1.1") implementation("com.fifesoft:rsyntaxtextarea:3.1.2") + implementation("com.fifesoft:autocomplete:3.1.1") implementation("com.formdev:flatlaf:1.0") diff --git a/src/main/java/com/dfsek/noise/ColorConfigTemplate.java b/src/main/java/com/dfsek/noise/ColorConfigTemplate.java index a1c8672..eb3c890 100644 --- a/src/main/java/com/dfsek/noise/ColorConfigTemplate.java +++ b/src/main/java/com/dfsek/noise/ColorConfigTemplate.java @@ -3,7 +3,7 @@ import com.dfsek.tectonic.annotations.Default; import com.dfsek.tectonic.annotations.Value; import com.dfsek.tectonic.config.ConfigTemplate; -import com.dfsek.terra.api.math.ProbabilityCollection; +import com.dfsek.terra.api.util.collections.ProbabilityCollection; public class ColorConfigTemplate implements ConfigTemplate { @Value("colors") diff --git a/src/main/java/com/dfsek/noise/NoiseTool.java b/src/main/java/com/dfsek/noise/NoiseTool.java index 73b7308..3f20b7d 100644 --- a/src/main/java/com/dfsek/noise/NoiseTool.java +++ b/src/main/java/com/dfsek/noise/NoiseTool.java @@ -9,6 +9,7 @@ import javax.swing.UIManager.LookAndFeelInfo; import com.dfsek.noise.swing.*; +import com.dfsek.terra.registry.config.NoiseRegistry; import com.formdev.flatlaf.FlatDarculaLaf; import com.formdev.flatlaf.FlatDarkLaf; import com.formdev.flatlaf.FlatLightLaf; @@ -20,6 +21,7 @@ import org.fife.rsta.ui.search.SearchEvent; import org.fife.rsta.ui.search.SearchListener; import org.fife.rsta.ui.search.FindToolBar; +import org.fife.ui.autocomplete.*; import org.fife.ui.rsyntaxtextarea.ErrorStrip; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; @@ -54,6 +56,19 @@ private NoiseTool() { textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_YAML); textArea.setCodeFoldingEnabled(true); textArea.setMarkOccurrences(true); + textArea.setTabsEmulated(true); + textArea.setTabSize(2); + + CompletionProvider provider = createCompletionProvider(new NoiseRegistry()); //new LanguageAwareCompletionProvider(new DefaultCompletionProvider()); + + AutoCompletion ac = new AutoCompletion(provider); + ac.install(textArea); + ac.setShowDescWindow(true); + ac.setAutoCompleteEnabled(true); + ac.setAutoActivationEnabled(true); + ac.setAutoCompleteSingleChoices(false); + ac.setAutoActivationDelay(200); + this.noise = new NoisePanel(textArea); @@ -67,12 +82,15 @@ private NoiseTool() { setJMenuBar(createMenuBar()); + + try { textArea.setText(IOUtils.toString(NoiseTool.class.getResourceAsStream("/config.yml"), StandardCharsets.UTF_8)); } catch (IOException e) { e.printStackTrace(); } + RTextScrollPane sp = new RTextScrollPane(textArea); csp.add(sp); @@ -97,8 +115,30 @@ private NoiseTool() { pack(); setLocationRelativeTo(null); + noise.update(); } + private CompletionProvider createCompletionProvider(NoiseRegistry registry) { + DefaultCompletionProvider noiseTypeProvider = new DefaultCompletionProvider(); + noiseTypeProvider.setAutoActivationRules(true, null); + + registry.keys().forEach(key -> noiseTypeProvider.addCompletion(new BasicCompletion(noiseTypeProvider, key, null, key + " noise type"))); + + DefaultCompletionProvider basicProvider = new DefaultCompletionProvider(); + basicProvider.setAutoActivationRules(true, null); + + registry.keys().forEach(key -> basicProvider.addCompletion(new BasicCompletion(basicProvider, key, null, key + " noise type"))); + basicProvider.addCompletion(new BasicCompletion(basicProvider, "type", null, "Sets the noise type for this sampler.")); + basicProvider.addCompletion(new BasicCompletion(basicProvider, "frequency", null, "Sets the frequency for this sampler.")); + + LanguageAwareCompletionProvider provider = new LanguageAwareCompletionProvider(basicProvider); + basicProvider.setAutoActivationRules(true, null); + + provider.setStringCompletionProvider(noiseTypeProvider); + + return provider; + + } private void addItem(Action a, ButtonGroup bg, JMenu menu) { JRadioButtonMenuItem item = new JRadioButtonMenuItem(a); diff --git a/src/main/java/com/dfsek/noise/swing/NoisePanel.java b/src/main/java/com/dfsek/noise/swing/NoisePanel.java index d6c4910..5c68cb0 100644 --- a/src/main/java/com/dfsek/noise/swing/NoisePanel.java +++ b/src/main/java/com/dfsek/noise/swing/NoisePanel.java @@ -4,8 +4,8 @@ import com.dfsek.noise.NoiseConfigTemplate; import com.dfsek.tectonic.exception.ConfigException; import com.dfsek.tectonic.loading.ConfigLoader; -import com.dfsek.terra.api.math.ProbabilityCollection; import com.dfsek.terra.api.math.noise.NoiseSampler; +import com.dfsek.terra.api.util.collections.ProbabilityCollection; import com.dfsek.terra.api.util.mutable.MutableBoolean; import com.dfsek.terra.api.util.seeded.NoiseSeeded; import com.dfsek.terra.config.GenericLoaders; diff --git a/src/main/java/com/dfsek/noise/swing/UpdateNoiseAction.java b/src/main/java/com/dfsek/noise/swing/UpdateNoiseAction.java index b0a0be5..48573bf 100644 --- a/src/main/java/com/dfsek/noise/swing/UpdateNoiseAction.java +++ b/src/main/java/com/dfsek/noise/swing/UpdateNoiseAction.java @@ -2,6 +2,7 @@ import javax.swing.*; import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; public class UpdateNoiseAction extends AbstractAction { private final NoisePanel noisePanel; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 0736c2e..cf90234 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,7 +1,12 @@ -dimensions: 2 -sampler-type: NOISE -type: OpenSimplex2 -frequency: 0.01 -fractal: - type: FBm - octaves: 4 \ No newline at end of file +type: "DOMAINWARP" +amplitude: 10 +warp: + type: "OPENSIMPLEX2S" + frequency: 0.01 +function: + type: "FBM" + octaves: 4 + function: + type: "CELLULAR" + return: "CellValue" + frequency: 0.01 \ No newline at end of file From 31c80783a1ad88cd01cb598466fa517cd1fe085e Mon Sep 17 00:00:00 2001 From: dfsek Date: Wed, 17 Mar 2021 08:51:46 -0700 Subject: [PATCH 03/12] add error handling and console view --- src/main/java/com/dfsek/noise/NoiseTool.java | 18 +- .../com/dfsek/noise/TextAreaOutputStream.java | 19 + .../{ => config}/ColorConfigTemplate.java | 2 +- .../{ => config}/NoiseConfigTemplate.java | 2 +- .../com/dfsek/noise/swing/NoisePanel.java | 28 +- .../java/com/dfsek/noise/swing/TextIcon.java | 327 ++++++++++++++++++ 6 files changed, 380 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/dfsek/noise/TextAreaOutputStream.java rename src/main/java/com/dfsek/noise/{ => config}/ColorConfigTemplate.java (94%) rename src/main/java/com/dfsek/noise/{ => config}/NoiseConfigTemplate.java (91%) create mode 100644 src/main/java/com/dfsek/noise/swing/TextIcon.java diff --git a/src/main/java/com/dfsek/noise/NoiseTool.java b/src/main/java/com/dfsek/noise/NoiseTool.java index 3f20b7d..43bcff1 100644 --- a/src/main/java/com/dfsek/noise/NoiseTool.java +++ b/src/main/java/com/dfsek/noise/NoiseTool.java @@ -3,6 +3,7 @@ import java.awt.*; import java.awt.event.*; import java.io.IOException; +import java.io.PrintStream; import java.nio.charset.StandardCharsets; import javax.swing.*; @@ -70,11 +71,24 @@ private NoiseTool() { ac.setAutoActivationDelay(200); - this.noise = new NoisePanel(textArea); + JTabbedPane pane = new JTabbedPane(); + pane.addTab("Render", noise); + + JTextArea sysout = new JTextArea(); + + System.setOut(new PrintStream(new TextAreaOutputStream(sysout))); + System.setErr(new PrintStream(new TextAreaOutputStream(sysout))); + + pane.addTab("Console", new JScrollPane(sysout)); + + pane.setSelectedIndex(0); + + pane.setBorder(BorderFactory.createEmptyBorder(0,0,20,10)); + add(contentPane); - add(noise); + add(pane); csp = new CollapsibleSectionPanel(); contentPane.add(csp); diff --git a/src/main/java/com/dfsek/noise/TextAreaOutputStream.java b/src/main/java/com/dfsek/noise/TextAreaOutputStream.java new file mode 100644 index 0000000..eadcb9f --- /dev/null +++ b/src/main/java/com/dfsek/noise/TextAreaOutputStream.java @@ -0,0 +1,19 @@ +package com.dfsek.noise; + +import javax.swing.*; +import java.io.IOException; +import java.io.OutputStream; + +public class TextAreaOutputStream extends OutputStream { + private JTextArea textArea; + + public TextAreaOutputStream(JTextArea textArea) { + this.textArea = textArea; + } + + @Override + public void write(int b) throws IOException { + textArea.append(String.valueOf((char)b)); + textArea.setCaretPosition(textArea.getDocument().getLength()); + } +} diff --git a/src/main/java/com/dfsek/noise/ColorConfigTemplate.java b/src/main/java/com/dfsek/noise/config/ColorConfigTemplate.java similarity index 94% rename from src/main/java/com/dfsek/noise/ColorConfigTemplate.java rename to src/main/java/com/dfsek/noise/config/ColorConfigTemplate.java index eb3c890..2197fab 100644 --- a/src/main/java/com/dfsek/noise/ColorConfigTemplate.java +++ b/src/main/java/com/dfsek/noise/config/ColorConfigTemplate.java @@ -1,4 +1,4 @@ -package com.dfsek.noise; +package com.dfsek.noise.config; import com.dfsek.tectonic.annotations.Default; import com.dfsek.tectonic.annotations.Value; diff --git a/src/main/java/com/dfsek/noise/NoiseConfigTemplate.java b/src/main/java/com/dfsek/noise/config/NoiseConfigTemplate.java similarity index 91% rename from src/main/java/com/dfsek/noise/NoiseConfigTemplate.java rename to src/main/java/com/dfsek/noise/config/NoiseConfigTemplate.java index 18926a9..de0fc1b 100644 --- a/src/main/java/com/dfsek/noise/NoiseConfigTemplate.java +++ b/src/main/java/com/dfsek/noise/config/NoiseConfigTemplate.java @@ -1,4 +1,4 @@ -package com.dfsek.noise; +package com.dfsek.noise.config; import com.dfsek.tectonic.annotations.Value; import com.dfsek.tectonic.config.ConfigTemplate; diff --git a/src/main/java/com/dfsek/noise/swing/NoisePanel.java b/src/main/java/com/dfsek/noise/swing/NoisePanel.java index 5c68cb0..ecfb007 100644 --- a/src/main/java/com/dfsek/noise/swing/NoisePanel.java +++ b/src/main/java/com/dfsek/noise/swing/NoisePanel.java @@ -1,7 +1,7 @@ package com.dfsek.noise.swing; -import com.dfsek.noise.ColorConfigTemplate; -import com.dfsek.noise.NoiseConfigTemplate; +import com.dfsek.noise.config.ColorConfigTemplate; +import com.dfsek.noise.config.NoiseConfigTemplate; import com.dfsek.tectonic.exception.ConfigException; import com.dfsek.tectonic.loading.ConfigLoader; import com.dfsek.terra.api.math.noise.NoiseSampler; @@ -40,8 +40,9 @@ public NoisePanel(RSyntaxTextArea textArea) { public void update() { try { image.setIcon(new ImageIcon(getImage(2403))); - } catch (ConfigException | FileNotFoundException e) { + } catch (Exception e) { e.printStackTrace(); + image.setIcon(new TextIcon(this, "An error occurred. ")); } } @@ -54,7 +55,8 @@ public MutableBoolean getDistribution() { } private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundException { - long s = System.nanoTime(); + + System.out.println("Rendering noise with seed " + seed); FolderLoader folderLoader = new FolderLoader(Paths.get("./")); @@ -66,10 +68,6 @@ private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundEx new GenericLoaders(null).register(loader); NoiseConfigTemplate template = new NoiseConfigTemplate(); - File file = new File("./config.yml"); - - System.out.println(file.getAbsolutePath()); - File colorFile = new File("./color.yml"); @@ -82,7 +80,7 @@ private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundEx ProbabilityCollection colorCollection = color.getColors(); loader.load(template, textArea.getText()); - System.out.println(template.getBuilder().getDimensions()); + System.out.println(template.getBuilder().getDimensions() + " Dimensions."); NoiseSampler noise = template.getBuilder().apply((long) seed); @@ -96,7 +94,9 @@ private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundEx double max = Double.MIN_VALUE; double min = Double.MAX_VALUE; - int[] buckets = new int[1024]; + int[] buckets = new int[sizeX]; + + long s = System.nanoTime(); for(int x = 0; x < noiseVals.length; x++) { for(int z = 0; z < noiseVals[x].length; z++) { @@ -108,6 +108,10 @@ private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundEx } } + long time = System.nanoTime() - s; + + double ms = time / 1000000d; + for(int x = 0; x < noiseVals.length; x++) { for(int z = 0; z < noiseVals[x].length; z++) { if(colors) image.setRGB(x, z, rgbVals[x][z] + (255 << 24)); @@ -116,9 +120,7 @@ private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundEx } } - long time = System.nanoTime() - s; - double ms = time / 1000000d; if(chunk.get()) { @@ -156,6 +158,8 @@ private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundEx } + System.out.println("Rendered " + sizeX*sizeY + " points in " + ms + "ms."); + return image; } diff --git a/src/main/java/com/dfsek/noise/swing/TextIcon.java b/src/main/java/com/dfsek/noise/swing/TextIcon.java new file mode 100644 index 0000000..5de280d --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/TextIcon.java @@ -0,0 +1,327 @@ +package com.dfsek.noise.swing; + +import java.awt.*; +import java.beans.*; +import java.util.*; +import javax.swing.*; + +import java.awt.font.*; + +/** + * The TextIcon will paint a String of text as an Icon. The Icon + * can be used by any Swing component that supports icons. + * + * TextIcon supports two different layout styles: + *
    + *
  • Horizontally - does normal rendering of the text by using the + * Graphics.drawString(...) method + *
  • Vertically - Each character is displayed on a separate line + *
+ * + * TextIcon was designed to be rendered on a specific JComponent as it + * requires FontMetrics information in order to calculate its size and to do + * the rendering. Therefore, it should only be added to component it was + * created for. + * + * By default the text will be rendered using the Font and foreground color + * of its associated component. However, this class does allow you to override + * these properties. Also starting in JDK6 the desktop renderering hints will + * be used to renderer the text. For versions not supporting the rendering + * hints antialiasing will be turned on. + */ +public class TextIcon implements Icon, PropertyChangeListener +{ + public enum Layout + { + HORIZONTAL, + VERTICAL; + } + + private JComponent component; + + private Layout layout; + + private String text; + + private Font font; + + private Color foreground; + + private int padding; + + // Used for the implementation of Icon interface + + private int iconWidth; + private int iconHeight; + + // Used for Layout.VERTICAL to save reparsing the text every time the + // icon is repainted + + private String[] strings; + private int[] stringWidths; + + /** + * Convenience constructor to create a TextIcon with a HORIZONTAL layout. + * + * @param component the component to which the icon will be added + * @param text the text to be rendered on the Icon + */ + public TextIcon(JComponent component, String text) + { + this(component, text, Layout.HORIZONTAL); + } + + /** + * Create a TextIcon specifying all the properties. + * + * @param component the component to which the icon will be added + * @param text the text to be rendered on the Icon + * @param layout specify the layout of the text. Must be one of + * the Layout enums: HORIZONTAL or VERTICAL + */ + public TextIcon(JComponent component, String text, Layout layout) + { + this.component = component; + this.layout = layout; + setText( text ); + + component.addPropertyChangeListener("font", this); + } + + /** + * Get the Layout enum + * + * @return the Layout enum + */ + public Layout getLayout() + { + return layout; + } + + /** + * Get the text String that will be rendered on the Icon + * + * @return the text of the Icon + */ + public String getText() + { + return text; + } + + /** + * Set the text to be rendered on the Icon + * + * @param text the text to be rendered on the Icon + */ + public void setText(String text) + { + this.text = text; + + calculateIconDimensions(); + } + + /** + * Get the Font used to render the text. This will default to the Font + * of the component unless the Font has been overridden by using the + * setFont() method. + * + * @return the Font used to render the text + */ + public Font getFont() + { + if (font == null) + return component.getFont(); + else + return font; + } + + /** + * Set the Font to be used for rendering the text + * + * @param font the Font to be used for rendering the text + */ + public void setFont(Font font) + { + this.font = font; + + calculateIconDimensions(); + } + + /** + * Get the foreground Color used to render the text. This will default to + * the foreground Color of the component unless the foreground Color has + * been overridden by using the setForeground() method. + * + * @return the Color used to render the text + */ + public Color getForeground() + { + if (foreground == null) + return component.getForeground(); + else + return foreground; + } + + /** + * Set the foreground Color to be used for rendering the text + * + * @param foreground the foreground Color to be used for rendering the text + */ + public void setForeground(Color foreground) + { + this.foreground = foreground; + component.repaint(); + } + + /** + * Get the padding used when rendering the text + * + * @return the padding specified in pixels + */ + public int getPadding() + { + return padding; + } + + /** + * By default the size of the Icon is based on the size of the rendered + * text. You can specify some padding to be added to the start and end + * of the text when it is rendered. + * + * @param padding the padding amount in pixels + */ + public void setPadding(int padding) + { + this.padding = padding; + + calculateIconDimensions(); + } + + /** + * Calculate the size of the Icon using the FontMetrics of the Font. + */ + private void calculateIconDimensions() + { + Font font = getFont(); + FontMetrics fm = component.getFontMetrics( font ); + + if (layout == Layout.HORIZONTAL) + { + iconWidth = fm.stringWidth( text ) + (padding * 2); + iconHeight = fm.getHeight(); + } + else if (layout == Layout.VERTICAL) + { + int maxWidth = 0; + strings = new String[text.length()]; + stringWidths = new int[text.length()]; + + // Find the widest character in the text string + + for (int i = 0; i < text.length(); i++) + { + strings[i] = text.substring(i, i + 1); + stringWidths[i] = fm.stringWidth( strings[i] ); + maxWidth = Math.max(maxWidth, stringWidths[i]); + } + + // Add a minimum of 2 extra pixels, plus the leading value, + // on each side of the character. + + iconWidth = maxWidth + ((fm.getLeading() + 2) * 2); + + // Decrease then normal gap betweens lines of text by taking into + // account the descent. + + iconHeight = (fm.getHeight() - fm.getDescent()) * text.length(); + iconHeight += padding * 2; + } + + component.revalidate(); + } +// +// Implement the Icon Interface +// + /** + * Gets the width of this icon. + * + * @return the width of the icon in pixels. + */ + @Override + public int getIconWidth() + { + return iconWidth; + } + + /** + * Gets the height of this icon. + * + * @return the height of the icon in pixels. + */ + @Override + public int getIconHeight() + { + return iconHeight; + } + + /** + * Paint the icons of this compound icon at the specified location + * + * @param c The component to which the icon is added + * @param g the graphics context + * @param x the X coordinate of the icon's top-left corner + * @param y the Y coordinate of the icon's top-left corner + */ + @Override + public void paintIcon(Component c, Graphics g, int x, int y) + { + Graphics2D g2 = (Graphics2D)g.create(); + + // The "desktophints" is supported in JDK6 + + Toolkit toolkit = Toolkit.getDefaultToolkit(); + Map map = (Map)(toolkit.getDesktopProperty("awt.font.desktophints")); + + if (map != null) + { + g2.addRenderingHints(map); + } + else + g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON ); + + g2.setFont( getFont() ); + g2.setColor( getForeground() ); + FontMetrics fm = g2.getFontMetrics(); + + if (layout == Layout.HORIZONTAL) + { + g2.translate(x, y + fm.getAscent()); + g2.drawString(text, padding, 0); + } + else if (layout == Layout.VERTICAL) + { + int offsetY = fm.getAscent() - fm.getDescent() + padding; + int incrementY = fm.getHeight() - fm.getDescent(); + + for (int i = 0; i < text.length(); i++) + { + int offsetX = Math.round((getIconWidth() - stringWidths[i]) / 2.0f); + g2.drawString(strings[i], x + offsetX, y + offsetY); + offsetY += incrementY; + } + } + + g2.dispose(); + } + // +// Implement the PropertyChangeListener interface +// + public void propertyChange(PropertyChangeEvent e) + { + // Handle font change when using the default font + + if (font == null) + calculateIconDimensions(); + } +} \ No newline at end of file From 770b9622b9314f24133d5914cd9607ff2ef824e2 Mon Sep 17 00:00:00 2001 From: dfsek Date: Wed, 17 Mar 2021 09:29:35 -0700 Subject: [PATCH 04/12] add distribution panel --- src/main/java/com/dfsek/noise/NoiseTool.java | 13 ++++- .../noise/swing/NoiseDistributionPanel.java | 57 +++++++++++++++++++ .../com/dfsek/noise/swing/NoisePanel.java | 43 +++----------- 3 files changed, 76 insertions(+), 37 deletions(-) create mode 100644 src/main/java/com/dfsek/noise/swing/NoiseDistributionPanel.java diff --git a/src/main/java/com/dfsek/noise/NoiseTool.java b/src/main/java/com/dfsek/noise/NoiseTool.java index 43bcff1..7419e62 100644 --- a/src/main/java/com/dfsek/noise/NoiseTool.java +++ b/src/main/java/com/dfsek/noise/NoiseTool.java @@ -70,13 +70,22 @@ private NoiseTool() { ac.setAutoCompleteSingleChoices(false); ac.setAutoActivationDelay(200); + JTextArea statisticsPanel = new JTextArea(); + statisticsPanel.setEditable(false); - this.noise = new NoisePanel(textArea); + NoiseDistributionPanel distributionPanel = new NoiseDistributionPanel(); + + this.noise = new NoisePanel(textArea, statisticsPanel, distributionPanel); JTabbedPane pane = new JTabbedPane(); pane.addTab("Render", noise); + pane.addTab("Statistics", statisticsPanel); + + pane.addTab("Distribution", distributionPanel); + JTextArea sysout = new JTextArea(); + sysout.setEditable(false); System.setOut(new PrintStream(new TextAreaOutputStream(sysout))); System.setErr(new PrintStream(new TextAreaOutputStream(sysout))); @@ -197,7 +206,6 @@ private JMenuBar createMenuBar() { menu = new JMenu("Noise"); menu.add(new UpdateNoiseAction(noise)); menu.add(new MutableBooleanAction(noise.getChunk(), "Toggle Chunk Borders")); - menu.add(new MutableBooleanAction(noise.getDistribution(), "Toggle Distribution Plot")); mb.add(menu); return mb; @@ -292,7 +300,6 @@ public static void main(String[] args) { SwingUtilities.invokeLater(() -> { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); -// UIManager.setLookAndFeel("org.pushingpixels.substance.api.skin.SubstanceGraphiteAquaLookAndFeel"); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/com/dfsek/noise/swing/NoiseDistributionPanel.java b/src/main/java/com/dfsek/noise/swing/NoiseDistributionPanel.java new file mode 100644 index 0000000..e789ee9 --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/NoiseDistributionPanel.java @@ -0,0 +1,57 @@ +package com.dfsek.noise.swing; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; + +public class NoiseDistributionPanel extends JPanel { + private final JLabel image; + + public NoiseDistributionPanel() { + this.image = new JLabel(); + add(image); + } + + public void update(int[] buckets) { + try { + image.setIcon(new ImageIcon(getImage(buckets))); + } catch (Exception e) { + e.printStackTrace(); + image.setIcon(new TextIcon(this, "An error occurred. ")); + } + } + + private BufferedImage getImage(int[] buckets) { + int sizeX = getWidth(); + int sizeY = getHeight(); + Color text = image.getForeground(); + BufferedImage image = new BufferedImage(sizeX, sizeY, BufferedImage.TYPE_INT_ARGB); + Graphics graphics = image.getGraphics(); + + int highestBucket = Integer.MIN_VALUE; + for (int i : buckets) highestBucket = Math.max(highestBucket, i); + + graphics.setColor(text); + graphics.drawString("" + highestBucket, 0, -1 + 20); + + for (int x = 0; x < sizeX; x++) { + for (int y = 0; y < ((double) buckets[x] / highestBucket) * ((double) sizeY); y++) { + image.setRGB(x, sizeY - y - 1, text.getRGB()); + } + } + + + return image; + } + + private static int buildRGBA(int in) { + return (255 << 24) + + (in << 16) + + (in << 8) + + in; + } + + public void error() { + image.setIcon(new TextIcon(image, "An error occurred.")); + } +} diff --git a/src/main/java/com/dfsek/noise/swing/NoisePanel.java b/src/main/java/com/dfsek/noise/swing/NoisePanel.java index ecfb007..85bdca8 100644 --- a/src/main/java/com/dfsek/noise/swing/NoisePanel.java +++ b/src/main/java/com/dfsek/noise/swing/NoisePanel.java @@ -28,11 +28,14 @@ public class NoisePanel extends JPanel { private final RSyntaxTextArea textArea; private final JLabel image; + private final JTextArea statisticsPanel; + private final NoiseDistributionPanel distributionPanel; private MutableBoolean chunk = new MutableBoolean(); - private MutableBoolean distribution = new MutableBoolean(); - public NoisePanel(RSyntaxTextArea textArea) { + public NoisePanel(RSyntaxTextArea textArea, JTextArea statisticsPanel, NoiseDistributionPanel distributionPanel) { this.textArea = textArea; + this.statisticsPanel = statisticsPanel; + this.distributionPanel = distributionPanel; this.image = new JLabel(); add(image); } @@ -43,6 +46,8 @@ public void update() { } catch (Exception e) { e.printStackTrace(); image.setIcon(new TextIcon(this, "An error occurred. ")); + statisticsPanel.setText("An error occurred."); + distributionPanel.error(); } } @@ -50,10 +55,6 @@ public MutableBoolean getChunk() { return chunk; } - public MutableBoolean getDistribution() { - return distribution; - } - private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundException { System.out.println("Rendering noise with seed " + seed); @@ -120,9 +121,6 @@ private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundEx } } - - - if(chunk.get()) { for(int x = 0; x < FastMath.floorDiv(image.getWidth(), 16); x++) { for(int y = 0; y < image.getHeight(); y++) image.setRGB(x*16, y, buildRGBA(0)); @@ -132,31 +130,8 @@ private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundEx } } - Graphics graphics = image.getGraphics(); - graphics.setColor(Color.WHITE); - graphics.fillRect(0, 0, 325, 90); - graphics.setColor(Color.BLACK); - graphics.setFont(new Font("Monospace", Font.BOLD, 20)); - graphics.drawString("min: " + min, 0, 20); - graphics.drawString("max: " + max, 0, 40); - graphics.drawString("seed: " + seed, 0, 60); - graphics.drawString("time: " + ms + "ms", 0, 80); - - if(distribution.get()) { - graphics.setColor(Color.WHITE); - graphics.fillRect(0, sizeY - (sizeY / 4) - 1, sizeX, (sizeY / 4) - 1); - int highestBucket = Integer.MIN_VALUE; - for(int i : buckets) highestBucket = Math.max(highestBucket, i); - graphics.setColor(Color.BLACK); - graphics.drawString("" + highestBucket, 0, sizeY - (sizeY / 4) - 1 + 20); - - for(int x = 0; x < sizeX; x++) { - for(int y = 0; y < ((double) buckets[x] / highestBucket) * ((double) sizeY / 4); y++) { - image.setRGB(x, sizeY - y - 1, buildRGBA(0)); - } - } - - } + statisticsPanel.setText("min: " + min + "\nmax: " + max + "\nseed: " + seed + "\ntime: " + ms + "ms"); + distributionPanel.update(buckets); System.out.println("Rendered " + sizeX*sizeY + " points in " + ms + "ms."); From 711b2bf6a840dbe70cbabc8ffbfe572da69a197c Mon Sep 17 00:00:00 2001 From: dfsek Date: Wed, 17 Mar 2021 09:31:01 -0700 Subject: [PATCH 05/12] re-render noise on laf change --- src/main/java/com/dfsek/noise/NoiseTool.java | 3 +++ src/main/java/com/dfsek/noise/swing/LookAndFeelAction.java | 1 + 2 files changed, 4 insertions(+) diff --git a/src/main/java/com/dfsek/noise/NoiseTool.java b/src/main/java/com/dfsek/noise/NoiseTool.java index 7419e62..6842b4a 100644 --- a/src/main/java/com/dfsek/noise/NoiseTool.java +++ b/src/main/java/com/dfsek/noise/NoiseTool.java @@ -218,6 +218,9 @@ public String getSelectedText() { return textArea.getSelectedText(); } + public NoisePanel getNoise() { + return noise; + } /** * Creates our Find and Replace dialogs. diff --git a/src/main/java/com/dfsek/noise/swing/LookAndFeelAction.java b/src/main/java/com/dfsek/noise/swing/LookAndFeelAction.java index 4b0e795..7af8f41 100644 --- a/src/main/java/com/dfsek/noise/swing/LookAndFeelAction.java +++ b/src/main/java/com/dfsek/noise/swing/LookAndFeelAction.java @@ -27,6 +27,7 @@ public void actionPerformed(ActionEvent e) { if (noiseTool.getFindDialog() != null) { noiseTool.getFindDialog().updateUI(); noiseTool.getReplaceDialog().updateUI(); + noiseTool.getNoise().update(); } noiseTool.pack(); } catch (RuntimeException re) { From 3230e4e0981a5b33c725cc71818ca44883c245ad Mon Sep 17 00:00:00 2001 From: dfsek Date: Wed, 17 Mar 2021 14:58:55 -0700 Subject: [PATCH 06/12] add open/save/save as actions --- src/main/java/com/dfsek/noise/NoiseTool.java | 20 +++++++-- .../swing/{ => actions}/GoToLineAction.java | 2 +- .../{ => actions}/LookAndFeelAction.java | 2 +- .../{ => actions}/MutableBooleanAction.java | 2 +- .../noise/swing/actions/OpenFileAction.java | 38 ++++++++++++++++ .../dfsek/noise/swing/actions/SaveAction.java | 43 +++++++++++++++++++ .../noise/swing/actions/SaveAsAction.java | 43 +++++++++++++++++++ .../{ => actions}/ShowFindDialogAction.java | 2 +- .../ShowReplaceDialogAction.java | 2 +- .../{ => actions}/UpdateNoiseAction.java | 4 +- 10 files changed, 149 insertions(+), 9 deletions(-) rename src/main/java/com/dfsek/noise/swing/{ => actions}/GoToLineAction.java (97%) rename src/main/java/com/dfsek/noise/swing/{ => actions}/LookAndFeelAction.java (96%) rename src/main/java/com/dfsek/noise/swing/{ => actions}/MutableBooleanAction.java (92%) create mode 100644 src/main/java/com/dfsek/noise/swing/actions/OpenFileAction.java create mode 100644 src/main/java/com/dfsek/noise/swing/actions/SaveAction.java create mode 100644 src/main/java/com/dfsek/noise/swing/actions/SaveAsAction.java rename src/main/java/com/dfsek/noise/swing/{ => actions}/ShowFindDialogAction.java (95%) rename src/main/java/com/dfsek/noise/swing/{ => actions}/ShowReplaceDialogAction.java (95%) rename src/main/java/com/dfsek/noise/swing/{ => actions}/UpdateNoiseAction.java (83%) diff --git a/src/main/java/com/dfsek/noise/NoiseTool.java b/src/main/java/com/dfsek/noise/NoiseTool.java index 6842b4a..a3d566e 100644 --- a/src/main/java/com/dfsek/noise/NoiseTool.java +++ b/src/main/java/com/dfsek/noise/NoiseTool.java @@ -10,6 +10,7 @@ import javax.swing.UIManager.LookAndFeelInfo; import com.dfsek.noise.swing.*; +import com.dfsek.noise.swing.actions.*; import com.dfsek.terra.registry.config.NoiseRegistry; import com.formdev.flatlaf.FlatDarculaLaf; import com.formdev.flatlaf.FlatDarkLaf; @@ -41,6 +42,7 @@ public final class NoiseTool extends JFrame implements SearchListener { private FindToolBar findToolBar; private ReplaceToolBar replaceToolBar; private final StatusBar statusBar; + private JFileChooser fileChooser = new JFileChooser(); private final NoisePanel noise; @@ -53,7 +55,7 @@ private NoiseTool() { GridLayout layout = new GridLayout(1, 2); setLayout(layout); - textArea = new RSyntaxTextArea(25, 80); + textArea = new RSyntaxTextArea(35, 45); textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_YAML); textArea.setCodeFoldingEnabled(true); textArea.setMarkOccurrences(true); @@ -163,6 +165,10 @@ private CompletionProvider createCompletionProvider(NoiseRegistry registry) { } + public JFileChooser getFileChooser() { + return fileChooser; + } + private void addItem(Action a, ButtonGroup bg, JMenu menu) { JRadioButtonMenuItem item = new JRadioButtonMenuItem(a); bg.add(item); @@ -171,9 +177,17 @@ private void addItem(Action a, ButtonGroup bg, JMenu menu) { private JMenuBar createMenuBar() { - JMenuBar mb = new JMenuBar(); - JMenu menu = new JMenu("Search"); + + JMenu menu = new JMenu("File"); + + menu.add(new OpenFileAction(this)); + menu.add(new SaveAction(this)); + menu.add(new SaveAsAction(this)); + + mb.add(menu); + + menu = new JMenu("Search"); menu.add(new JMenuItem(new ShowFindDialogAction(this))); menu.add(new JMenuItem(new ShowReplaceDialogAction(this))); menu.add(new JMenuItem(new GoToLineAction(this))); diff --git a/src/main/java/com/dfsek/noise/swing/GoToLineAction.java b/src/main/java/com/dfsek/noise/swing/actions/GoToLineAction.java similarity index 97% rename from src/main/java/com/dfsek/noise/swing/GoToLineAction.java rename to src/main/java/com/dfsek/noise/swing/actions/GoToLineAction.java index b760dcf..8e87371 100644 --- a/src/main/java/com/dfsek/noise/swing/GoToLineAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/GoToLineAction.java @@ -1,4 +1,4 @@ -package com.dfsek.noise.swing; +package com.dfsek.noise.swing.actions; import com.dfsek.noise.NoiseTool; import org.fife.rsta.ui.GoToDialog; diff --git a/src/main/java/com/dfsek/noise/swing/LookAndFeelAction.java b/src/main/java/com/dfsek/noise/swing/actions/LookAndFeelAction.java similarity index 96% rename from src/main/java/com/dfsek/noise/swing/LookAndFeelAction.java rename to src/main/java/com/dfsek/noise/swing/actions/LookAndFeelAction.java index 7af8f41..2e96cb5 100644 --- a/src/main/java/com/dfsek/noise/swing/LookAndFeelAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/LookAndFeelAction.java @@ -1,4 +1,4 @@ -package com.dfsek.noise.swing; +package com.dfsek.noise.swing.actions; import com.dfsek.noise.NoiseTool; diff --git a/src/main/java/com/dfsek/noise/swing/MutableBooleanAction.java b/src/main/java/com/dfsek/noise/swing/actions/MutableBooleanAction.java similarity index 92% rename from src/main/java/com/dfsek/noise/swing/MutableBooleanAction.java rename to src/main/java/com/dfsek/noise/swing/actions/MutableBooleanAction.java index 3778ef5..d821ef2 100644 --- a/src/main/java/com/dfsek/noise/swing/MutableBooleanAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/MutableBooleanAction.java @@ -1,4 +1,4 @@ -package com.dfsek.noise.swing; +package com.dfsek.noise.swing.actions; import com.dfsek.terra.api.util.mutable.MutableBoolean; diff --git a/src/main/java/com/dfsek/noise/swing/actions/OpenFileAction.java b/src/main/java/com/dfsek/noise/swing/actions/OpenFileAction.java new file mode 100644 index 0000000..23db1b5 --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/actions/OpenFileAction.java @@ -0,0 +1,38 @@ +package com.dfsek.noise.swing.actions; + +import com.dfsek.noise.NoiseTool; +import org.apache.commons.io.IOUtils; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.Charset; + +public class OpenFileAction extends AbstractAction { + private final NoiseTool noiseTool; + + public OpenFileAction(NoiseTool noiseTool) { + super("Open"); + this.noiseTool = noiseTool; + } + + + @Override + public void actionPerformed(ActionEvent actionEvent) { + int returnVal = noiseTool.getFileChooser().showOpenDialog(noiseTool); + + if(returnVal == JFileChooser.APPROVE_OPTION) { + File file = noiseTool.getFileChooser().getSelectedFile(); + System.out.println("Opening " + file.getAbsolutePath()); + try { + noiseTool.getTextArea().setText(IOUtils.toString(new FileInputStream(file), Charset.defaultCharset())); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + System.out.println("Operation cancelled by user."); + } + } +} diff --git a/src/main/java/com/dfsek/noise/swing/actions/SaveAction.java b/src/main/java/com/dfsek/noise/swing/actions/SaveAction.java new file mode 100644 index 0000000..97947cd --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/actions/SaveAction.java @@ -0,0 +1,43 @@ +package com.dfsek.noise.swing.actions; + +import com.dfsek.noise.NoiseTool; +import org.apache.commons.io.IOUtils; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +public class SaveAction extends AbstractAction { + private final NoiseTool noiseTool; + + public SaveAction(NoiseTool noiseTool) { + super("Save"); + this.noiseTool = noiseTool; + } + + + @Override + public void actionPerformed(ActionEvent actionEvent) { + + File file = noiseTool.getFileChooser().getSelectedFile(); + + if (file == null) { + System.out.println("No file is selected."); + return; + } + + System.out.println("Saving to " + file.getAbsolutePath()); + try (FileWriter writer = new FileWriter(file)) { + if (!file.exists()) { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + IOUtils.write(noiseTool.getTextArea().getText(), writer); + } catch (IOException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/main/java/com/dfsek/noise/swing/actions/SaveAsAction.java b/src/main/java/com/dfsek/noise/swing/actions/SaveAsAction.java new file mode 100644 index 0000000..0cc92ba --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/actions/SaveAsAction.java @@ -0,0 +1,43 @@ +package com.dfsek.noise.swing.actions; + +import com.dfsek.noise.NoiseTool; +import org.apache.commons.io.IOUtils; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.charset.Charset; + +public class SaveAsAction extends AbstractAction { + private final NoiseTool noiseTool; + + public SaveAsAction(NoiseTool noiseTool) { + super("Save As"); + this.noiseTool = noiseTool; + } + + + @Override + public void actionPerformed(ActionEvent actionEvent) { + int returnVal = noiseTool.getFileChooser().showOpenDialog(noiseTool); + + if(returnVal == JFileChooser.APPROVE_OPTION) { + File file = noiseTool.getFileChooser().getSelectedFile(); + System.out.println("Saving to " + file.getAbsolutePath()); + try(FileWriter writer = new FileWriter(file)) { + if(!file.exists()) { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + IOUtils.write(noiseTool.getTextArea().getText(), writer); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + System.out.println("Operation cancelled by user."); + } + } +} diff --git a/src/main/java/com/dfsek/noise/swing/ShowFindDialogAction.java b/src/main/java/com/dfsek/noise/swing/actions/ShowFindDialogAction.java similarity index 95% rename from src/main/java/com/dfsek/noise/swing/ShowFindDialogAction.java rename to src/main/java/com/dfsek/noise/swing/actions/ShowFindDialogAction.java index e135ed0..235d5eb 100644 --- a/src/main/java/com/dfsek/noise/swing/ShowFindDialogAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/ShowFindDialogAction.java @@ -1,4 +1,4 @@ -package com.dfsek.noise.swing; +package com.dfsek.noise.swing.actions; import com.dfsek.noise.NoiseTool; diff --git a/src/main/java/com/dfsek/noise/swing/ShowReplaceDialogAction.java b/src/main/java/com/dfsek/noise/swing/actions/ShowReplaceDialogAction.java similarity index 95% rename from src/main/java/com/dfsek/noise/swing/ShowReplaceDialogAction.java rename to src/main/java/com/dfsek/noise/swing/actions/ShowReplaceDialogAction.java index 8ef233f..f66576c 100644 --- a/src/main/java/com/dfsek/noise/swing/ShowReplaceDialogAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/ShowReplaceDialogAction.java @@ -1,4 +1,4 @@ -package com.dfsek.noise.swing; +package com.dfsek.noise.swing.actions; import com.dfsek.noise.NoiseTool; diff --git a/src/main/java/com/dfsek/noise/swing/UpdateNoiseAction.java b/src/main/java/com/dfsek/noise/swing/actions/UpdateNoiseAction.java similarity index 83% rename from src/main/java/com/dfsek/noise/swing/UpdateNoiseAction.java rename to src/main/java/com/dfsek/noise/swing/actions/UpdateNoiseAction.java index 48573bf..578b71b 100644 --- a/src/main/java/com/dfsek/noise/swing/UpdateNoiseAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/UpdateNoiseAction.java @@ -1,4 +1,6 @@ -package com.dfsek.noise.swing; +package com.dfsek.noise.swing.actions; + +import com.dfsek.noise.swing.NoisePanel; import javax.swing.*; import java.awt.event.ActionEvent; From b489dd6d74ad2d3ba9fab2d7d2a1d18ed871ad64 Mon Sep 17 00:00:00 2001 From: dfsek Date: Wed, 17 Mar 2021 17:21:37 -0700 Subject: [PATCH 07/12] add dragging --- src/main/java/com/dfsek/noise/NoiseTool.java | 12 +- .../com/dfsek/noise/swing/NoisePanel.java | 192 ++++++++++++------ .../dfsek/noise/swing/NoiseSettingsPanel.java | 43 ++++ .../swing/actions/SaveRenderAsAction.java | 50 +++++ .../com/dfsek/noise/thread/NoiseAction.java | 32 +++ .../com/dfsek/noise/utils/SwingUtils.java | 81 ++++++++ 6 files changed, 345 insertions(+), 65 deletions(-) create mode 100644 src/main/java/com/dfsek/noise/swing/NoiseSettingsPanel.java create mode 100644 src/main/java/com/dfsek/noise/swing/actions/SaveRenderAsAction.java create mode 100644 src/main/java/com/dfsek/noise/thread/NoiseAction.java create mode 100644 src/main/java/com/dfsek/noise/utils/SwingUtils.java diff --git a/src/main/java/com/dfsek/noise/NoiseTool.java b/src/main/java/com/dfsek/noise/NoiseTool.java index a3d566e..92dce45 100644 --- a/src/main/java/com/dfsek/noise/NoiseTool.java +++ b/src/main/java/com/dfsek/noise/NoiseTool.java @@ -42,7 +42,8 @@ public final class NoiseTool extends JFrame implements SearchListener { private FindToolBar findToolBar; private ReplaceToolBar replaceToolBar; private final StatusBar statusBar; - private JFileChooser fileChooser = new JFileChooser(); + private final JFileChooser fileChooser = new JFileChooser(); + private final JFileChooser imageChooser = new JFileChooser(); private final NoisePanel noise; @@ -77,11 +78,15 @@ private NoiseTool() { NoiseDistributionPanel distributionPanel = new NoiseDistributionPanel(); - this.noise = new NoisePanel(textArea, statisticsPanel, distributionPanel); + NoiseSettingsPanel settingsPanel = new NoiseSettingsPanel(); + + this.noise = new NoisePanel(textArea, statisticsPanel, distributionPanel, settingsPanel); JTabbedPane pane = new JTabbedPane(); pane.addTab("Render", noise); + pane.addTab("Settings", settingsPanel); + pane.addTab("Statistics", statisticsPanel); pane.addTab("Distribution", distributionPanel); @@ -337,4 +342,7 @@ public RSyntaxTextArea getTextArea() { } + public JFileChooser getImageChooser() { + return imageChooser; + } } diff --git a/src/main/java/com/dfsek/noise/swing/NoisePanel.java b/src/main/java/com/dfsek/noise/swing/NoisePanel.java index 85bdca8..829a540 100644 --- a/src/main/java/com/dfsek/noise/swing/NoisePanel.java +++ b/src/main/java/com/dfsek/noise/swing/NoisePanel.java @@ -2,151 +2,217 @@ import com.dfsek.noise.config.ColorConfigTemplate; import com.dfsek.noise.config.NoiseConfigTemplate; +import com.dfsek.tectonic.config.ConfigTemplate; import com.dfsek.tectonic.exception.ConfigException; import com.dfsek.tectonic.loading.ConfigLoader; +import com.dfsek.tectonic.loading.TypeLoader; +import com.dfsek.tectonic.loading.TypeRegistry; import com.dfsek.terra.api.math.noise.NoiseSampler; import com.dfsek.terra.api.util.collections.ProbabilityCollection; import com.dfsek.terra.api.util.mutable.MutableBoolean; import com.dfsek.terra.api.util.seeded.NoiseSeeded; import com.dfsek.terra.config.GenericLoaders; import com.dfsek.terra.config.fileloaders.FolderLoader; +import com.dfsek.terra.config.fileloaders.Loader; import com.dfsek.terra.config.loaders.ProbabilityCollectionLoader; import com.dfsek.terra.config.loaders.config.BufferedImageLoader; import com.dfsek.terra.config.loaders.config.sampler.NoiseSamplerBuilderLoader; import com.dfsek.terra.registry.config.NoiseRegistry; -import net.jafama.FastMath; -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; - -import javax.swing.*; -import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.nio.file.Paths; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import net.jafama.FastMath; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; public class NoisePanel extends JPanel { private final RSyntaxTextArea textArea; + private final JLabel image; + private final JTextArea statisticsPanel; + private final NoiseDistributionPanel distributionPanel; - private MutableBoolean chunk = new MutableBoolean(); - public NoisePanel(RSyntaxTextArea textArea, JTextArea statisticsPanel, NoiseDistributionPanel distributionPanel) { + private final MutableBoolean chunk = new MutableBoolean(); + + private final NoiseSettingsPanel settingsPanel; + + private BufferedImage render; + + public NoisePanel(RSyntaxTextArea textArea, JTextArea statisticsPanel, NoiseDistributionPanel distributionPanel, final NoiseSettingsPanel settingsPanel) { this.textArea = textArea; this.statisticsPanel = statisticsPanel; this.distributionPanel = distributionPanel; + this.settingsPanel = settingsPanel; this.image = new JLabel(); - add(image); + add(new JPanel() { + private volatile int screenX; + + private volatile int screenY; + + private volatile int myX; + + private volatile int myY; + { + add(image); + addMouseListener(new MouseListener() { + + @Override + public void mouseClicked(MouseEvent e) { } + + @Override + public void mousePressed(MouseEvent e) { + screenX = e.getXOnScreen(); + screenY = e.getYOnScreen(); + + myX = getX(); + myY = getY(); + } + + @Override + public void mouseReleased(MouseEvent e) { + int deltaX = e.getXOnScreen() - screenX; + int deltaY = e.getYOnScreen() - screenY; + + settingsPanel.setOriginX(settingsPanel.getOriginX() - (myX + deltaX)); + settingsPanel.setOriginZ(settingsPanel.getOriginZ() - (myY + deltaY)); + NoisePanel.this.update(); + } + + @Override + public void mouseEntered(MouseEvent e) { } + + @Override + public void mouseExited(MouseEvent e) { } + + }); + addMouseMotionListener(new MouseMotionListener() { + + @Override + public void mouseDragged(MouseEvent e) { + int deltaX = e.getXOnScreen() - screenX; + int deltaY = e.getYOnScreen() - screenY; + + setLocation(myX + deltaX, myY + deltaY); + } + + @Override + public void mouseMoved(MouseEvent e) { } + + }); + } + }); } public void update() { try { - image.setIcon(new ImageIcon(getImage(2403))); + this.render = getImage(this.settingsPanel.getSeed()); + this.image.setIcon(new ImageIcon(this.render)); } catch (Exception e) { e.printStackTrace(); - image.setIcon(new TextIcon(this, "An error occurred. ")); - statisticsPanel.setText("An error occurred."); - distributionPanel.error(); + this.image.setIcon(new TextIcon(this, "An error occurred. ")); + this.statisticsPanel.setText("An error occurred."); + this.distributionPanel.error(); } } + public BufferedImage getRender() { + return this.render; + } + public MutableBoolean getChunk() { - return chunk; + return this.chunk; } private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundException { - System.out.println("Rendering noise with seed " + seed); - FolderLoader folderLoader = new FolderLoader(Paths.get("./")); - ConfigLoader loader = new ConfigLoader(); loader.registerLoader(NoiseSeeded.class, new NoiseSamplerBuilderLoader(new NoiseRegistry())) .registerLoader(BufferedImage.class, new BufferedImageLoader(folderLoader)) .registerLoader(ProbabilityCollection.class, new ProbabilityCollectionLoader()); - - new GenericLoaders(null).register(loader); + (new GenericLoaders(null)).register(loader); NoiseConfigTemplate template = new NoiseConfigTemplate(); - File colorFile = new File("./color.yml"); - - boolean colors = false; ColorConfigTemplate color = new ColorConfigTemplate(); - if(colorFile.exists()) { + if (colorFile.exists()) { loader.load(color, new FileInputStream(colorFile)); colors = color.enable(); } ProbabilityCollection colorCollection = color.getColors(); - - loader.load(template, textArea.getText()); + loader.load(template, this.textArea.getText()); System.out.println(template.getBuilder().getDimensions() + " Dimensions."); - NoiseSampler noise = template.getBuilder().apply((long) seed); - - + NoiseSampler noise = template.getBuilder().apply(seed); int sizeX = getWidth(); int sizeY = getHeight(); - - BufferedImage image = new BufferedImage(sizeX, sizeY, BufferedImage.TYPE_INT_ARGB); - + double originX = this.settingsPanel.getOriginX(); + double originZ = this.settingsPanel.getOriginZ(); + BufferedImage image = new BufferedImage(sizeX, sizeY, 2); double[][] noiseVals = new double[sizeX][sizeY]; int[][] rgbVals = new int[sizeX][sizeY]; double max = Double.MIN_VALUE; double min = Double.MAX_VALUE; - int[] buckets = new int[sizeX]; - long s = System.nanoTime(); - - for(int x = 0; x < noiseVals.length; x++) { - for(int z = 0; z < noiseVals[x].length; z++) { - double n = noise.getNoise(x, z); + for (int x = 0; x < noiseVals.length; x++) { + for (int z = 0; z < (noiseVals[x]).length; z++) { + double n = noise.getNoise(x + originX, z + originZ); noiseVals[x][z] = n; max = Math.max(n, max); min = Math.min(n, min); - if(colors) rgbVals[x][z] = colorCollection.get(noise, x, z); + if (colors) + rgbVals[x][z] = colorCollection.get(noise, x + originX, z + originZ); } } - long time = System.nanoTime() - s; - - double ms = time / 1000000d; - - for(int x = 0; x < noiseVals.length; x++) { - for(int z = 0; z < noiseVals[x].length; z++) { - if(colors) image.setRGB(x, z, rgbVals[x][z] + (255 << 24)); - else image.setRGB(x, z, buildRGBA(normal(noiseVals[x][z], 255, min, max))); - buckets[normal(noiseVals[x][z], sizeX - 1, min, max)]++; + double ms = time / 1000000.0D; + int i; + for (i = 0; i < noiseVals.length; i++) { + for (int z = 0; z < (noiseVals[i]).length; z++) { + if (colors) { + image.setRGB(i, z, rgbVals[i][z] + -16777216); + } else { + image.setRGB(i, z, buildRGBA(normal(noiseVals[i][z], 255.0D, min, max))); + } + buckets[normal(noiseVals[i][z], (sizeX - 1), min, max)] = buckets[normal(noiseVals[i][z], (sizeX - 1), min, max)] + 1; } } - - if(chunk.get()) { - for(int x = 0; x < FastMath.floorDiv(image.getWidth(), 16); x++) { - for(int y = 0; y < image.getHeight(); y++) image.setRGB(x*16, y, buildRGBA(0)); + if (this.chunk.get()) { + for (i = 0; i < FastMath.floorDiv(image.getWidth(), 16); i++) { + for (int j = 0; j < image.getHeight(); ) { + image.setRGB(i * 16, j, buildRGBA(0)); + j++; + } } - for(int y = 0; y < FastMath.floorDiv(image.getHeight(), 16); y++) { - for(int x = 0; x < image.getWidth(); x++) image.setRGB(x, y*16, buildRGBA(0)); + for (int y = 0; y < FastMath.floorDiv(image.getHeight(), 16); y++) { + for (int j = 0; j < image.getWidth(); ) { + image.setRGB(j, y * 16, buildRGBA(0)); + j++; + } } } - - statisticsPanel.setText("min: " + min + "\nmax: " + max + "\nseed: " + seed + "\ntime: " + ms + "ms"); - distributionPanel.update(buckets); - - System.out.println("Rendered " + sizeX*sizeY + " points in " + ms + "ms."); - + this.statisticsPanel.setText("min: " + min + "\nmax: " + max + "\nseed: " + seed + "\ntime: " + ms + "ms"); + this.distributionPanel.update(buckets); + System.out.println("Rendered " + (sizeX * sizeY) + " points in " + ms + "ms."); return image; } private static int normal(double in, double out, double min, double max) { double range = max - min; - return (int) (((in - min) * out) / range); + return (int)((in - min) * out / range); } private static int buildRGBA(int in) { - return (255 << 24) - + (in << 16) - + (in << 8) - + in; + return -16777216 + (in << 16) + (in << 8) + in; } } diff --git a/src/main/java/com/dfsek/noise/swing/NoiseSettingsPanel.java b/src/main/java/com/dfsek/noise/swing/NoiseSettingsPanel.java new file mode 100644 index 0000000..72bc0ef --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/NoiseSettingsPanel.java @@ -0,0 +1,43 @@ +package com.dfsek.noise.swing; + +import com.dfsek.noise.utils.SwingUtils; + +import javax.swing.*; + +public class NoiseSettingsPanel extends JPanel { + private final JSpinner seedSpinner = new JSpinner(new SpinnerNumberModel(2403, Integer.MIN_VALUE, Integer.MAX_VALUE, 1)); + private final JSpinner xOrigin = new JSpinner(new SpinnerNumberModel(0, Integer.MIN_VALUE, Integer.MAX_VALUE, 1)); + private final JSpinner zOrigin = new JSpinner(new SpinnerNumberModel(0, Integer.MIN_VALUE, Integer.MAX_VALUE, 1)); + + public NoiseSettingsPanel() { + super(new SpringLayout()); + add(new JLabel("Seed: ")); + add(seedSpinner); + add(new JLabel("X Origin: ")); + add(xOrigin); + add(new JLabel("Z Origin: ")); + add(zOrigin); + + SwingUtils.makeCompactGrid(this, 3, 2, 10, 10, 10, 10); + } + + public int getSeed() { + return ((Number) seedSpinner.getValue()).intValue(); + } + + public double getOriginX() { + return ((Number) xOrigin.getValue()).doubleValue(); + } + + public double getOriginZ() { + return ((Number) zOrigin.getValue()).doubleValue(); + } + + public void setOriginX(double x) { + xOrigin.setValue(x); + } + + public void setOriginZ(double z) { + zOrigin.setValue(z); + } +} diff --git a/src/main/java/com/dfsek/noise/swing/actions/SaveRenderAsAction.java b/src/main/java/com/dfsek/noise/swing/actions/SaveRenderAsAction.java new file mode 100644 index 0000000..9ab3095 --- /dev/null +++ b/src/main/java/com/dfsek/noise/swing/actions/SaveRenderAsAction.java @@ -0,0 +1,50 @@ +package com.dfsek.noise.swing.actions; + +import com.dfsek.noise.NoiseTool; +import org.apache.commons.io.IOUtils; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.image.RenderedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; + +public class SaveRenderAsAction extends AbstractAction { + private final NoiseTool noiseTool; + + public SaveRenderAsAction(NoiseTool noiseTool) { + super("Save Render As"); + this.noiseTool = noiseTool; + } + + + @Override + public void actionPerformed(ActionEvent actionEvent) { + int returnVal = noiseTool.getImageChooser().showOpenDialog(noiseTool); + + if(returnVal == JFileChooser.APPROVE_OPTION) { + File file = noiseTool.getImageChooser().getSelectedFile(); + + if(!file.getName().endsWith(".png")) { + System.out.println("ERROR: image file is not of type PNG"); + return; + } + + System.out.println("Saving to " + file.getAbsolutePath()); + try(FileOutputStream writer = new FileOutputStream(file)) { + if(!file.exists()) { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + ImageIO.write(noiseTool.getNoise().getRender(), "png", writer); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + System.out.println("Operation cancelled by user."); + } + } +} diff --git a/src/main/java/com/dfsek/noise/thread/NoiseAction.java b/src/main/java/com/dfsek/noise/thread/NoiseAction.java new file mode 100644 index 0000000..f429cd7 --- /dev/null +++ b/src/main/java/com/dfsek/noise/thread/NoiseAction.java @@ -0,0 +1,32 @@ +package com.dfsek.noise.thread; + +import com.dfsek.terra.api.math.noise.NoiseSampler; + +import java.util.concurrent.RecursiveAction; + +public class NoiseAction extends RecursiveAction { + private final double originX; + + private final double originZ; + + private final double[] result; + + private final NoiseSampler sampler; + + private final double in; + + public NoiseAction(double originX, double originZ, double[] result, NoiseSampler sampler, double in) { + this.originX = originX; + this.originZ = originZ; + this.result = result; + this.sampler = sampler; + this.in = in; + } + + @Override + protected void compute() { + for(int i = 0; i <= result.length; i++) { + result[i] = sampler.getNoise(i + originX, in + originZ); + } + } +} diff --git a/src/main/java/com/dfsek/noise/utils/SwingUtils.java b/src/main/java/com/dfsek/noise/utils/SwingUtils.java new file mode 100644 index 0000000..0df582e --- /dev/null +++ b/src/main/java/com/dfsek/noise/utils/SwingUtils.java @@ -0,0 +1,81 @@ +package com.dfsek.noise.utils; + +import javax.swing.*; +import java.awt.*; + +public class SwingUtils { + /* Used by makeCompactGrid. */ + private static SpringLayout.Constraints getConstraintsForCell( + int row, int col, + Container parent, + int cols) { + SpringLayout layout = (SpringLayout) parent.getLayout(); + Component c = parent.getComponent(row * cols + col); + return layout.getConstraints(c); + } + + /** + * Aligns the first rows * cols + * components of parent in + * a grid. Each component in a column is as wide as the maximum + * preferred width of the components in that column; + * height is similarly determined for each row. + * The parent is made just big enough to fit them all. + * + * @param rows number of rows + * @param cols number of columns + * @param initialX x location to start the grid at + * @param initialY y location to start the grid at + * @param xPad x padding between cells + * @param yPad y padding between cells + */ + public static void makeCompactGrid(Container parent, + int rows, int cols, + int initialX, int initialY, + int xPad, int yPad) { + SpringLayout layout; + try { + layout = (SpringLayout)parent.getLayout(); + } catch (ClassCastException exc) { + System.err.println("The first argument to makeCompactGrid must use SpringLayout."); + return; + } + + //Align all cells in each column and make them the same width. + Spring x = Spring.constant(initialX); + for (int c = 0; c < cols; c++) { + Spring width = Spring.constant(0); + for (int r = 0; r < rows; r++) { + width = Spring.max(width, + getConstraintsForCell(r, c, parent, cols). + getWidth()); + } + for (int r = 0; r < rows; r++) { + SpringLayout.Constraints constraints = + getConstraintsForCell(r, c, parent, cols); + constraints.setX(x); + constraints.setWidth(width); + } + x = Spring.sum(x, Spring.sum(width, Spring.constant(xPad))); + } + + //Align all cells in each row and make them the same height. + Spring y = Spring.constant(initialY); + for (int r = 0; r < rows; r++) { + Spring height = Spring.constant(35); + + for (int c = 0; c < cols; c++) { + SpringLayout.Constraints constraints = + getConstraintsForCell(r, c, parent, cols); + constraints.setY(y); + //constraints.setHeight(height); + } + y = Spring.sum(y, Spring.sum(height, Spring.constant(yPad))); + } + + //Set the parent's size. + SpringLayout.Constraints pCons = layout.getConstraints(parent); + pCons.setConstraint(SpringLayout.SOUTH, y); + pCons.setConstraint(SpringLayout.EAST, x); + } +} From a2b0d80b9c225891b728497dc3ba97dacc927412 Mon Sep 17 00:00:00 2001 From: dfsek Date: Wed, 17 Mar 2021 17:22:53 -0700 Subject: [PATCH 08/12] reformat --- README.md | 35 ++- src/main/java/com/dfsek/noise/NoiseTool.java | 109 +++---- .../com/dfsek/noise/TextAreaOutputStream.java | 6 +- .../noise/swing/NoiseDistributionPanel.java | 22 +- .../com/dfsek/noise/swing/NoisePanel.java | 71 +++-- .../dfsek/noise/swing/NoiseSettingsPanel.java | 8 +- .../java/com/dfsek/noise/swing/TextIcon.java | 267 ++++++++---------- .../noise/swing/actions/GoToLineAction.java | 8 +- .../swing/actions/LookAndFeelAction.java | 6 +- .../noise/swing/actions/OpenFileAction.java | 2 +- .../dfsek/noise/swing/actions/SaveAction.java | 8 +- .../noise/swing/actions/SaveAsAction.java | 6 +- .../swing/actions/SaveRenderAsAction.java | 8 +- .../swing/actions/ShowFindDialogAction.java | 2 +- .../actions/ShowReplaceDialogAction.java | 2 +- .../swing/actions/UpdateNoiseAction.java | 3 +- .../com/dfsek/noise/utils/SwingUtils.java | 22 +- 17 files changed, 277 insertions(+), 308 deletions(-) diff --git a/README.md b/README.md index 57a1556..6bfdc18 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,48 @@ # Noise Tool + A generic name for a generic program. -This tool allows you to visualise noise. -Specifically, you can visualise noise as defined by Terra configuration files. The tool can show you information about -the noise, including: +This tool allows you to visualise noise. Specifically, you can visualise noise as defined by Terra configuration files. +The tool can show you information about the noise, including: + * 2D maps of the noise function itself * Minimum/Maximum values of the function * Plots of the distribution of noise values * Time taken to generate the noise # Using the tool -To use the tool, simply download the latest release, and put it in a folder. -Run the tool from the command line via `java -jar NoiseTool-VERSION.jar`. -When you run the tool, a GUI will open showing a view of the noise, min/max values, the seed, and the time taken to -generate it. The view is 1024x1024. + +To use the tool, simply download the latest release, and put it in a folder. Run the tool from the command line +via `java -jar NoiseTool-VERSION.jar`. When you run the tool, a GUI will open showing a view of the noise, min/max +values, the seed, and the time taken to generate it. The view is 1024x1024. When you run the tool it will create a configuration file, `config.yml` in the same directory. This config is identical to Terra noise configs. [here](https://github.com/PolyhedralDev/Terra/wiki/Noise-Options). ## Reloading the config + This tool allows you to reload the config live. To do so, simply press `r`. ## Randomizing seed + To randomize the seed, press `s`. ## Plotting distribution + To plot the distribution of the noise function at the bottom of the window, press `d`. ## Colorizing -To colorize noise (useful for visualizing the actual distribution of things in probability collections), create -a file in the same directory as the noise tool called `color.yml`. This file has 2 keys: - * `enable` - Whether to pull colors from a probability collection rather than from noise values. - * `colors` - A probability collection of colors. They are represented in a raw integer format. You may use - hexadecimal color notation by prefixing hex values with `0x`. - - Example `color.yml`: + +To colorize noise (useful for visualizing the actual distribution of things in probability collections), create a file +in the same directory as the noise tool called `color.yml`. This file has 2 keys: + +* `enable` - Whether to pull colors from a probability collection rather than from noise values. +* `colors` - A probability collection of colors. They are represented in a raw integer format. You may use hexadecimal + color notation by prefixing hex values with `0x`. + +Example `color.yml`: + ```yaml colors: 0xff0000: 1 diff --git a/src/main/java/com/dfsek/noise/NoiseTool.java b/src/main/java/com/dfsek/noise/NoiseTool.java index 92dce45..50421f0 100644 --- a/src/main/java/com/dfsek/noise/NoiseTool.java +++ b/src/main/java/com/dfsek/noise/NoiseTool.java @@ -1,16 +1,18 @@ package com.dfsek.noise; -import java.awt.*; -import java.awt.event.*; -import java.io.IOException; -import java.io.PrintStream; -import java.nio.charset.StandardCharsets; - -import javax.swing.*; -import javax.swing.UIManager.LookAndFeelInfo; - -import com.dfsek.noise.swing.*; -import com.dfsek.noise.swing.actions.*; +import com.dfsek.noise.swing.NoiseDistributionPanel; +import com.dfsek.noise.swing.NoisePanel; +import com.dfsek.noise.swing.NoiseSettingsPanel; +import com.dfsek.noise.swing.StatusBar; +import com.dfsek.noise.swing.actions.GoToLineAction; +import com.dfsek.noise.swing.actions.LookAndFeelAction; +import com.dfsek.noise.swing.actions.MutableBooleanAction; +import com.dfsek.noise.swing.actions.OpenFileAction; +import com.dfsek.noise.swing.actions.SaveAction; +import com.dfsek.noise.swing.actions.SaveAsAction; +import com.dfsek.noise.swing.actions.ShowFindDialogAction; +import com.dfsek.noise.swing.actions.ShowReplaceDialogAction; +import com.dfsek.noise.swing.actions.UpdateNoiseAction; import com.dfsek.terra.registry.config.NoiseRegistry; import com.formdev.flatlaf.FlatDarculaLaf; import com.formdev.flatlaf.FlatDarkLaf; @@ -18,12 +20,16 @@ import org.apache.commons.io.IOUtils; import org.fife.rsta.ui.CollapsibleSectionPanel; import org.fife.rsta.ui.search.FindDialog; +import org.fife.rsta.ui.search.FindToolBar; import org.fife.rsta.ui.search.ReplaceDialog; import org.fife.rsta.ui.search.ReplaceToolBar; import org.fife.rsta.ui.search.SearchEvent; import org.fife.rsta.ui.search.SearchListener; -import org.fife.rsta.ui.search.FindToolBar; -import org.fife.ui.autocomplete.*; +import org.fife.ui.autocomplete.AutoCompletion; +import org.fife.ui.autocomplete.BasicCompletion; +import org.fife.ui.autocomplete.CompletionProvider; +import org.fife.ui.autocomplete.DefaultCompletionProvider; +import org.fife.ui.autocomplete.LanguageAwareCompletionProvider; import org.fife.ui.rsyntaxtextarea.ErrorStrip; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; @@ -32,20 +38,28 @@ import org.fife.ui.rtextarea.SearchEngine; import org.fife.ui.rtextarea.SearchResult; +import javax.swing.*; +import javax.swing.UIManager.LookAndFeelInfo; +import java.awt.*; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; + public final class NoiseTool extends JFrame implements SearchListener { private final CollapsibleSectionPanel csp; private final RSyntaxTextArea textArea; - private FindDialog findDialog; - private ReplaceDialog replaceDialog; - private FindToolBar findToolBar; - private ReplaceToolBar replaceToolBar; private final StatusBar statusBar; private final JFileChooser fileChooser = new JFileChooser(); private final JFileChooser imageChooser = new JFileChooser(); - private final NoisePanel noise; + private FindDialog findDialog; + private ReplaceDialog replaceDialog; + private FindToolBar findToolBar; + private ReplaceToolBar replaceToolBar; private NoiseTool() { @@ -101,7 +115,7 @@ private NoiseTool() { pane.setSelectedIndex(0); - pane.setBorder(BorderFactory.createEmptyBorder(0,0,20,10)); + pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 10)); add(contentPane); add(pane); @@ -112,11 +126,9 @@ private NoiseTool() { setJMenuBar(createMenuBar()); - - try { textArea.setText(IOUtils.toString(NoiseTool.class.getResourceAsStream("/config.yml"), StandardCharsets.UTF_8)); - } catch (IOException e) { + } catch(IOException e) { e.printStackTrace(); } @@ -136,7 +148,7 @@ private NoiseTool() { FlatDarculaLaf.install(); SwingUtilities.updateComponentTreeUI(NoiseTool.this); - if (findDialog!=null) { + if(findDialog != null) { findDialog.updateUI(); replaceDialog.updateUI(); } @@ -148,6 +160,17 @@ private NoiseTool() { noise.update(); } + public static void main(String[] args) { + SwingUtilities.invokeLater(() -> { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch(Exception e) { + e.printStackTrace(); + } + new NoiseTool().setVisible(true); + }); + } + private CompletionProvider createCompletionProvider(NoiseRegistry registry) { DefaultCompletionProvider noiseTypeProvider = new DefaultCompletionProvider(); noiseTypeProvider.setAutoActivationRules(true, null); @@ -180,7 +203,6 @@ private void addItem(Action a, ButtonGroup bg, JMenu menu) { menu.add(item); } - private JMenuBar createMenuBar() { JMenuBar mb = new JMenuBar(); @@ -200,11 +222,11 @@ private JMenuBar createMenuBar() { int ctrl = getToolkit().getMenuShortcutKeyMask(); int shift = InputEvent.SHIFT_MASK; - KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F, ctrl|shift); + KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F, ctrl | shift); Action a = csp.addBottomComponent(ks, findToolBar); a.putValue(Action.NAME, "Show Find Search Bar"); menu.add(new JMenuItem(a)); - ks = KeyStroke.getKeyStroke(KeyEvent.VK_H, ctrl|shift); + ks = KeyStroke.getKeyStroke(KeyEvent.VK_H, ctrl | shift); a = csp.addBottomComponent(ks, replaceToolBar); a.putValue(Action.NAME, "Show Replace Search Bar"); menu.add(new JMenuItem(a)); @@ -217,7 +239,7 @@ private JMenuBar createMenuBar() { FlatDarculaLaf.installLafInfo(); FlatDarkLaf.installLafInfo(); LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels(); - for (LookAndFeelInfo info : infos) { + for(LookAndFeelInfo info : infos) { addItem(new LookAndFeelAction(this, info), bg, menu); } mb.add(menu); @@ -231,7 +253,6 @@ private JMenuBar createMenuBar() { } - @Override public String getSelectedText() { return textArea.getSelectedText(); @@ -262,7 +283,6 @@ private void initSearchDialogs() { } - /** * Listens for events from our search dialogs and actually does the dirty * work. @@ -274,20 +294,20 @@ public void searchEvent(SearchEvent e) { SearchContext context = e.getSearchContext(); SearchResult result; - switch (type) { + switch(type) { default: // Prevent FindBugs warning later case MARK_ALL: result = SearchEngine.markAll(textArea, context); break; case FIND: result = SearchEngine.find(textArea, context); - if (!result.wasFound() || result.isWrapped()) { + if(!result.wasFound() || result.isWrapped()) { UIManager.getLookAndFeel().provideErrorFeedback(textArea); } break; case REPLACE: result = SearchEngine.replace(textArea, context); - if (!result.wasFound() || result.isWrapped()) { + if(!result.wasFound() || result.isWrapped()) { UIManager.getLookAndFeel().provideErrorFeedback(textArea); } break; @@ -299,36 +319,21 @@ public void searchEvent(SearchEvent e) { } String text; - if (result.wasFound()) { + if(result.wasFound()) { text = "Text found; occurrences marked: " + result.getMarkedCount(); - } - else if (type==SearchEvent.Type.MARK_ALL) { - if (result.getMarkedCount()>0) { + } else if(type == SearchEvent.Type.MARK_ALL) { + if(result.getMarkedCount() > 0) { text = "Occurrences marked: " + result.getMarkedCount(); - } - else { + } else { text = ""; } - } - else { + } else { text = "Text not found"; } statusBar.setLabel(text); } - - public static void main(String[] args) { - SwingUtilities.invokeLater(() -> { - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception e) { - e.printStackTrace(); - } - new NoiseTool().setVisible(true); - }); - } - public ReplaceDialog getReplaceDialog() { return replaceDialog; } diff --git a/src/main/java/com/dfsek/noise/TextAreaOutputStream.java b/src/main/java/com/dfsek/noise/TextAreaOutputStream.java index eadcb9f..9ef178a 100644 --- a/src/main/java/com/dfsek/noise/TextAreaOutputStream.java +++ b/src/main/java/com/dfsek/noise/TextAreaOutputStream.java @@ -5,15 +5,15 @@ import java.io.OutputStream; public class TextAreaOutputStream extends OutputStream { - private JTextArea textArea; + private final JTextArea textArea; public TextAreaOutputStream(JTextArea textArea) { this.textArea = textArea; } @Override - public void write(int b) throws IOException { - textArea.append(String.valueOf((char)b)); + public void write(int b) { + textArea.append(String.valueOf((char) b)); textArea.setCaretPosition(textArea.getDocument().getLength()); } } diff --git a/src/main/java/com/dfsek/noise/swing/NoiseDistributionPanel.java b/src/main/java/com/dfsek/noise/swing/NoiseDistributionPanel.java index e789ee9..7c06bf8 100644 --- a/src/main/java/com/dfsek/noise/swing/NoiseDistributionPanel.java +++ b/src/main/java/com/dfsek/noise/swing/NoiseDistributionPanel.java @@ -12,10 +12,17 @@ public NoiseDistributionPanel() { add(image); } + private static int buildRGBA(int in) { + return (255 << 24) + + (in << 16) + + (in << 8) + + in; + } + public void update(int[] buckets) { try { image.setIcon(new ImageIcon(getImage(buckets))); - } catch (Exception e) { + } catch(Exception e) { e.printStackTrace(); image.setIcon(new TextIcon(this, "An error occurred. ")); } @@ -29,13 +36,13 @@ private BufferedImage getImage(int[] buckets) { Graphics graphics = image.getGraphics(); int highestBucket = Integer.MIN_VALUE; - for (int i : buckets) highestBucket = Math.max(highestBucket, i); + for(int i : buckets) highestBucket = Math.max(highestBucket, i); graphics.setColor(text); graphics.drawString("" + highestBucket, 0, -1 + 20); - for (int x = 0; x < sizeX; x++) { - for (int y = 0; y < ((double) buckets[x] / highestBucket) * ((double) sizeY); y++) { + for(int x = 0; x < sizeX; x++) { + for(int y = 0; y < ((double) buckets[x] / highestBucket) * ((double) sizeY); y++) { image.setRGB(x, sizeY - y - 1, text.getRGB()); } } @@ -44,13 +51,6 @@ private BufferedImage getImage(int[] buckets) { return image; } - private static int buildRGBA(int in) { - return (255 << 24) - + (in << 16) - + (in << 8) - + in; - } - public void error() { image.setIcon(new TextIcon(image, "An error occurred.")); } diff --git a/src/main/java/com/dfsek/noise/swing/NoisePanel.java b/src/main/java/com/dfsek/noise/swing/NoisePanel.java index 829a540..461406b 100644 --- a/src/main/java/com/dfsek/noise/swing/NoisePanel.java +++ b/src/main/java/com/dfsek/noise/swing/NoisePanel.java @@ -2,22 +2,22 @@ import com.dfsek.noise.config.ColorConfigTemplate; import com.dfsek.noise.config.NoiseConfigTemplate; -import com.dfsek.tectonic.config.ConfigTemplate; import com.dfsek.tectonic.exception.ConfigException; import com.dfsek.tectonic.loading.ConfigLoader; -import com.dfsek.tectonic.loading.TypeLoader; -import com.dfsek.tectonic.loading.TypeRegistry; import com.dfsek.terra.api.math.noise.NoiseSampler; import com.dfsek.terra.api.util.collections.ProbabilityCollection; import com.dfsek.terra.api.util.mutable.MutableBoolean; import com.dfsek.terra.api.util.seeded.NoiseSeeded; import com.dfsek.terra.config.GenericLoaders; import com.dfsek.terra.config.fileloaders.FolderLoader; -import com.dfsek.terra.config.fileloaders.Loader; import com.dfsek.terra.config.loaders.ProbabilityCollectionLoader; import com.dfsek.terra.config.loaders.config.BufferedImageLoader; import com.dfsek.terra.config.loaders.config.sampler.NoiseSamplerBuilderLoader; import com.dfsek.terra.registry.config.NoiseRegistry; +import net.jafama.FastMath; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; + +import javax.swing.*; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; @@ -26,12 +26,6 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.nio.file.Paths; -import javax.swing.ImageIcon; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JTextArea; -import net.jafama.FastMath; -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; public class NoisePanel extends JPanel { private final RSyntaxTextArea textArea; @@ -62,12 +56,14 @@ public NoisePanel(RSyntaxTextArea textArea, JTextArea statisticsPanel, NoiseDist private volatile int myX; private volatile int myY; + { add(image); addMouseListener(new MouseListener() { @Override - public void mouseClicked(MouseEvent e) { } + public void mouseClicked(MouseEvent e) { + } @Override public void mousePressed(MouseEvent e) { @@ -89,10 +85,12 @@ public void mouseReleased(MouseEvent e) { } @Override - public void mouseEntered(MouseEvent e) { } + public void mouseEntered(MouseEvent e) { + } @Override - public void mouseExited(MouseEvent e) { } + public void mouseExited(MouseEvent e) { + } }); addMouseMotionListener(new MouseMotionListener() { @@ -106,18 +104,28 @@ public void mouseDragged(MouseEvent e) { } @Override - public void mouseMoved(MouseEvent e) { } + public void mouseMoved(MouseEvent e) { + } }); } }); } + private static int normal(double in, double out, double min, double max) { + double range = max - min; + return (int) ((in - min) * out / range); + } + + private static int buildRGBA(int in) { + return -16777216 + (in << 16) + (in << 8) + in; + } + public void update() { try { this.render = getImage(this.settingsPanel.getSeed()); this.image.setIcon(new ImageIcon(this.render)); - } catch (Exception e) { + } catch(Exception e) { e.printStackTrace(); this.image.setIcon(new TextIcon(this, "An error occurred. ")); this.statisticsPanel.setText("An error occurred."); @@ -145,7 +153,7 @@ private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundEx File colorFile = new File("./color.yml"); boolean colors = false; ColorConfigTemplate color = new ColorConfigTemplate(); - if (colorFile.exists()) { + if(colorFile.exists()) { loader.load(color, new FileInputStream(colorFile)); colors = color.enable(); } @@ -164,22 +172,22 @@ private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundEx double min = Double.MAX_VALUE; int[] buckets = new int[sizeX]; long s = System.nanoTime(); - for (int x = 0; x < noiseVals.length; x++) { - for (int z = 0; z < (noiseVals[x]).length; z++) { + for(int x = 0; x < noiseVals.length; x++) { + for(int z = 0; z < (noiseVals[x]).length; z++) { double n = noise.getNoise(x + originX, z + originZ); noiseVals[x][z] = n; max = Math.max(n, max); min = Math.min(n, min); - if (colors) + if(colors) rgbVals[x][z] = colorCollection.get(noise, x + originX, z + originZ); } } long time = System.nanoTime() - s; double ms = time / 1000000.0D; int i; - for (i = 0; i < noiseVals.length; i++) { - for (int z = 0; z < (noiseVals[i]).length; z++) { - if (colors) { + for(i = 0; i < noiseVals.length; i++) { + for(int z = 0; z < (noiseVals[i]).length; z++) { + if(colors) { image.setRGB(i, z, rgbVals[i][z] + -16777216); } else { image.setRGB(i, z, buildRGBA(normal(noiseVals[i][z], 255.0D, min, max))); @@ -187,15 +195,15 @@ private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundEx buckets[normal(noiseVals[i][z], (sizeX - 1), min, max)] = buckets[normal(noiseVals[i][z], (sizeX - 1), min, max)] + 1; } } - if (this.chunk.get()) { - for (i = 0; i < FastMath.floorDiv(image.getWidth(), 16); i++) { - for (int j = 0; j < image.getHeight(); ) { + if(this.chunk.get()) { + for(i = 0; i < FastMath.floorDiv(image.getWidth(), 16); i++) { + for(int j = 0; j < image.getHeight(); ) { image.setRGB(i * 16, j, buildRGBA(0)); j++; } } - for (int y = 0; y < FastMath.floorDiv(image.getHeight(), 16); y++) { - for (int j = 0; j < image.getWidth(); ) { + for(int y = 0; y < FastMath.floorDiv(image.getHeight(), 16); y++) { + for(int j = 0; j < image.getWidth(); ) { image.setRGB(j, y * 16, buildRGBA(0)); j++; } @@ -206,13 +214,4 @@ private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundEx System.out.println("Rendered " + (sizeX * sizeY) + " points in " + ms + "ms."); return image; } - - private static int normal(double in, double out, double min, double max) { - double range = max - min; - return (int)((in - min) * out / range); - } - - private static int buildRGBA(int in) { - return -16777216 + (in << 16) + (in << 8) + in; - } } diff --git a/src/main/java/com/dfsek/noise/swing/NoiseSettingsPanel.java b/src/main/java/com/dfsek/noise/swing/NoiseSettingsPanel.java index 72bc0ef..a2aaecb 100644 --- a/src/main/java/com/dfsek/noise/swing/NoiseSettingsPanel.java +++ b/src/main/java/com/dfsek/noise/swing/NoiseSettingsPanel.java @@ -29,14 +29,14 @@ public double getOriginX() { return ((Number) xOrigin.getValue()).doubleValue(); } - public double getOriginZ() { - return ((Number) zOrigin.getValue()).doubleValue(); - } - public void setOriginX(double x) { xOrigin.setValue(x); } + public double getOriginZ() { + return ((Number) zOrigin.getValue()).doubleValue(); + } + public void setOriginZ(double z) { zOrigin.setValue(z); } diff --git a/src/main/java/com/dfsek/noise/swing/TextIcon.java b/src/main/java/com/dfsek/noise/swing/TextIcon.java index 5de280d..4c52978 100644 --- a/src/main/java/com/dfsek/noise/swing/TextIcon.java +++ b/src/main/java/com/dfsek/noise/swing/TextIcon.java @@ -1,227 +1,195 @@ package com.dfsek.noise.swing; -import java.awt.*; -import java.beans.*; -import java.util.*; import javax.swing.*; - -import java.awt.font.*; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Map; /** - * The TextIcon will paint a String of text as an Icon. The Icon - * can be used by any Swing component that supports icons. - * - * TextIcon supports two different layout styles: - *
    - *
  • Horizontally - does normal rendering of the text by using the - * Graphics.drawString(...) method - *
  • Vertically - Each character is displayed on a separate line - *
- * - * TextIcon was designed to be rendered on a specific JComponent as it - * requires FontMetrics information in order to calculate its size and to do - * the rendering. Therefore, it should only be added to component it was - * created for. - * - * By default the text will be rendered using the Font and foreground color - * of its associated component. However, this class does allow you to override - * these properties. Also starting in JDK6 the desktop renderering hints will - * be used to renderer the text. For versions not supporting the rendering - * hints antialiasing will be turned on. + * The TextIcon will paint a String of text as an Icon. The Icon + * can be used by any Swing component that supports icons. + *

+ * extIcon supports two different layout styles: + *

    + *
  • Horizontally - does normal rendering of the text by using the + * Graphics.drawString(...) method + *
  • Vertically - Each character is displayed on a separate line + *
+ *

+ * TextIcon was designed to be rendered on a specific JComponent as it + * requires FontMetrics information in order to calculate its size and to do + * the rendering. Therefore, it should only be added to component it was + * created for. + *

+ * By default the text will be rendered using the Font and foreground color + * of its associated component. However, this class does allow you to override + * these properties. Also starting in JDK6 the desktop renderering hints will + * be used to renderer the text. For versions not supporting the rendering + * hints antialiasing will be turned on. */ -public class TextIcon implements Icon, PropertyChangeListener -{ - public enum Layout - { - HORIZONTAL, - VERTICAL; - } - +public class TextIcon implements Icon, PropertyChangeListener { private JComponent component; - private Layout layout; - private String text; - private Font font; - private Color foreground; - private int padding; + private int iconWidth; // Used for the implementation of Icon interface - - private int iconWidth; private int iconHeight; + private String[] strings; // Used for Layout.VERTICAL to save reparsing the text every time the // icon is repainted - - private String[] strings; private int[] stringWidths; - /** - * Convenience constructor to create a TextIcon with a HORIZONTAL layout. + * Convenience constructor to create a TextIcon with a HORIZONTAL layout. * - * @param component the component to which the icon will be added - * @param text the text to be rendered on the Icon + * @param component the component to which the icon will be added + * @param text the text to be rendered on the Icon */ - public TextIcon(JComponent component, String text) - { + public TextIcon(JComponent component, String text) { this(component, text, Layout.HORIZONTAL); } /** - * Create a TextIcon specifying all the properties. + * Create a TextIcon specifying all the properties. * - * @param component the component to which the icon will be added - * @param text the text to be rendered on the Icon - * @param layout specify the layout of the text. Must be one of - * the Layout enums: HORIZONTAL or VERTICAL + * @param component the component to which the icon will be added + * @param text the text to be rendered on the Icon + * @param layout specify the layout of the text. Must be one of + * the Layout enums: HORIZONTAL or VERTICAL */ - public TextIcon(JComponent component, String text, Layout layout) - { + public TextIcon(JComponent component, String text, Layout layout) { this.component = component; this.layout = layout; - setText( text ); + setText(text); component.addPropertyChangeListener("font", this); } /** - * Get the Layout enum + * Get the Layout enum * - * @return the Layout enum + * @return the Layout enum */ - public Layout getLayout() - { + public Layout getLayout() { return layout; } /** - * Get the text String that will be rendered on the Icon + * Get the text String that will be rendered on the Icon * - * @return the text of the Icon + * @return the text of the Icon */ - public String getText() - { + public String getText() { return text; } /** - * Set the text to be rendered on the Icon + * Set the text to be rendered on the Icon * - * @param text the text to be rendered on the Icon + * @param text the text to be rendered on the Icon */ - public void setText(String text) - { + public void setText(String text) { this.text = text; calculateIconDimensions(); } /** - * Get the Font used to render the text. This will default to the Font - * of the component unless the Font has been overridden by using the - * setFont() method. + * Get the Font used to render the text. This will default to the Font + * of the component unless the Font has been overridden by using the + * setFont() method. * - * @return the Font used to render the text + * @return the Font used to render the text */ - public Font getFont() - { - if (font == null) + public Font getFont() { + if(font == null) return component.getFont(); else return font; } /** - * Set the Font to be used for rendering the text + * Set the Font to be used for rendering the text * - * @param font the Font to be used for rendering the text + * @param font the Font to be used for rendering the text */ - public void setFont(Font font) - { + public void setFont(Font font) { this.font = font; calculateIconDimensions(); } /** - * Get the foreground Color used to render the text. This will default to - * the foreground Color of the component unless the foreground Color has - * been overridden by using the setForeground() method. + * Get the foreground Color used to render the text. This will default to + * the foreground Color of the component unless the foreground Color has + * been overridden by using the setForeground() method. * - * @return the Color used to render the text + * @return the Color used to render the text */ - public Color getForeground() - { - if (foreground == null) + public Color getForeground() { + if(foreground == null) return component.getForeground(); else return foreground; } /** - * Set the foreground Color to be used for rendering the text + * Set the foreground Color to be used for rendering the text * - * @param foreground the foreground Color to be used for rendering the text + * @param foreground the foreground Color to be used for rendering the text */ - public void setForeground(Color foreground) - { + public void setForeground(Color foreground) { this.foreground = foreground; component.repaint(); } /** - * Get the padding used when rendering the text + * Get the padding used when rendering the text * - * @return the padding specified in pixels + * @return the padding specified in pixels */ - public int getPadding() - { + public int getPadding() { return padding; } /** - * By default the size of the Icon is based on the size of the rendered - * text. You can specify some padding to be added to the start and end - * of the text when it is rendered. + * By default the size of the Icon is based on the size of the rendered + * text. You can specify some padding to be added to the start and end + * of the text when it is rendered. * - * @param padding the padding amount in pixels + * @param padding the padding amount in pixels */ - public void setPadding(int padding) - { + public void setPadding(int padding) { this.padding = padding; calculateIconDimensions(); } /** - * Calculate the size of the Icon using the FontMetrics of the Font. + * Calculate the size of the Icon using the FontMetrics of the Font. */ - private void calculateIconDimensions() - { + private void calculateIconDimensions() { Font font = getFont(); - FontMetrics fm = component.getFontMetrics( font ); + FontMetrics fm = component.getFontMetrics(font); - if (layout == Layout.HORIZONTAL) - { - iconWidth = fm.stringWidth( text ) + (padding * 2); + if(layout == Layout.HORIZONTAL) { + iconWidth = fm.stringWidth(text) + (padding * 2); iconHeight = fm.getHeight(); - } - else if (layout == Layout.VERTICAL) - { + } else if(layout == Layout.VERTICAL) { int maxWidth = 0; strings = new String[text.length()]; stringWidths = new int[text.length()]; // Find the widest character in the text string - for (int i = 0; i < text.length(); i++) - { + for(int i = 0; i < text.length(); i++) { strings[i] = text.substring(i, i + 1); - stringWidths[i] = fm.stringWidth( strings[i] ); + stringWidths[i] = fm.stringWidth(strings[i]); maxWidth = Math.max(maxWidth, stringWidths[i]); } @@ -239,73 +207,65 @@ else if (layout == Layout.VERTICAL) component.revalidate(); } -// -// Implement the Icon Interface -// + /** - * Gets the width of this icon. + * Gets the width of this icon. * - * @return the width of the icon in pixels. + * @return the width of the icon in pixels. */ @Override - public int getIconWidth() - { + public int getIconWidth() { return iconWidth; } +// +// Implement the Icon Interface +// /** - * Gets the height of this icon. + * Gets the height of this icon. * - * @return the height of the icon in pixels. + * @return the height of the icon in pixels. */ @Override - public int getIconHeight() - { + public int getIconHeight() { return iconHeight; } /** - * Paint the icons of this compound icon at the specified location + * Paint the icons of this compound icon at the specified location * - * @param c The component to which the icon is added - * @param g the graphics context - * @param x the X coordinate of the icon's top-left corner - * @param y the Y coordinate of the icon's top-left corner + * @param c The component to which the icon is added + * @param g the graphics context + * @param x the X coordinate of the icon's top-left corner + * @param y the Y coordinate of the icon's top-left corner */ @Override - public void paintIcon(Component c, Graphics g, int x, int y) - { - Graphics2D g2 = (Graphics2D)g.create(); + public void paintIcon(Component c, Graphics g, int x, int y) { + Graphics2D g2 = (Graphics2D) g.create(); // The "desktophints" is supported in JDK6 Toolkit toolkit = Toolkit.getDefaultToolkit(); - Map map = (Map)(toolkit.getDesktopProperty("awt.font.desktophints")); + Map map = (Map) (toolkit.getDesktopProperty("awt.font.desktophints")); - if (map != null) - { + if(map != null) { g2.addRenderingHints(map); - } - else + } else g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_ON ); + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - g2.setFont( getFont() ); - g2.setColor( getForeground() ); + g2.setFont(getFont()); + g2.setColor(getForeground()); FontMetrics fm = g2.getFontMetrics(); - if (layout == Layout.HORIZONTAL) - { - g2.translate(x, y + fm.getAscent()); + if(layout == Layout.HORIZONTAL) { + g2.translate(x, y + fm.getAscent()); g2.drawString(text, padding, 0); - } - else if (layout == Layout.VERTICAL) - { + } else if(layout == Layout.VERTICAL) { int offsetY = fm.getAscent() - fm.getDescent() + padding; int incrementY = fm.getHeight() - fm.getDescent(); - for (int i = 0; i < text.length(); i++) - { + for(int i = 0; i < text.length(); i++) { int offsetX = Math.round((getIconWidth() - stringWidths[i]) / 2.0f); g2.drawString(strings[i], x + offsetX, y + offsetY); offsetY += incrementY; @@ -314,14 +274,19 @@ else if (layout == Layout.VERTICAL) g2.dispose(); } + // // Implement the PropertyChangeListener interface // - public void propertyChange(PropertyChangeEvent e) - { + public void propertyChange(PropertyChangeEvent e) { // Handle font change when using the default font - if (font == null) + if(font == null) calculateIconDimensions(); } + + public enum Layout { + HORIZONTAL, + VERTICAL; + } } \ No newline at end of file diff --git a/src/main/java/com/dfsek/noise/swing/actions/GoToLineAction.java b/src/main/java/com/dfsek/noise/swing/actions/GoToLineAction.java index 8e87371..9dd59b4 100644 --- a/src/main/java/com/dfsek/noise/swing/actions/GoToLineAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/GoToLineAction.java @@ -24,20 +24,20 @@ public GoToLineAction(NoiseTool noiseTool) { @Override public void actionPerformed(ActionEvent e) { - if (noiseTool.getFindDialog().isVisible()) { + if(noiseTool.getFindDialog().isVisible()) { noiseTool.getFindDialog().setVisible(false); } - if (noiseTool.getReplaceDialog().isVisible()) { + if(noiseTool.getReplaceDialog().isVisible()) { noiseTool.getReplaceDialog().setVisible(false); } GoToDialog dialog = new GoToDialog(noiseTool); dialog.setMaxLineNumberAllowed(noiseTool.getTextArea().getLineCount()); dialog.setVisible(true); int line = dialog.getLineNumber(); - if (line > 0) { + if(line > 0) { try { noiseTool.getTextArea().setCaretPosition(noiseTool.getTextArea().getLineStartOffset(line - 1)); - } catch (BadLocationException ble) { // Never happens + } catch(BadLocationException ble) { // Never happens UIManager.getLookAndFeel().provideErrorFeedback(noiseTool.getFindDialog()); ble.printStackTrace(); } diff --git a/src/main/java/com/dfsek/noise/swing/actions/LookAndFeelAction.java b/src/main/java/com/dfsek/noise/swing/actions/LookAndFeelAction.java index 2e96cb5..62ae9fd 100644 --- a/src/main/java/com/dfsek/noise/swing/actions/LookAndFeelAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/LookAndFeelAction.java @@ -24,15 +24,15 @@ public void actionPerformed(ActionEvent e) { try { UIManager.setLookAndFeel(info.getClassName()); SwingUtilities.updateComponentTreeUI(noiseTool); - if (noiseTool.getFindDialog() != null) { + if(noiseTool.getFindDialog() != null) { noiseTool.getFindDialog().updateUI(); noiseTool.getReplaceDialog().updateUI(); noiseTool.getNoise().update(); } noiseTool.pack(); - } catch (RuntimeException re) { + } catch(RuntimeException re) { throw re; // FindBugs - } catch (Exception ex) { + } catch(Exception ex) { ex.printStackTrace(); } } diff --git a/src/main/java/com/dfsek/noise/swing/actions/OpenFileAction.java b/src/main/java/com/dfsek/noise/swing/actions/OpenFileAction.java index 23db1b5..4a87f11 100644 --- a/src/main/java/com/dfsek/noise/swing/actions/OpenFileAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/OpenFileAction.java @@ -28,7 +28,7 @@ public void actionPerformed(ActionEvent actionEvent) { System.out.println("Opening " + file.getAbsolutePath()); try { noiseTool.getTextArea().setText(IOUtils.toString(new FileInputStream(file), Charset.defaultCharset())); - } catch (IOException e) { + } catch(IOException e) { e.printStackTrace(); } } else { diff --git a/src/main/java/com/dfsek/noise/swing/actions/SaveAction.java b/src/main/java/com/dfsek/noise/swing/actions/SaveAction.java index 97947cd..6e0f53a 100644 --- a/src/main/java/com/dfsek/noise/swing/actions/SaveAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/SaveAction.java @@ -23,19 +23,19 @@ public void actionPerformed(ActionEvent actionEvent) { File file = noiseTool.getFileChooser().getSelectedFile(); - if (file == null) { + if(file == null) { System.out.println("No file is selected."); return; } System.out.println("Saving to " + file.getAbsolutePath()); - try (FileWriter writer = new FileWriter(file)) { - if (!file.exists()) { + try(FileWriter writer = new FileWriter(file)) { + if(!file.exists()) { file.getParentFile().mkdirs(); file.createNewFile(); } IOUtils.write(noiseTool.getTextArea().getText(), writer); - } catch (IOException e) { + } catch(IOException e) { e.printStackTrace(); } diff --git a/src/main/java/com/dfsek/noise/swing/actions/SaveAsAction.java b/src/main/java/com/dfsek/noise/swing/actions/SaveAsAction.java index 0cc92ba..2929b41 100644 --- a/src/main/java/com/dfsek/noise/swing/actions/SaveAsAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/SaveAsAction.java @@ -6,10 +6,8 @@ import javax.swing.*; import java.awt.event.ActionEvent; import java.io.File; -import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; -import java.nio.charset.Charset; public class SaveAsAction extends AbstractAction { private final NoiseTool noiseTool; @@ -27,13 +25,13 @@ public void actionPerformed(ActionEvent actionEvent) { if(returnVal == JFileChooser.APPROVE_OPTION) { File file = noiseTool.getFileChooser().getSelectedFile(); System.out.println("Saving to " + file.getAbsolutePath()); - try(FileWriter writer = new FileWriter(file)) { + try(FileWriter writer = new FileWriter(file)) { if(!file.exists()) { file.getParentFile().mkdirs(); file.createNewFile(); } IOUtils.write(noiseTool.getTextArea().getText(), writer); - } catch (IOException e) { + } catch(IOException e) { e.printStackTrace(); } } else { diff --git a/src/main/java/com/dfsek/noise/swing/actions/SaveRenderAsAction.java b/src/main/java/com/dfsek/noise/swing/actions/SaveRenderAsAction.java index 9ab3095..809be96 100644 --- a/src/main/java/com/dfsek/noise/swing/actions/SaveRenderAsAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/SaveRenderAsAction.java @@ -1,16 +1,12 @@ package com.dfsek.noise.swing.actions; import com.dfsek.noise.NoiseTool; -import org.apache.commons.io.IOUtils; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.event.ActionEvent; -import java.awt.image.RenderedImage; import java.io.File; import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; public class SaveRenderAsAction extends AbstractAction { private final NoiseTool noiseTool; @@ -34,13 +30,13 @@ public void actionPerformed(ActionEvent actionEvent) { } System.out.println("Saving to " + file.getAbsolutePath()); - try(FileOutputStream writer = new FileOutputStream(file)) { + try(FileOutputStream writer = new FileOutputStream(file)) { if(!file.exists()) { file.getParentFile().mkdirs(); file.createNewFile(); } ImageIO.write(noiseTool.getNoise().getRender(), "png", writer); - } catch (Exception e) { + } catch(Exception e) { e.printStackTrace(); } } else { diff --git a/src/main/java/com/dfsek/noise/swing/actions/ShowFindDialogAction.java b/src/main/java/com/dfsek/noise/swing/actions/ShowFindDialogAction.java index 235d5eb..2b89654 100644 --- a/src/main/java/com/dfsek/noise/swing/actions/ShowFindDialogAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/ShowFindDialogAction.java @@ -22,7 +22,7 @@ public ShowFindDialogAction(NoiseTool noiseTool) { @Override public void actionPerformed(ActionEvent e) { - if (noiseTool.getReplaceDialog().isVisible()) { + if(noiseTool.getReplaceDialog().isVisible()) { noiseTool.getReplaceDialog().setVisible(false); } noiseTool.getFindDialog().setVisible(true); diff --git a/src/main/java/com/dfsek/noise/swing/actions/ShowReplaceDialogAction.java b/src/main/java/com/dfsek/noise/swing/actions/ShowReplaceDialogAction.java index f66576c..9bd0ac1 100644 --- a/src/main/java/com/dfsek/noise/swing/actions/ShowReplaceDialogAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/ShowReplaceDialogAction.java @@ -22,7 +22,7 @@ public ShowReplaceDialogAction(NoiseTool noiseTool) { @Override public void actionPerformed(ActionEvent e) { - if (noiseTool.getFindDialog().isVisible()) { + if(noiseTool.getFindDialog().isVisible()) { noiseTool.getFindDialog().setVisible(false); } noiseTool.getReplaceDialog().setVisible(true); diff --git a/src/main/java/com/dfsek/noise/swing/actions/UpdateNoiseAction.java b/src/main/java/com/dfsek/noise/swing/actions/UpdateNoiseAction.java index 578b71b..4f4e670 100644 --- a/src/main/java/com/dfsek/noise/swing/actions/UpdateNoiseAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/UpdateNoiseAction.java @@ -4,9 +4,8 @@ import javax.swing.*; import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -public class UpdateNoiseAction extends AbstractAction { +public class UpdateNoiseAction extends AbstractAction { private final NoisePanel noisePanel; public UpdateNoiseAction(NoisePanel noisePanel) { diff --git a/src/main/java/com/dfsek/noise/utils/SwingUtils.java b/src/main/java/com/dfsek/noise/utils/SwingUtils.java index 0df582e..a644ec0 100644 --- a/src/main/java/com/dfsek/noise/utils/SwingUtils.java +++ b/src/main/java/com/dfsek/noise/utils/SwingUtils.java @@ -22,12 +22,12 @@ private static SpringLayout.Constraints getConstraintsForCell( * height is similarly determined for each row. * The parent is made just big enough to fit them all. * - * @param rows number of rows - * @param cols number of columns + * @param rows number of rows + * @param cols number of columns * @param initialX x location to start the grid at * @param initialY y location to start the grid at - * @param xPad x padding between cells - * @param yPad y padding between cells + * @param xPad x padding between cells + * @param yPad y padding between cells */ public static void makeCompactGrid(Container parent, int rows, int cols, @@ -35,22 +35,22 @@ public static void makeCompactGrid(Container parent, int xPad, int yPad) { SpringLayout layout; try { - layout = (SpringLayout)parent.getLayout(); - } catch (ClassCastException exc) { + layout = (SpringLayout) parent.getLayout(); + } catch(ClassCastException exc) { System.err.println("The first argument to makeCompactGrid must use SpringLayout."); return; } //Align all cells in each column and make them the same width. Spring x = Spring.constant(initialX); - for (int c = 0; c < cols; c++) { + for(int c = 0; c < cols; c++) { Spring width = Spring.constant(0); - for (int r = 0; r < rows; r++) { + for(int r = 0; r < rows; r++) { width = Spring.max(width, getConstraintsForCell(r, c, parent, cols). getWidth()); } - for (int r = 0; r < rows; r++) { + for(int r = 0; r < rows; r++) { SpringLayout.Constraints constraints = getConstraintsForCell(r, c, parent, cols); constraints.setX(x); @@ -61,10 +61,10 @@ public static void makeCompactGrid(Container parent, //Align all cells in each row and make them the same height. Spring y = Spring.constant(initialY); - for (int r = 0; r < rows; r++) { + for(int r = 0; r < rows; r++) { Spring height = Spring.constant(35); - for (int c = 0; c < cols; c++) { + for(int c = 0; c < cols; c++) { SpringLayout.Constraints constraints = getConstraintsForCell(r, c, parent, cols); constraints.setY(y); From 7d73575bacd7972e223467f6d411f40193f7991e Mon Sep 17 00:00:00 2001 From: dfsek Date: Wed, 17 Mar 2021 18:24:39 -0700 Subject: [PATCH 09/12] add keybinds --- src/main/java/com/dfsek/noise/NoiseTool.java | 21 +++++++++--- .../com/dfsek/noise/thread/NoiseAction.java | 32 ------------------- 2 files changed, 17 insertions(+), 36 deletions(-) delete mode 100644 src/main/java/com/dfsek/noise/thread/NoiseAction.java diff --git a/src/main/java/com/dfsek/noise/NoiseTool.java b/src/main/java/com/dfsek/noise/NoiseTool.java index 50421f0..b9a3bda 100644 --- a/src/main/java/com/dfsek/noise/NoiseTool.java +++ b/src/main/java/com/dfsek/noise/NoiseTool.java @@ -208,9 +208,18 @@ private JMenuBar createMenuBar() { JMenu menu = new JMenu("File"); - menu.add(new OpenFileAction(this)); - menu.add(new SaveAction(this)); - menu.add(new SaveAsAction(this)); + Action open = new OpenFileAction(this); + Action save = new SaveAction(this); + Action saveAs = new SaveAsAction(this); + + open.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_MASK)); + save.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK)); + saveAs.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK | KeyEvent.SHIFT_MASK)); + + menu.add(open); + menu.add(save); + menu.add(saveAs); + mb.add(menu); @@ -245,7 +254,11 @@ private JMenuBar createMenuBar() { mb.add(menu); menu = new JMenu("Noise"); - menu.add(new UpdateNoiseAction(noise)); + + Action up = new UpdateNoiseAction(noise); + + up.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0)); + menu.add(up); menu.add(new MutableBooleanAction(noise.getChunk(), "Toggle Chunk Borders")); mb.add(menu); diff --git a/src/main/java/com/dfsek/noise/thread/NoiseAction.java b/src/main/java/com/dfsek/noise/thread/NoiseAction.java deleted file mode 100644 index f429cd7..0000000 --- a/src/main/java/com/dfsek/noise/thread/NoiseAction.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.dfsek.noise.thread; - -import com.dfsek.terra.api.math.noise.NoiseSampler; - -import java.util.concurrent.RecursiveAction; - -public class NoiseAction extends RecursiveAction { - private final double originX; - - private final double originZ; - - private final double[] result; - - private final NoiseSampler sampler; - - private final double in; - - public NoiseAction(double originX, double originZ, double[] result, NoiseSampler sampler, double in) { - this.originX = originX; - this.originZ = originZ; - this.result = result; - this.sampler = sampler; - this.in = in; - } - - @Override - protected void compute() { - for(int i = 0; i <= result.length; i++) { - result[i] = sampler.getNoise(i + originX, in + originZ); - } - } -} From 1bef3fd4942295a38e1a9d2d8a7fd5ee4194dd2c Mon Sep 17 00:00:00 2001 From: dfsek Date: Wed, 17 Mar 2021 18:26:22 -0700 Subject: [PATCH 10/12] add save render --- src/main/java/com/dfsek/noise/NoiseTool.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/dfsek/noise/NoiseTool.java b/src/main/java/com/dfsek/noise/NoiseTool.java index b9a3bda..7d5bbd9 100644 --- a/src/main/java/com/dfsek/noise/NoiseTool.java +++ b/src/main/java/com/dfsek/noise/NoiseTool.java @@ -10,6 +10,7 @@ import com.dfsek.noise.swing.actions.OpenFileAction; import com.dfsek.noise.swing.actions.SaveAction; import com.dfsek.noise.swing.actions.SaveAsAction; +import com.dfsek.noise.swing.actions.SaveRenderAsAction; import com.dfsek.noise.swing.actions.ShowFindDialogAction; import com.dfsek.noise.swing.actions.ShowReplaceDialogAction; import com.dfsek.noise.swing.actions.UpdateNoiseAction; @@ -211,14 +212,17 @@ private JMenuBar createMenuBar() { Action open = new OpenFileAction(this); Action save = new SaveAction(this); Action saveAs = new SaveAsAction(this); + Action saveRender = new SaveRenderAsAction(this); open.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_MASK)); save.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK)); saveAs.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK | KeyEvent.SHIFT_MASK)); + saveRender.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK | KeyEvent.ALT_MASK)); menu.add(open); menu.add(save); menu.add(saveAs); + menu.add(saveRender); mb.add(menu); From e6e797eb0c4f9d07b83e511e91c848c8c6d4ca53 Mon Sep 17 00:00:00 2001 From: dfsek Date: Wed, 17 Mar 2021 18:38:50 -0700 Subject: [PATCH 11/12] bump version --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4f102d9..f3697ea 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } group = "com.dfsek" -version = "0.5.1" +version = "1.0.0" repositories { maven { url = uri("https://repo.codemc.org/repository/maven-public") } From ea64e0732a6afcd09b144188f0ae10c8934880df Mon Sep 17 00:00:00 2001 From: dfsek Date: Thu, 18 Mar 2021 00:55:04 -0700 Subject: [PATCH 12/12] cleanup and improvements --- src/main/java/com/dfsek/noise/NoiseTool.java | 1 + .../com/dfsek/noise/swing/NoisePanel.java | 162 +++++++++++------- .../swing/actions/UpdateNoiseAction.java | 1 + 3 files changed, 103 insertions(+), 61 deletions(-) diff --git a/src/main/java/com/dfsek/noise/NoiseTool.java b/src/main/java/com/dfsek/noise/NoiseTool.java index 7d5bbd9..d1fbbda 100644 --- a/src/main/java/com/dfsek/noise/NoiseTool.java +++ b/src/main/java/com/dfsek/noise/NoiseTool.java @@ -158,6 +158,7 @@ private NoiseTool() { pack(); setLocationRelativeTo(null); + noise.reload(); noise.update(); } diff --git a/src/main/java/com/dfsek/noise/swing/NoisePanel.java b/src/main/java/com/dfsek/noise/swing/NoisePanel.java index 461406b..43b55d9 100644 --- a/src/main/java/com/dfsek/noise/swing/NoisePanel.java +++ b/src/main/java/com/dfsek/noise/swing/NoisePanel.java @@ -39,15 +39,24 @@ public class NoisePanel extends JPanel { private final MutableBoolean chunk = new MutableBoolean(); private final NoiseSettingsPanel settingsPanel; - + private final MutableBoolean error; + private final MutableBoolean moved; private BufferedImage render; + private NoiseSeeded noiseSeeded; + private ProbabilityCollection colorCollection; + private final MutableBoolean color = new MutableBoolean(); + public NoisePanel(RSyntaxTextArea textArea, JTextArea statisticsPanel, NoiseDistributionPanel distributionPanel, final NoiseSettingsPanel settingsPanel) { this.textArea = textArea; this.statisticsPanel = statisticsPanel; this.distributionPanel = distributionPanel; this.settingsPanel = settingsPanel; this.image = new JLabel(); + this.error = new MutableBoolean(); + this.moved = new MutableBoolean(); + this.moved.set(false); + error.set(false); add(new JPanel() { private volatile int screenX; @@ -63,25 +72,31 @@ public NoisePanel(RSyntaxTextArea textArea, JTextArea statisticsPanel, NoiseDist @Override public void mouseClicked(MouseEvent e) { + System.nanoTime(); } @Override public void mousePressed(MouseEvent e) { - screenX = e.getXOnScreen(); - screenY = e.getYOnScreen(); + if(!error.get()) { + screenX = e.getXOnScreen(); + screenY = e.getYOnScreen(); - myX = getX(); - myY = getY(); + myX = getX(); + myY = getY(); + } } @Override public void mouseReleased(MouseEvent e) { - int deltaX = e.getXOnScreen() - screenX; - int deltaY = e.getYOnScreen() - screenY; - - settingsPanel.setOriginX(settingsPanel.getOriginX() - (myX + deltaX)); - settingsPanel.setOriginZ(settingsPanel.getOriginZ() - (myY + deltaY)); - NoisePanel.this.update(); + if(!error.get() && moved.get()) { + moved.set(false); + int deltaX = e.getXOnScreen() - screenX; + int deltaY = e.getYOnScreen() - screenY; + + settingsPanel.setOriginX(settingsPanel.getOriginX() - deltaX); + settingsPanel.setOriginZ(settingsPanel.getOriginZ() - deltaY); + NoisePanel.this.update(); + } } @Override @@ -97,10 +112,13 @@ public void mouseExited(MouseEvent e) { @Override public void mouseDragged(MouseEvent e) { - int deltaX = e.getXOnScreen() - screenX; - int deltaY = e.getYOnScreen() - screenY; + if(!error.get()) { + moved.set(true); + int deltaX = e.getXOnScreen() - screenX; + int deltaY = e.getYOnScreen() - screenY; - setLocation(myX + deltaX, myY + deltaY); + setLocation(myX + deltaX, myY + deltaY); + } } @Override @@ -122,9 +140,42 @@ private static int buildRGBA(int in) { } public void update() { + if(this.error.get()) return; + this.error.set(true); try { this.render = getImage(this.settingsPanel.getSeed()); this.image.setIcon(new ImageIcon(this.render)); + this.error.set(false); + } catch(Exception e) { + e.printStackTrace(); + this.image.setIcon(new TextIcon(this, "An error occurred. ")); + this.statisticsPanel.setText("An error occurred."); + this.distributionPanel.error(); + } + } + + public void reload() { + this.error.set(true); + try { + FolderLoader folderLoader = new FolderLoader(Paths.get("./")); + ConfigLoader loader = new ConfigLoader(); + loader.registerLoader(NoiseSeeded.class, new NoiseSamplerBuilderLoader(new NoiseRegistry())) + .registerLoader(BufferedImage.class, new BufferedImageLoader(folderLoader)) + .registerLoader(ProbabilityCollection.class, new ProbabilityCollectionLoader()); + new GenericLoaders(null).register(loader); + NoiseConfigTemplate template = new NoiseConfigTemplate(); + File colorFile = new File("./color.yml"); + + ColorConfigTemplate colorConfigTemplate = new ColorConfigTemplate(); + if(colorFile.exists()) { + loader.load(colorConfigTemplate, new FileInputStream(colorFile)); + color.set(colorConfigTemplate.enable()); + } + this.colorCollection = colorConfigTemplate.getColors(); + loader.load(template, this.textArea.getText()); + System.out.println(template.getBuilder().getDimensions() + " Dimensions."); + this.noiseSeeded = template.getBuilder(); + this.error.set(false); } catch(Exception e) { e.printStackTrace(); this.image.setIcon(new TextIcon(this, "An error occurred. ")); @@ -141,77 +192,66 @@ public MutableBoolean getChunk() { return this.chunk; } - private BufferedImage getImage(long seed) throws ConfigException, FileNotFoundException { + private BufferedImage getImage(long seed) { System.out.println("Rendering noise with seed " + seed); - FolderLoader folderLoader = new FolderLoader(Paths.get("./")); - ConfigLoader loader = new ConfigLoader(); - loader.registerLoader(NoiseSeeded.class, new NoiseSamplerBuilderLoader(new NoiseRegistry())) - .registerLoader(BufferedImage.class, new BufferedImageLoader(folderLoader)) - .registerLoader(ProbabilityCollection.class, new ProbabilityCollectionLoader()); - (new GenericLoaders(null)).register(loader); - NoiseConfigTemplate template = new NoiseConfigTemplate(); - File colorFile = new File("./color.yml"); - boolean colors = false; - ColorConfigTemplate color = new ColorConfigTemplate(); - if(colorFile.exists()) { - loader.load(color, new FileInputStream(colorFile)); - colors = color.enable(); - } - ProbabilityCollection colorCollection = color.getColors(); - loader.load(template, this.textArea.getText()); - System.out.println(template.getBuilder().getDimensions() + " Dimensions."); - NoiseSampler noise = template.getBuilder().apply(seed); + + NoiseSampler noise = noiseSeeded.apply(seed); int sizeX = getWidth(); int sizeY = getHeight(); double originX = this.settingsPanel.getOriginX(); double originZ = this.settingsPanel.getOriginZ(); - BufferedImage image = new BufferedImage(sizeX, sizeY, 2); + BufferedImage image = new BufferedImage(sizeX, sizeY, BufferedImage.TYPE_INT_ARGB); double[][] noiseVals = new double[sizeX][sizeY]; - int[][] rgbVals = new int[sizeX][sizeY]; - double max = Double.MIN_VALUE; - double min = Double.MAX_VALUE; - int[] buckets = new int[sizeX]; - long s = System.nanoTime(); + + + + long startTime = System.nanoTime(); for(int x = 0; x < noiseVals.length; x++) { for(int z = 0; z < (noiseVals[x]).length; z++) { double n = noise.getNoise(x + originX, z + originZ); noiseVals[x][z] = n; - max = Math.max(n, max); - min = Math.min(n, min); - if(colors) - rgbVals[x][z] = colorCollection.get(noise, x + originX, z + originZ); } } - long time = System.nanoTime() - s; - double ms = time / 1000000.0D; - int i; - for(i = 0; i < noiseVals.length; i++) { - for(int z = 0; z < (noiseVals[i]).length; z++) { - if(colors) { - image.setRGB(i, z, rgbVals[i][z] + -16777216); + long endTime = System.nanoTime(); + double timeMs = (endTime - startTime) / 1000000.0D; + + double max = Double.MIN_VALUE; + double min = Double.MAX_VALUE; + for(double[] noiseVal : noiseVals) { // do this separately to give more accurate measure of performance. + for(double v : noiseVal) { + max = FastMath.max(v, max); + min = FastMath.min(v, min); + } + } + + + int[] buckets = new int[sizeX]; + for(int x = 0; x < noiseVals.length; x++) { + for(int z = 0; z < (noiseVals[x]).length; z++) { + if(color.get()) { + image.setRGB(x, z, colorCollection.get(noise, x + originX, z + originZ) + -16777216); } else { - image.setRGB(i, z, buildRGBA(normal(noiseVals[i][z], 255.0D, min, max))); + image.setRGB(x, z, buildRGBA(normal(noiseVals[x][z], 255.0D, min, max))); } - buckets[normal(noiseVals[i][z], (sizeX - 1), min, max)] = buckets[normal(noiseVals[i][z], (sizeX - 1), min, max)] + 1; + buckets[normal(noiseVals[x][z], (sizeX - 1), min, max)] = buckets[normal(noiseVals[x][z], (sizeX - 1), min, max)] + 1; } } + if(this.chunk.get()) { - for(i = 0; i < FastMath.floorDiv(image.getWidth(), 16); i++) { - for(int j = 0; j < image.getHeight(); ) { - image.setRGB(i * 16, j, buildRGBA(0)); - j++; + for(int x = 0; x < FastMath.floorDiv(image.getWidth(), 16); x++) { + for(int y = 0; y < image.getHeight(); y++) { + image.setRGB(x * 16, y, buildRGBA(0)); } } for(int y = 0; y < FastMath.floorDiv(image.getHeight(), 16); y++) { - for(int j = 0; j < image.getWidth(); ) { - image.setRGB(j, y * 16, buildRGBA(0)); - j++; + for(int x = 0; x < image.getWidth(); x++) { + image.setRGB(x, y * 16, buildRGBA(0)); } } } - this.statisticsPanel.setText("min: " + min + "\nmax: " + max + "\nseed: " + seed + "\ntime: " + ms + "ms"); + this.statisticsPanel.setText("min: " + min + "\nmax: " + max + "\nseed: " + seed + "\ntime: " + timeMs + "ms"); this.distributionPanel.update(buckets); - System.out.println("Rendered " + (sizeX * sizeY) + " points in " + ms + "ms."); + System.out.println("Rendered " + (sizeX * sizeY) + " points in " + timeMs + "ms."); return image; } } diff --git a/src/main/java/com/dfsek/noise/swing/actions/UpdateNoiseAction.java b/src/main/java/com/dfsek/noise/swing/actions/UpdateNoiseAction.java index 4f4e670..2837225 100644 --- a/src/main/java/com/dfsek/noise/swing/actions/UpdateNoiseAction.java +++ b/src/main/java/com/dfsek/noise/swing/actions/UpdateNoiseAction.java @@ -15,6 +15,7 @@ public UpdateNoiseAction(NoisePanel noisePanel) { @Override public void actionPerformed(ActionEvent actionEvent) { + noisePanel.reload(); noisePanel.update(); } }