From 42db19b253102f2d5469223d4bc26f895b25fbb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Sun, 26 Feb 2023 00:31:39 +0100 Subject: [PATCH] [MULTI] + layout working now BUT WIP + transform + fixed (virtual) paper size + size adjust + transparency adjust + step-adjust --- .gitignore | 4 +- src/main/java/de/ph87/kindermalen/CONFIG.java | 17 ++ .../de/ph87/kindermalen/ClickListener.java | 47 ----- .../java/de/ph87/kindermalen/KeyListener.java | 40 ----- src/main/java/de/ph87/kindermalen/Main.java | 117 ------------- .../de/ph87/kindermalen/MotionListener.java | 32 ---- .../java/de/ph87/kindermalen/Sidebar.java | 53 ++++++ src/main/java/de/ph87/kindermalen/Window.java | 73 ++++++++ .../de/ph87/kindermalen/drawing/Drawing.java | 56 +++++- .../kindermalen/drawing/DrawingPanel.java | 161 +++++++++++++----- .../de/ph87/kindermalen/drawing/Layer.java | 33 +++- .../java/de/ph87/kindermalen/tool/Tool.java | 21 --- .../de/ph87/kindermalen/tool/ToolPanel.java | 18 -- .../kindermalen/tool/stamp/StampTool.java | 118 ------------- .../tool/stamp/StampToolPanel.java | 67 -------- .../de/ph87/kindermalen/toolbox/Observer.java | 7 - .../de/ph87/kindermalen/toolbox/ToolBox.java | 38 ----- .../kindermalen/toolbox/ToolBoxPanel.java | 56 ------ .../de/ph87/kindermalen/tools/ToolButton.java | 32 ++++ .../java/de/ph87/kindermalen/tools/Tools.java | 38 +++++ .../de/ph87/kindermalen/tools/ToolsPanel.java | 30 ++++ .../de/ph87/kindermalen/tools/tool/Tool.java | 31 ++++ .../kindermalen/tools/tool/ToolPanel.java | 10 ++ .../{ => tools}/tool/stamp/Stamp.java | 13 +- .../tools/tool/stamp/StampButton.java | 80 +++++++++ .../tools/tool/stamp/StampListPanel.java | 23 +++ .../tools/tool/stamp/StampOptionsPanel.java | 62 +++++++ .../tools/tool/stamp/StampPanel.java | 20 +++ .../tools/tool/stamp/StampTool.java | 96 +++++++++++ .../de/ph87/kindermalen/util/FileHelper.java | 22 +++ .../kindermalen/{ => util}/ImageHelper.java | 2 +- .../de/ph87/kindermalen/util/KeyListener.java | 39 +++++ .../{tool/stamp => util}/ListHelper.java | 2 +- .../ph87/kindermalen/util/MouseListener.java | 109 ++++++++++++ .../util/MyGridBagConstraints.java | 23 +++ .../kindermalen/{ => util}/Publisher.java | 2 +- .../kindermalen/{ => util}/Subscription.java | 2 +- .../kindermalen/{drawing => util}/Vector.java | 19 ++- 38 files changed, 988 insertions(+), 625 deletions(-) create mode 100644 src/main/java/de/ph87/kindermalen/CONFIG.java delete mode 100644 src/main/java/de/ph87/kindermalen/ClickListener.java delete mode 100644 src/main/java/de/ph87/kindermalen/KeyListener.java delete mode 100644 src/main/java/de/ph87/kindermalen/Main.java delete mode 100644 src/main/java/de/ph87/kindermalen/MotionListener.java create mode 100644 src/main/java/de/ph87/kindermalen/Sidebar.java create mode 100644 src/main/java/de/ph87/kindermalen/Window.java delete mode 100644 src/main/java/de/ph87/kindermalen/tool/Tool.java delete mode 100644 src/main/java/de/ph87/kindermalen/tool/ToolPanel.java delete mode 100644 src/main/java/de/ph87/kindermalen/tool/stamp/StampTool.java delete mode 100644 src/main/java/de/ph87/kindermalen/tool/stamp/StampToolPanel.java delete mode 100644 src/main/java/de/ph87/kindermalen/toolbox/Observer.java delete mode 100644 src/main/java/de/ph87/kindermalen/toolbox/ToolBox.java delete mode 100644 src/main/java/de/ph87/kindermalen/toolbox/ToolBoxPanel.java create mode 100644 src/main/java/de/ph87/kindermalen/tools/ToolButton.java create mode 100644 src/main/java/de/ph87/kindermalen/tools/Tools.java create mode 100644 src/main/java/de/ph87/kindermalen/tools/ToolsPanel.java create mode 100644 src/main/java/de/ph87/kindermalen/tools/tool/Tool.java create mode 100644 src/main/java/de/ph87/kindermalen/tools/tool/ToolPanel.java rename src/main/java/de/ph87/kindermalen/{ => tools}/tool/stamp/Stamp.java (73%) create mode 100644 src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampButton.java create mode 100644 src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampListPanel.java create mode 100644 src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampOptionsPanel.java create mode 100644 src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampPanel.java create mode 100644 src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampTool.java create mode 100644 src/main/java/de/ph87/kindermalen/util/FileHelper.java rename src/main/java/de/ph87/kindermalen/{ => util}/ImageHelper.java (97%) create mode 100644 src/main/java/de/ph87/kindermalen/util/KeyListener.java rename src/main/java/de/ph87/kindermalen/{tool/stamp => util}/ListHelper.java (94%) create mode 100644 src/main/java/de/ph87/kindermalen/util/MouseListener.java create mode 100644 src/main/java/de/ph87/kindermalen/util/MyGridBagConstraints.java rename src/main/java/de/ph87/kindermalen/{ => util}/Publisher.java (96%) rename src/main/java/de/ph87/kindermalen/{ => util}/Subscription.java (92%) rename src/main/java/de/ph87/kindermalen/{drawing => util}/Vector.java (64%) diff --git a/.gitignore b/.gitignore index fb12677..91326aa 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ target/ !.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ +!**/src/window/**/target/ !**/src/test/**/target/ ### IntelliJ IDEA ### @@ -32,7 +32,7 @@ target/ /nbdist/ /.nb-gradle/ build/ -!**/src/main/**/build/ +!**/src/window/**/build/ !**/src/test/**/build/ ### VS Code ### diff --git a/src/main/java/de/ph87/kindermalen/CONFIG.java b/src/main/java/de/ph87/kindermalen/CONFIG.java new file mode 100644 index 0000000..696dccd --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/CONFIG.java @@ -0,0 +1,17 @@ +package de.ph87.kindermalen; + +import java.awt.*; + +public class CONFIG { + + public static final boolean BORDERS = false; + + public static final BasicStroke STROKE_BORDER = new BasicStroke(1f); + + public static final int STROKE_HIGHLIGHT_WIDTH = 4; + + public static final BasicStroke STROKE_HIGHLIGHT = new BasicStroke(STROKE_HIGHLIGHT_WIDTH); + + public static final int STAMP_BUTTON_SIZE = 75; + +} diff --git a/src/main/java/de/ph87/kindermalen/ClickListener.java b/src/main/java/de/ph87/kindermalen/ClickListener.java deleted file mode 100644 index d579c28..0000000 --- a/src/main/java/de/ph87/kindermalen/ClickListener.java +++ /dev/null @@ -1,47 +0,0 @@ -package de.ph87.kindermalen; - -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.util.function.Consumer; - -public class ClickListener implements MouseListener { - - private final Consumer onPress; - - private final Consumer onRelease; - - public ClickListener(final Consumer onPress, final Consumer onRelease) { - this.onPress = onPress; - this.onRelease = onRelease; - } - - @Override - public void mouseClicked(final MouseEvent e) { - - } - - @Override - public void mousePressed(final MouseEvent e) { - if (onPress != null) { - onPress.accept(e); - } - } - - @Override - public void mouseReleased(final MouseEvent e) { - if (onRelease != null) { - onRelease.accept(e); - } - } - - @Override - public void mouseEntered(final MouseEvent e) { - - } - - @Override - public void mouseExited(final MouseEvent e) { - - } - -} diff --git a/src/main/java/de/ph87/kindermalen/KeyListener.java b/src/main/java/de/ph87/kindermalen/KeyListener.java deleted file mode 100644 index 98f77a8..0000000 --- a/src/main/java/de/ph87/kindermalen/KeyListener.java +++ /dev/null @@ -1,40 +0,0 @@ -package de.ph87.kindermalen; - -import java.awt.event.KeyEvent; -import java.util.function.Consumer; - -public class KeyListener implements java.awt.event.KeyListener { - - private final boolean ctrl; - - private final boolean shift; - - private final int keycode; - - private final Consumer next; - - public KeyListener(final boolean ctrl, final boolean shift, final int keycode, final Consumer next) { - this.ctrl = ctrl; - this.shift = shift; - this.keycode = keycode; - this.next = next; - } - - @Override - public void keyTyped(final KeyEvent e) { - - } - - @Override - public void keyPressed(final KeyEvent e) { - if (e.isControlDown() == ctrl && e.isShiftDown() == shift && e.getKeyCode() == keycode) { - next.accept(e); - } - } - - @Override - public void keyReleased(final KeyEvent e) { - - } - -} diff --git a/src/main/java/de/ph87/kindermalen/Main.java b/src/main/java/de/ph87/kindermalen/Main.java deleted file mode 100644 index d0e8e4c..0000000 --- a/src/main/java/de/ph87/kindermalen/Main.java +++ /dev/null @@ -1,117 +0,0 @@ -package de.ph87.kindermalen; - -import de.ph87.kindermalen.drawing.Drawing; -import de.ph87.kindermalen.drawing.DrawingPanel; -import de.ph87.kindermalen.tool.Tool; -import de.ph87.kindermalen.tool.ToolPanel; -import de.ph87.kindermalen.tool.stamp.StampTool; -import de.ph87.kindermalen.tool.stamp.StampToolPanel; -import de.ph87.kindermalen.toolbox.ToolBox; -import de.ph87.kindermalen.toolbox.ToolBoxPanel; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.KeyEvent; - -public class Main extends JFrame { - - public static final int SCREEN = 1; - - private static final double SIDEBAR_RATIO = 0.15; - - public static final double TOOLBOX_RATIO = 0; - - public static final double PROPERTIES_RATIO = 2; - - private final ToolBox toolBox = new ToolBox(); - - private final Drawing drawing = new Drawing(1500, 1000); - - private final DrawingPanel drawingPanel = new DrawingPanel(toolBox, drawing); - - private final ToolBoxPanel toolBoxPanel = new ToolBoxPanel(toolBox); - - private ToolPanel toolPanel = null; - - public static void main(String[] args) { - final Main main = new Main(); - main.setVisible(true); - } - - public Main() { - setLayout(new GridBagLayout()); - final GridBagConstraints c = new GridBagConstraints(); - - c.gridx = 0; - c.gridy = 0; - c.weightx = SIDEBAR_RATIO; - c.weighty = TOOLBOX_RATIO; - c.fill = GridBagConstraints.BOTH; - c.anchor = GridBagConstraints.FIRST_LINE_START; - add(toolBoxPanel, c); - - c.gridx = 1; - c.gridy = 0; - c.weightx = 1 - SIDEBAR_RATIO; - c.weighty = 1; - c.fill = GridBagConstraints.BOTH; - c.gridheight = 3; - add(drawingPanel, c); - - pack(); - - setFullscreen(); - setDefaultCloseOperation(EXIT_ON_CLOSE); - - addKeyListener(new KeyListener(true, false, KeyEvent.VK_Z, this::undo)); - addKeyListener(new KeyListener(true, true, KeyEvent.VK_Z, this::redo)); - - toolBox.onToolChange(this::onToolChanged); - } - - private void undo(final KeyEvent keyEvent) { - drawing.getCurrent().undo(); - drawingPanel.repaint(); - } - - private void redo(final KeyEvent keyEvent) { - drawing.getCurrent().redo(); - drawingPanel.repaint(); - } - - private void onToolChanged(final Tool tool) { - if (toolPanel != null) { - remove(toolPanel); - toolPanel = null; - } - - if (tool instanceof StampTool) { - toolPanel = new StampToolPanel((StampTool) tool); - } - - if (toolPanel != null) { - final GridBagConstraints c = new GridBagConstraints(); - c.gridx = 0; - c.gridy = 1; - c.weightx = SIDEBAR_RATIO; - c.weighty = PROPERTIES_RATIO; - c.fill = GridBagConstraints.BOTH; - c.anchor = GridBagConstraints.FIRST_LINE_START; - add(toolPanel, c); - pack(); - } - } - - private void setFullscreen() { - final GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment(); - final GraphicsDevice[] graphicsDevices = graphicsEnvironment.getScreenDevices(); - if (SCREEN < graphicsDevices.length) { - graphicsDevices[SCREEN].setFullScreenWindow(this); - } else if (graphicsDevices.length > 0) { - graphicsDevices[0].setFullScreenWindow(this); - } else { - throw new RuntimeException("No Screens Found"); - } - } - -} diff --git a/src/main/java/de/ph87/kindermalen/MotionListener.java b/src/main/java/de/ph87/kindermalen/MotionListener.java deleted file mode 100644 index dc03533..0000000 --- a/src/main/java/de/ph87/kindermalen/MotionListener.java +++ /dev/null @@ -1,32 +0,0 @@ -package de.ph87.kindermalen; - -import java.awt.event.MouseEvent; -import java.awt.event.MouseMotionListener; -import java.util.function.Consumer; - -public class MotionListener implements MouseMotionListener { - - private final Consumer onMove; - - private final Consumer onDrag; - - public MotionListener(final Consumer onMove, final Consumer onDrag) { - this.onMove = onMove; - this.onDrag = onDrag; - } - - @Override - public void mouseDragged(final MouseEvent e) { - if (onDrag != null) { - onDrag.accept(e); - } - } - - @Override - public void mouseMoved(final MouseEvent e) { - if (onMove != null) { - onMove.accept(e); - } - } - -} diff --git a/src/main/java/de/ph87/kindermalen/Sidebar.java b/src/main/java/de/ph87/kindermalen/Sidebar.java new file mode 100644 index 0000000..59226bf --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/Sidebar.java @@ -0,0 +1,53 @@ +package de.ph87.kindermalen; + +import de.ph87.kindermalen.tools.Tools; +import de.ph87.kindermalen.tools.ToolsPanel; +import de.ph87.kindermalen.tools.tool.Tool; +import de.ph87.kindermalen.tools.tool.ToolPanel; +import de.ph87.kindermalen.tools.tool.stamp.StampPanel; +import de.ph87.kindermalen.tools.tool.stamp.StampTool; + +import javax.swing.*; +import java.awt.*; + +import static de.ph87.kindermalen.CONFIG.BORDERS; +import static de.ph87.kindermalen.util.MyGridBagConstraints.C; + +public class Sidebar extends JPanel { + + private ToolPanel toolPanel = null; + + public Sidebar(final Tools tools) { + setPreferredSize(new Dimension(300, 0)); + setLayout(new GridBagLayout()); + add(new ToolsPanel(tools), C(0, 0, 1, 0.1)); + tools.onToolSelected(this::onToolSelected); + } + + @Override + public void paint(final Graphics g) { + super.paint(g); + if (BORDERS) { + g.setColor(Color.red); + g.drawRect(0, 0, getWidth() - 1, getHeight() - 1); + } + } + + private void onToolSelected(final Tool tool) { + if (toolPanel != null) { + remove(toolPanel); + toolPanel = null; + } + + if (tool instanceof StampTool) { + toolPanel = new StampPanel((StampTool) tool); + } + + if (toolPanel != null) { + add(toolPanel, C(0, 1, 1, 0.9)); + } + + repaint(); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/Window.java b/src/main/java/de/ph87/kindermalen/Window.java new file mode 100644 index 0000000..084b857 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/Window.java @@ -0,0 +1,73 @@ +package de.ph87.kindermalen; + +import de.ph87.kindermalen.drawing.Drawing; +import de.ph87.kindermalen.drawing.DrawingPanel; +import de.ph87.kindermalen.tools.Tools; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import static de.ph87.kindermalen.util.MyGridBagConstraints.C; + +public class Window extends JFrame { + + public static final int SCREEN = 1; + + private final Drawing drawing = new Drawing(1920, 1080); + + public static void main(String[] args) { + final Tools tools = new Tools(); + final Window window = new Window(tools); + window.setVisible(true); + } + + public Window(final Tools tools) { + setLayout(new GridBagLayout()); + + final Sidebar sidebar = new Sidebar(tools); + add(sidebar, C(0, 0, 0, 1.0)); + + final DrawingPanel drawingPanel = new DrawingPanel(tools, drawing); + add(drawingPanel, C(1, 0, 1, 1.0)); + + pack(); + + setFullscreen(); + setDefaultCloseOperation(EXIT_ON_CLOSE); + + KeyboardFocusManager.getCurrentKeyboardFocusManager() + .addKeyEventDispatcher( + keyEvent -> { + if (keyEvent.getID() == KeyEvent.KEY_PRESSED && keyEvent.getKeyCode() == KeyEvent.VK_Z) { + if (keyEvent.getModifiersEx() == InputEvent.CTRL_DOWN_MASK) { + drawing.undo(); + return true; + } + if (keyEvent.getModifiersEx() == InputEvent.SHIFT_DOWN_MASK + InputEvent.CTRL_DOWN_MASK) { + drawing.redo(); + return true; + } + } + return false; + } + ); + } + + private void setFullscreen() { + final GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment(); + final GraphicsDevice[] graphicsDevices = graphicsEnvironment.getScreenDevices(); + final GraphicsDevice graphicsDevice; + if (SCREEN < graphicsDevices.length) { + graphicsDevice = graphicsDevices[SCREEN]; + } else if (graphicsDevices.length > 0) { + graphicsDevice = graphicsDevices[0]; + } else { + throw new RuntimeException("No Screens Found"); + } + graphicsDevice.setFullScreenWindow(this); +// setPreferredSize(new Dimension(graphicsDevice.getDisplayMode().getWidth(),graphicsDevice.getDisplayMode().getHeight())); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/drawing/Drawing.java b/src/main/java/de/ph87/kindermalen/drawing/Drawing.java index cd0ac36..6edc99b 100644 --- a/src/main/java/de/ph87/kindermalen/drawing/Drawing.java +++ b/src/main/java/de/ph87/kindermalen/drawing/Drawing.java @@ -1,20 +1,74 @@ package de.ph87.kindermalen.drawing; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; -@Getter +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@Slf4j public class Drawing { + @Getter private final int width; + @Getter private final int height; private final Layer current; + private final ZonedDateTime created = ZonedDateTime.now(); + public Drawing(final int width, final int height) { this.width = width; this.height = height; current = new Layer(width, height); } + public Graphics2D getGraphics() { + return (Graphics2D) current.getCurrent().getGraphics(); + } + + public void publish() { + current.publish(); + } + + public void undo() { + current.undo(); + } + + public void redo() { + current.redo(); + } + + public void newRevision() { + current.newRevision(); + } + + public void save(final File dir) throws IOException { + final int revision = current.getRevision(); + final BufferedImage image = current.getCurrent(); + final File subdir = new File(dir, created.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); + final File file = new File(subdir, "%05d".formatted(revision) + ".png"); + if (subdir.mkdirs()) { + log.debug("Directory created: {}", subdir); + } + new Thread(() -> { + try { + ImageIO.write(image, "PNG", file); + } catch (IOException e) { + log.error(e.toString()); + } + }).start(); + } + + public Image getImage() { + return current.getCurrent(); + } + } diff --git a/src/main/java/de/ph87/kindermalen/drawing/DrawingPanel.java b/src/main/java/de/ph87/kindermalen/drawing/DrawingPanel.java index b7b8e66..96c7f4e 100644 --- a/src/main/java/de/ph87/kindermalen/drawing/DrawingPanel.java +++ b/src/main/java/de/ph87/kindermalen/drawing/DrawingPanel.java @@ -1,85 +1,168 @@ package de.ph87.kindermalen.drawing; -import de.ph87.kindermalen.ClickListener; -import de.ph87.kindermalen.MotionListener; -import de.ph87.kindermalen.toolbox.ToolBox; +import de.ph87.kindermalen.tools.Tools; +import de.ph87.kindermalen.tools.tool.Tool; +import de.ph87.kindermalen.util.MouseListener; +import de.ph87.kindermalen.util.Subscription; +import de.ph87.kindermalen.util.Vector; import lombok.extern.slf4j.Slf4j; -import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; @Slf4j public class DrawingPanel extends JPanel { private static final File SAVE_DIR = new File("./data/images"); - private final ToolBox toolBox; + private final Tools tools; private final Drawing drawing; + private Subscription toolSubscription = null; + private Vector lastPoint = null; - private Point cursor = null; + private Vector cursor = null; - private final ZonedDateTime startup = ZonedDateTime.now(); + private AffineTransform transform; - public DrawingPanel(final ToolBox toolBox, final Drawing drawing) { - this.toolBox = toolBox; + private AffineTransform inverseTransform; + + public DrawingPanel(final Tools tools, final Drawing drawing) { + this.tools = tools; this.drawing = drawing; - this.addMouseListener(new ClickListener(this::onPress, this::onRelease)); - this.addMouseMotionListener(new MotionListener(this::onMove, this::onDrag)); + MouseListener.onPress(this, this::onPress); + MouseListener.onRelease(this, this::onRelease); + MouseListener.onMove(this, this::onMove); + MouseListener.onDrag(this, this::onDrag); + tools.onToolSelected(tool -> { + repaint(); + if (toolSubscription != null) { + toolSubscription.unsubscribe(); + } + toolSubscription = tool.onChange(ignore -> repaint()); + }); + + addComponentListener(new ComponentListener() { + @Override + public void componentResized(final ComponentEvent e) { + calculateTransform(); + } + + @Override + public void componentMoved(final ComponentEvent e) { + calculateTransform(); + } + + @Override + public void componentShown(final ComponentEvent e) { + } + + @Override + public void componentHidden(final ComponentEvent e) { + } + }); } - private void onMove(final MouseEvent e) { - cursor = e.getPoint(); - repaint(); - } - - private void onPress(final MouseEvent e) { - drawing.getCurrent().newRevision(); - lastPoint = toolBox.getTool().apply(null, drawing.getCurrent().getCurrent(), new Vector(e.getPoint())); - repaint(); - } - - private void onDrag(final MouseEvent e) { - cursor = e.getPoint(); - lastPoint = toolBox.getTool().apply(lastPoint, drawing.getCurrent().getCurrent(), new Vector(e.getPoint())); - repaint(); - } - - private void onRelease(final MouseEvent e) { - SAVE_DIR.mkdirs(); + private void calculateTransform() { + final Image image = drawing.getImage(); + final Rectangle box = fitInside(image.getWidth(null), image.getHeight(null), getWidth(), getHeight(), false); + final double scale = (double) box.width / image.getWidth(null); + transform = new AffineTransform(); + transform.translate(box.x, box.y); + transform.scale(scale, scale); try { - ImageIO.write(drawing.getCurrent().getCurrent(), "PNG", new File(SAVE_DIR, startup.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + ".png")); + inverseTransform = transform.createInverse(); + } catch (NoninvertibleTransformException ex) { + log.error(ex.toString()); + } + System.out.printf("%.0f%%\n", 100.0 * scale); + } + + private void onMove(final MouseEvent mouseEvent) { + cursor = transform(mouseEvent.getPoint()); + repaint(); + } + + private void onPress(final MouseEvent mouseEvent) { + if (mouseEvent.getButton() == 3) { + drawing.undo(); + return; + } + drawing.newRevision(); + lastPoint = tools.getTool().apply(null, drawing, transform(mouseEvent.getPoint())); + repaint(); + } + + private void onDrag(final MouseEvent mouseEvent) { + cursor = transform(mouseEvent.getPoint()); + lastPoint = tools.getTool().apply(lastPoint, drawing, transform(mouseEvent.getPoint())); + repaint(); + } + + private Vector transform(final Point point) { + final Point2D transformed = inverseTransform.transform(new Point2D.Double(point.x, point.y), null); + return new Vector(transformed); + } + + private void onRelease(final MouseEvent mouseEvent) { + try { + drawing.save(SAVE_DIR); } catch (IOException ex) { log.error(ex.toString()); } } @Override - public void paint(final Graphics g) { + public void paint(final Graphics graphics) { + final Graphics2D g = (Graphics2D) graphics; g.setColor(Color.gray); g.fillRect(0, 0, getWidth(), getHeight()); + g.setTransform(transform); if (drawing == null) { return; } - g.setColor(Color.white); - g.fillRect(0, 0, drawing.getWidth(), drawing.getHeight()); - g.drawImage(drawing.getCurrent().getCurrent(), 0, 0, null); + final Image image = drawing.getImage(); - final BufferedImage preview = toolBox.getTool().getPreview(); + g.setColor(Color.white); + g.fillRect(0, 0, image.getWidth(null), image.getHeight(null)); + g.drawImage(image, 0, 0, image.getWidth(null), image.getHeight(null), null); + + final BufferedImage preview = tools.getTool().getPreview(); if (cursor != null && preview != null) { - g.drawImage(preview, cursor.x - preview.getWidth() / 2, cursor.y - preview.getHeight() / 2, null); + g.drawImage(preview, cursor.intX() - preview.getWidth() / 2, cursor.intY() - preview.getHeight() / 2, null); } } + private Rectangle fitInside(int w, int h, final int width, final int height, final boolean grow) { + final double r = (double) w / h; + if (grow && h < height) { + h = height; + w = (int) Math.round(h * r); + } + if (w > width || (grow && w < width)) { + w = width; + h = (int) Math.round(w / r); + } + if (h > height) { + h = height; + w = (int) Math.round(h * r); + } + final int x = (int) Math.round((width - w) / 2.0); + final int y = (int) Math.round((height - h) / 2.0); + return new Rectangle(x, y, w, h); + } + } diff --git a/src/main/java/de/ph87/kindermalen/drawing/Layer.java b/src/main/java/de/ph87/kindermalen/drawing/Layer.java index f370f27..ed29e4d 100644 --- a/src/main/java/de/ph87/kindermalen/drawing/Layer.java +++ b/src/main/java/de/ph87/kindermalen/drawing/Layer.java @@ -1,11 +1,16 @@ package de.ph87.kindermalen.drawing; +import de.ph87.kindermalen.util.Publisher; +import de.ph87.kindermalen.util.Subscription; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; + +import static de.ph87.kindermalen.util.ImageHelper.COPY; @Slf4j public class Layer { @@ -19,14 +24,19 @@ public class Layer { private List history = new ArrayList<>(); - private int index; + @Getter + private int index = 0; + + @Getter + private int revision = 0; + + private final Publisher onChange = new Publisher<>(); public Layer(final int width, final int height) { this.width = width; this.height = height; this.current = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); this.history.add(current); - this.index = 0; } public void newRevision() { @@ -36,23 +46,19 @@ public class Layer { history = history.subList(0, keep); log.info("{} Revisions deleted", old - keep); } - current = copy(); + current = COPY(current); index = history.size(); + revision++; history.add(current); log.info("Revision {} created", index); } - private BufferedImage copy() { - final BufferedImage created = new BufferedImage(current.getWidth(), current.getHeight(), current.getType()); - current.copyData(created.getRaster()); - return created; - } - public void undo() { if (index > 0) { index--; current = history.get(index); log.info("UNDO: Revision {} loaded", index); + publish(); } else { log.warn("No UNDO steps left."); } @@ -63,6 +69,7 @@ public class Layer { index++; current = history.get(index); log.info("REDO: Revision {} loaded", index); + publish(); } else { log.warn("No REDO steps left."); } @@ -72,4 +79,12 @@ public class Layer { return index < history.size() - 1; } + public void publish() { + onChange.publish(current); + } + + public Subscription onChange(final Consumer next) { + return onChange.subscribe(next); + } + } diff --git a/src/main/java/de/ph87/kindermalen/tool/Tool.java b/src/main/java/de/ph87/kindermalen/tool/Tool.java deleted file mode 100644 index a2127ae..0000000 --- a/src/main/java/de/ph87/kindermalen/tool/Tool.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.ph87.kindermalen.tool; - -import de.ph87.kindermalen.drawing.Vector; -import lombok.Getter; - -import java.awt.image.BufferedImage; - -public abstract class Tool { - - @Getter - private final String name; - - protected Tool(final String name) { - this.name = name; - } - - public abstract Vector apply(final Vector last, final BufferedImage image, final Vector point); - - public abstract BufferedImage getPreview(); - -} diff --git a/src/main/java/de/ph87/kindermalen/tool/ToolPanel.java b/src/main/java/de/ph87/kindermalen/tool/ToolPanel.java deleted file mode 100644 index e178360..0000000 --- a/src/main/java/de/ph87/kindermalen/tool/ToolPanel.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.ph87.kindermalen.tool; - -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import java.awt.*; - -@Slf4j -public abstract class ToolPanel extends JPanel { - - protected final T tool; - - protected ToolPanel(final T tool) { - this.tool = tool; - setPreferredSize(new Dimension(100, 100)); - } - -} diff --git a/src/main/java/de/ph87/kindermalen/tool/stamp/StampTool.java b/src/main/java/de/ph87/kindermalen/tool/stamp/StampTool.java deleted file mode 100644 index a3e3172..0000000 --- a/src/main/java/de/ph87/kindermalen/tool/stamp/StampTool.java +++ /dev/null @@ -1,118 +0,0 @@ -package de.ph87.kindermalen.tool.stamp; - -import de.ph87.kindermalen.drawing.Vector; -import de.ph87.kindermalen.tool.Tool; -import lombok.Getter; -import lombok.ToString; -import lombok.extern.slf4j.Slf4j; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import static de.ph87.kindermalen.ImageHelper.*; -import static de.ph87.kindermalen.tool.stamp.ListHelper.SYNC; - -@Slf4j -@ToString -public class StampTool extends Tool { - - private static final File STAMPS_DIR = new File("./data/stamps"); - - @Getter - private List stamps = new ArrayList<>(); - - @Getter - private Stamp stamp; - - @Getter - private int size = 100; - - @Getter - private double alpha = 0.75; - - private BufferedImage prepared; - - @Getter - private BufferedImage preview; - - public StampTool() { - super("Stempel"); - final List files = scan(STAMPS_DIR); - stamps = SYNC(files, stamps, Stamp::getFile, Stamp::new, Stamp::getName); - stamps.stream().filter(stamp -> stamp.getOriginal() == null).forEach(this::load); - } - - private List scan(final File dir) { - final List files = new ArrayList<>(); - for (final File child : Objects.requireNonNull(dir.listFiles())) { - if (child.isFile()) { - files.add(child.getAbsoluteFile()); - } else if (child.isDirectory()) { - files.addAll(scan(child)); - } - } - return files; - } - - private void load(final Stamp stamp) { - try { - stamp.load(); - if (this.stamp == stamp) { - prepare(); - } - } catch (IOException e) { - log.error(e.toString()); - } - } - - public void setStamp(final Stamp stamp) { - this.stamp = stamp; - log.info("Stamp chosen: {}", stamp.getName()); - prepare(); - } - - public void setSize(final int size) { - this.size = size; - prepare(); - } - - public void setAlpha(final double alpha) { - this.alpha = alpha; - prepare(); - } - - private void prepare() { - if (this.stamp == null || stamp.getOriginal() == null) { - return; - } - prepared = RESIZE(stamp.getOriginal(), size, size); - preview = COPY(prepared); - ALPHA(preview, 0.5); - ALPHA(prepared, alpha); - log.info("Stamp prepared: {}", stamp.getName()); - } - - @Override - public Vector apply(Vector last, final BufferedImage destination, final Vector current) { - if (last == null) { - apply(destination, current); - return current; - } - final Vector vector = new Vector(last, current); - for (int rest = (int) Math.floor(vector.length); rest >= 20; rest -= 20) { - final Vector point = last.plus(vector.withLength(20)); - apply(destination, point); - last = point; - } - return last; - } - - private void apply(final BufferedImage destination, final Vector point) { - destination.getGraphics().drawImage(prepared, point.intX() - prepared.getWidth() / 2, point.intY() - prepared.getHeight() / 2, null); - } - -} diff --git a/src/main/java/de/ph87/kindermalen/tool/stamp/StampToolPanel.java b/src/main/java/de/ph87/kindermalen/tool/stamp/StampToolPanel.java deleted file mode 100644 index 669a93f..0000000 --- a/src/main/java/de/ph87/kindermalen/tool/stamp/StampToolPanel.java +++ /dev/null @@ -1,67 +0,0 @@ -package de.ph87.kindermalen.tool.stamp; - -import de.ph87.kindermalen.ClickListener; -import de.ph87.kindermalen.tool.ToolPanel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import java.awt.*; -import java.awt.image.BufferedImage; - -@Slf4j -public class StampToolPanel extends ToolPanel { - - private static final BasicStroke STROKE_BORDER = new BasicStroke(1f); - - private static final int STROKE_HIGHLIGHT_WIDTH = 4; - - private static final BasicStroke STROKE_HIGHLIGHT = new BasicStroke(STROKE_HIGHLIGHT_WIDTH); - - private static final int SIZE = 50; - - public StampToolPanel(final StampTool tool) { - super(tool); - setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0)); - tool.getStamps().forEach(stamp -> add(new StampPanel(stamp))); - } - - private class StampPanel extends JPanel { - - @Getter - private final Stamp stamp; - - private final BufferedImage icon; - - private final Point position; - - public StampPanel(final Stamp stamp) { - this.stamp = stamp; - setPreferredSize(new Dimension(SIZE, SIZE)); - addMouseListener(new ClickListener(null, e -> tool.setStamp(stamp))); - icon = stamp.getSize(SIZE - STROKE_HIGHLIGHT_WIDTH * 2); - position = new Point((SIZE - icon.getWidth()) / 2, (SIZE - icon.getHeight()) / 2); - } - - @Override - public void paint(final Graphics g) { - super.paint(g); - - final Graphics2D g2 = (Graphics2D) g; - - if (tool.getStamp() == stamp) { - g2.setColor(Color.orange); - g2.setStroke(STROKE_HIGHLIGHT); - g2.fillRect(0, 0, getWidth(), getHeight()); - } else { - g2.setColor(Color.white); - g2.setStroke(STROKE_BORDER); - } - - g2.drawRect(0, 0, SIZE - 1, SIZE - 1); - g2.drawImage(icon, position.x, position.y, null); - } - - } - -} diff --git a/src/main/java/de/ph87/kindermalen/toolbox/Observer.java b/src/main/java/de/ph87/kindermalen/toolbox/Observer.java deleted file mode 100644 index de53eba..0000000 --- a/src/main/java/de/ph87/kindermalen/toolbox/Observer.java +++ /dev/null @@ -1,7 +0,0 @@ -package de.ph87.kindermalen.toolbox; - -public interface Observer { - - void next(final T item); - -} diff --git a/src/main/java/de/ph87/kindermalen/toolbox/ToolBox.java b/src/main/java/de/ph87/kindermalen/toolbox/ToolBox.java deleted file mode 100644 index 457214d..0000000 --- a/src/main/java/de/ph87/kindermalen/toolbox/ToolBox.java +++ /dev/null @@ -1,38 +0,0 @@ -package de.ph87.kindermalen.toolbox; - -import de.ph87.kindermalen.Publisher; -import de.ph87.kindermalen.Subscription; -import de.ph87.kindermalen.tool.Tool; -import de.ph87.kindermalen.tool.stamp.StampTool; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import java.util.List; -import java.util.function.Consumer; - -@Slf4j -public class ToolBox { - - @Getter - private final List tools = List.of(new StampTool()); - - @Getter - private Tool tool; - - private final Publisher onToolChange = new Publisher<>(); - - public ToolBox() { - setTool(tools.get(0)); - } - - public void setTool(final Tool tool) { - this.tool = tool; - log.info("Tool selected: {}", tool.getName()); - onToolChange.publish(tool); - } - - public Subscription onToolChange(final Consumer next) { - return onToolChange.subscribe(next); - } - -} diff --git a/src/main/java/de/ph87/kindermalen/toolbox/ToolBoxPanel.java b/src/main/java/de/ph87/kindermalen/toolbox/ToolBoxPanel.java deleted file mode 100644 index 40457c8..0000000 --- a/src/main/java/de/ph87/kindermalen/toolbox/ToolBoxPanel.java +++ /dev/null @@ -1,56 +0,0 @@ -package de.ph87.kindermalen.toolbox; - -import de.ph87.kindermalen.ClickListener; -import de.ph87.kindermalen.tool.Tool; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import java.awt.*; - -@Slf4j -public class ToolBoxPanel extends JPanel { - - private final ToolBox toolbox; - - public ToolBoxPanel(final ToolBox toolbox) { - this.toolbox = toolbox; - setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0)); - for (final Tool tool : toolbox.getTools()) { - add(new ToolButton(tool)); - } - } - - @Override - public void paint(final Graphics g) { - super.paint(g); -// final Graphics2D g2 = (Graphics2D) g; -// g2.setColor(Color.red); -// g2.setStroke(new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, new float[]{2f}, 0)); -// g2.drawRect(0, 0, getWidth() - 1, getHeight() - 1); - } - - private class ToolButton extends Component { - - private final Tool tool; - - public ToolButton(final Tool tool) { - this.tool = tool; - setPreferredSize(new Dimension(50, 50)); - addMouseListener(new ClickListener(null, e -> toolbox.setTool(tool))); - } - - @Override - public void paint(final Graphics g) { - g.setColor(Color.magenta); - g.fillRect(0, 0, getWidth(), getHeight()); - - g.setColor(Color.black); - g.setFont(g.getFont().deriveFont(12.0f)); - g.drawString(tool.getName(), 0, g.getFontMetrics().getHeight()); - - g.drawRect(0, 0, getWidth() - 1, getHeight() - 1); - } - - } - -} diff --git a/src/main/java/de/ph87/kindermalen/tools/ToolButton.java b/src/main/java/de/ph87/kindermalen/tools/ToolButton.java new file mode 100644 index 0000000..be3896f --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tools/ToolButton.java @@ -0,0 +1,32 @@ +package de.ph87.kindermalen.tools; + +import de.ph87.kindermalen.tools.tool.Tool; +import de.ph87.kindermalen.util.MouseListener; + +import java.awt.*; + +import static de.ph87.kindermalen.CONFIG.STAMP_BUTTON_SIZE; + +public class ToolButton extends Component { + + private final Tool tool; + + public ToolButton(final Tools tools, final Tool tool) { + this.tool = tool; + setPreferredSize(new Dimension(STAMP_BUTTON_SIZE, STAMP_BUTTON_SIZE)); + MouseListener.onRelease(this, e -> tools.setTool(tool)); + } + + @Override + public void paint(final Graphics g) { + g.setColor(Color.magenta); + g.fillRect(0, 0, getWidth(), getHeight()); + + g.setColor(Color.black); + g.setFont(g.getFont().deriveFont(12.0f)); + g.drawString(tool.getName(), 0, g.getFontMetrics().getHeight()); + + g.drawRect(0, 0, getWidth() - 1, getHeight() - 1); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tools/Tools.java b/src/main/java/de/ph87/kindermalen/tools/Tools.java new file mode 100644 index 0000000..552f82e --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tools/Tools.java @@ -0,0 +1,38 @@ +package de.ph87.kindermalen.tools; + +import de.ph87.kindermalen.tools.tool.Tool; +import de.ph87.kindermalen.tools.tool.stamp.StampTool; +import de.ph87.kindermalen.util.Publisher; +import de.ph87.kindermalen.util.Subscription; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.function.Consumer; + +@Slf4j +public class Tools { + + @Getter + private final List tools = List.of(new StampTool()); + + @Getter + private Tool tool; + + private final Publisher onToolSelected = new Publisher<>(); + + public Tools() { + setTool(tools.get(0)); + } + + public void setTool(final Tool tool) { + this.tool = tool; + log.info("Tool selected: {}", tool.getName()); + onToolSelected.publish(tool); + } + + public Subscription onToolSelected(final Consumer next) { + return onToolSelected.subscribe(next); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tools/ToolsPanel.java b/src/main/java/de/ph87/kindermalen/tools/ToolsPanel.java new file mode 100644 index 0000000..84bef8c --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tools/ToolsPanel.java @@ -0,0 +1,30 @@ +package de.ph87.kindermalen.tools; + +import de.ph87.kindermalen.tools.tool.Tool; +import lombok.extern.slf4j.Slf4j; + +import javax.swing.*; +import java.awt.*; + +import static de.ph87.kindermalen.CONFIG.BORDERS; + +@Slf4j +public class ToolsPanel extends JPanel { + + public ToolsPanel(final Tools tools) { + setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0)); + for (final Tool tool : tools.getTools()) { + add(new ToolButton(tools, tool)); + } + } + + @Override + public void paint(final Graphics g) { + super.paint(g); + if (BORDERS) { + g.setColor(Color.magenta); + g.drawRect(0, 0, getWidth() - 1, getHeight() - 1); + } + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tools/tool/Tool.java b/src/main/java/de/ph87/kindermalen/tools/tool/Tool.java new file mode 100644 index 0000000..aa8e49d --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tools/tool/Tool.java @@ -0,0 +1,31 @@ +package de.ph87.kindermalen.tools.tool; + +import de.ph87.kindermalen.drawing.Drawing; +import de.ph87.kindermalen.util.Publisher; +import de.ph87.kindermalen.util.Subscription; +import de.ph87.kindermalen.util.Vector; +import lombok.Getter; + +import java.awt.image.BufferedImage; +import java.util.function.Consumer; + +public abstract class Tool { + + protected final Publisher onChange = new Publisher<>(); + + @Getter + private final String name; + + protected Tool(final String name) { + this.name = name; + } + + public abstract Vector apply(final Vector last, final Drawing drawing, final Vector point); + + public abstract BufferedImage getPreview(); + + public Subscription onChange(final Consumer next) { + return onChange.subscribe(next); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tools/tool/ToolPanel.java b/src/main/java/de/ph87/kindermalen/tools/tool/ToolPanel.java new file mode 100644 index 0000000..2e1d1b0 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tools/tool/ToolPanel.java @@ -0,0 +1,10 @@ +package de.ph87.kindermalen.tools.tool; + +import lombok.extern.slf4j.Slf4j; + +import javax.swing.*; + +@Slf4j +public abstract class ToolPanel extends JPanel { + +} diff --git a/src/main/java/de/ph87/kindermalen/tool/stamp/Stamp.java b/src/main/java/de/ph87/kindermalen/tools/tool/stamp/Stamp.java similarity index 73% rename from src/main/java/de/ph87/kindermalen/tool/stamp/Stamp.java rename to src/main/java/de/ph87/kindermalen/tools/tool/stamp/Stamp.java index 1f863e5..ffa815e 100644 --- a/src/main/java/de/ph87/kindermalen/tool/stamp/Stamp.java +++ b/src/main/java/de/ph87/kindermalen/tools/tool/stamp/Stamp.java @@ -1,4 +1,4 @@ -package de.ph87.kindermalen.tool.stamp; +package de.ph87.kindermalen.tools.tool.stamp; import lombok.Getter; import lombok.ToString; @@ -11,7 +11,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -import static de.ph87.kindermalen.ImageHelper.RESIZE; +import static de.ph87.kindermalen.util.ImageHelper.RESIZE; @Slf4j @Getter @@ -22,17 +22,14 @@ public class Stamp { private final File file; - private BufferedImage original = null; + private final BufferedImage original; private final Map sizes = new HashMap<>(); - public Stamp(final File file) { + public Stamp(final File file) throws IOException { this.name = file.getName().substring(0, file.getName().lastIndexOf(".")); this.file = file; - } - - public void load() throws IOException { - original = ImageIO.read(file); + this.original = ImageIO.read(file); log.info("Stamp loaded: {}", this); } diff --git a/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampButton.java b/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampButton.java new file mode 100644 index 0000000..a873311 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampButton.java @@ -0,0 +1,80 @@ +package de.ph87.kindermalen.tools.tool.stamp; + +import de.ph87.kindermalen.util.MouseListener; +import lombok.Getter; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; + +import static de.ph87.kindermalen.CONFIG.*; + +public class StampButton extends JPanel { + + private final StampTool tool; + + @Getter + private final Stamp stamp; + + private final BufferedImage icon; + + private final Point position; + + private boolean hover = false; + + public StampButton(final StampTool tool, final Stamp stamp) { + this.tool = tool; + this.stamp = stamp; + setPreferredSize(new Dimension(STAMP_BUTTON_SIZE, STAMP_BUTTON_SIZE)); + MouseListener.onRelease(this, this::onRelease); + MouseListener.onEnter(this, this::onEnter); + MouseListener.onExit(this, this::onExit); + icon = stamp.getSize(STAMP_BUTTON_SIZE - STROKE_HIGHLIGHT_WIDTH * 2); + position = new Point((STAMP_BUTTON_SIZE - icon.getWidth()) / 2, (STAMP_BUTTON_SIZE - icon.getHeight()) / 2); + } + + private void onRelease(final MouseEvent mouseEvent) { + this.tool.setStamp(stamp); + this.tool.prepare(); + } + + private void onEnter(final MouseEvent mouseEvent) { + hover = true; + repaint(); + } + + private void onExit(final MouseEvent mouseEvent) { + hover = false; + repaint(); + } + + @Override + public void paint(final Graphics g) { + super.paint(g); + + final Graphics2D g2 = (Graphics2D) g; + + boolean highlight = false; + if (hover) { + g2.setColor(Color.yellow); + highlight = true; + } else if (tool.getStamp() == stamp) { + g2.setColor(Color.magenta); + highlight = true; + } else { + g2.setColor(Color.black); + } + + if (highlight) { + g2.setStroke(STROKE_HIGHLIGHT); + g2.fillRect(0, 0, getWidth(), getHeight()); + } else { + g2.setStroke(STROKE_BORDER); + } + + g2.drawImage(icon, position.x, position.y, null); + g2.drawRect(0, 0, STAMP_BUTTON_SIZE - 1, STAMP_BUTTON_SIZE - 1); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampListPanel.java b/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampListPanel.java new file mode 100644 index 0000000..a2a3262 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampListPanel.java @@ -0,0 +1,23 @@ +package de.ph87.kindermalen.tools.tool.stamp; + +import lombok.extern.slf4j.Slf4j; + +import javax.swing.*; +import java.awt.*; + +import static de.ph87.kindermalen.CONFIG.BORDERS; + +@Slf4j +public class StampListPanel extends JPanel { + + public StampListPanel(final StampTool tool) { + setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0)); + tool.getStamps().forEach(stamp -> add(new StampButton(tool, stamp))); + tool.onChange(ignore -> repaint()); + repaint(); + if (BORDERS) { + setBackground(Color.green.brighter().brighter()); + } + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampOptionsPanel.java b/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampOptionsPanel.java new file mode 100644 index 0000000..b0fafe7 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampOptionsPanel.java @@ -0,0 +1,62 @@ +package de.ph87.kindermalen.tools.tool.stamp; + +import javax.swing.*; +import java.awt.*; + +import static de.ph87.kindermalen.CONFIG.BORDERS; + +public class StampOptionsPanel extends JPanel { + + private final JSlider sizeSlider = new JSlider(10, 1000, 100); + + private final JSlider alphaSlider = new JSlider(1, 100, 100); + + private final JSlider stepSlider = new JSlider(1, 1000, 25); + + public StampOptionsPanel(final StampTool tool) { + setLayout(new GridLayout(6, 1)); + + final JLabel sizeLabel = new JLabel("Größe:"); + add(sizeLabel); + sizeSlider.setAutoscrolls(false); + sizeSlider.addChangeListener(e -> { + sizeLabel.setText("Größe: " + sizeSlider.getValue()); + tool.setSize(sizeSlider.getValue()); + tool.prepare(); + }); + add(sizeSlider); + + final JLabel alphaLabel = new JLabel("Transparenz:"); + add(alphaLabel); + alphaSlider.setAutoscrolls(false); + alphaSlider.addChangeListener(e -> { + alphaLabel.setText("Transparenz: " + alphaSlider.getValue()); + tool.setAlpha(alphaSlider.getValue() / 100.0); + tool.prepare(); + }); + add(alphaSlider); + + final JLabel stepsLabel = new JLabel("Schrittweite:"); + add(stepsLabel); + stepSlider.setAutoscrolls(false); + stepSlider.addChangeListener(e -> { + stepsLabel.setText("Schrittweite: " + stepSlider.getValue()); + tool.setStep(stepSlider.getValue()); + tool.prepare(); + }); + add(stepSlider); + + sizeLabel.setText("Größe: " + sizeSlider.getValue()); + tool.setSize(sizeSlider.getValue()); + alphaLabel.setText("Transparenz: " + alphaSlider.getValue()); + tool.setAlpha(alphaSlider.getValue() / 100.0); + stepsLabel.setText("Schrittweite: " + stepSlider.getValue()); + tool.setStep(stepSlider.getValue()); + tool.prepare(); + + if (BORDERS) { + setBackground(Color.blue.brighter().brighter()); + } + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampPanel.java b/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampPanel.java new file mode 100644 index 0000000..deaa1d8 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampPanel.java @@ -0,0 +1,20 @@ +package de.ph87.kindermalen.tools.tool.stamp; + +import de.ph87.kindermalen.tools.tool.ToolPanel; +import lombok.extern.slf4j.Slf4j; + +import java.awt.*; + +import static de.ph87.kindermalen.util.MyGridBagConstraints.C; +import static java.awt.GridBagConstraints.HORIZONTAL; + +@Slf4j +public class StampPanel extends ToolPanel { + + public StampPanel(final StampTool tool) { + setLayout(new GridBagLayout()); + add(new StampOptionsPanel(tool), C(0, 0, 1, 0, HORIZONTAL)); + add(new StampListPanel(tool), C(0, 1, 1, 1)); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampTool.java b/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampTool.java new file mode 100644 index 0000000..ffe4e8a --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tools/tool/stamp/StampTool.java @@ -0,0 +1,96 @@ +package de.ph87.kindermalen.tools.tool.stamp; + +import de.ph87.kindermalen.drawing.Drawing; +import de.ph87.kindermalen.tools.tool.Tool; +import de.ph87.kindermalen.util.Vector; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Objects; + +import static de.ph87.kindermalen.util.FileHelper.SCAN_FILES; +import static de.ph87.kindermalen.util.ImageHelper.ALPHA; +import static de.ph87.kindermalen.util.ImageHelper.RESIZE; + +@Slf4j +@Getter +@ToString +public class StampTool extends Tool { + + private static final File STAMPS_DIR = new File("./data/stamps"); + + private final List stamps; + + @Setter + private Stamp stamp = null; + + @Setter + private int size = 100; + + @Setter + private double alpha = 0.75; + + @Setter + private int step = 25; + + private BufferedImage prepared; + + public StampTool() { + super("Stempel"); + stamps = SCAN_FILES(STAMPS_DIR).stream().map(this::load).filter(Objects::nonNull).toList(); + if (stamp == null && !stamps.isEmpty()) { + setStamp(stamps.get(0)); + } + } + + private Stamp load(final File file) { + try { + return new Stamp(file); + } catch (IOException e) { + log.error(e.toString()); + return null; + } + } + + public void prepare() { + if (this.stamp == null || stamp.getOriginal() == null) { + return; + } + prepared = RESIZE(stamp.getOriginal(), size, size); + ALPHA(prepared, alpha); + log.info("Stamp prepared: {}", stamp.getName()); + onChange.publish(this); + } + + @Override + public Vector apply(Vector last, final Drawing drawing, final Vector current) { + if (last == null) { + apply(drawing, current); + return current; + } + final Vector vector = new Vector(last, current); + for (int rest = (int) Math.floor(vector.length); rest >= step; rest -= step) { + final Vector point = last.plus(vector.withLength(step)); + apply(drawing, point); + last = point; + } + return last; + } + + private void apply(final Drawing drawing, final Vector point) { + drawing.getGraphics().drawImage(prepared, point.intX() - prepared.getWidth() / 2, point.intY() - prepared.getHeight() / 2, null); + drawing.publish(); + } + + @Override + public BufferedImage getPreview() { + return prepared; + } + +} diff --git a/src/main/java/de/ph87/kindermalen/util/FileHelper.java b/src/main/java/de/ph87/kindermalen/util/FileHelper.java new file mode 100644 index 0000000..1115ae1 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/util/FileHelper.java @@ -0,0 +1,22 @@ +package de.ph87.kindermalen.util; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class FileHelper { + + public static List SCAN_FILES(final File dir) { + final List files = new ArrayList<>(); + for (final File child : Objects.requireNonNull(dir.listFiles())) { + if (child.isFile()) { + files.add(child.getAbsoluteFile()); + } else if (child.isDirectory()) { + files.addAll(SCAN_FILES(child)); + } + } + return files; + } + +} diff --git a/src/main/java/de/ph87/kindermalen/ImageHelper.java b/src/main/java/de/ph87/kindermalen/util/ImageHelper.java similarity index 97% rename from src/main/java/de/ph87/kindermalen/ImageHelper.java rename to src/main/java/de/ph87/kindermalen/util/ImageHelper.java index 8ea00a9..e31b781 100644 --- a/src/main/java/de/ph87/kindermalen/ImageHelper.java +++ b/src/main/java/de/ph87/kindermalen/util/ImageHelper.java @@ -1,4 +1,4 @@ -package de.ph87.kindermalen; +package de.ph87.kindermalen.util; import com.mortennobel.imagescaling.ResampleFilters; import com.mortennobel.imagescaling.ResampleOp; diff --git a/src/main/java/de/ph87/kindermalen/util/KeyListener.java b/src/main/java/de/ph87/kindermalen/util/KeyListener.java new file mode 100644 index 0000000..d891774 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/util/KeyListener.java @@ -0,0 +1,39 @@ +package de.ph87.kindermalen.util; + +import java.awt.*; +import java.awt.event.KeyEvent; +import java.util.function.Consumer; + +public class KeyListener { + + public static void onPress(final Component component, final boolean ctrl, final boolean shift, final int keycode, final Consumer onPress) { + component.addKeyListener(new KeyListenerImpl() { + @Override + public void keyPressed(final KeyEvent e) { + if (e.isControlDown() == ctrl && e.isShiftDown() == shift && e.getKeyCode() == keycode) { + onPress.accept(e); + } + } + }); + } + + private static class KeyListenerImpl implements java.awt.event.KeyListener { + + @Override + public void keyTyped(final KeyEvent e) { + + } + + @Override + public void keyPressed(final KeyEvent e) { + + } + + @Override + public void keyReleased(final KeyEvent e) { + + } + + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tool/stamp/ListHelper.java b/src/main/java/de/ph87/kindermalen/util/ListHelper.java similarity index 94% rename from src/main/java/de/ph87/kindermalen/tool/stamp/ListHelper.java rename to src/main/java/de/ph87/kindermalen/util/ListHelper.java index f89107a..d3eb0d4 100644 --- a/src/main/java/de/ph87/kindermalen/tool/stamp/ListHelper.java +++ b/src/main/java/de/ph87/kindermalen/util/ListHelper.java @@ -1,4 +1,4 @@ -package de.ph87.kindermalen.tool.stamp; +package de.ph87.kindermalen.util; import java.util.ArrayList; import java.util.Comparator; diff --git a/src/main/java/de/ph87/kindermalen/util/MouseListener.java b/src/main/java/de/ph87/kindermalen/util/MouseListener.java new file mode 100644 index 0000000..3e6f180 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/util/MouseListener.java @@ -0,0 +1,109 @@ +package de.ph87.kindermalen.util; + +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; +import java.util.function.Consumer; + +public class MouseListener { + + public static void onPress(final Component component, final Consumer onPress) { + component.addMouseListener(new MouseListenerImpl() { + @Override + public void mousePressed(final MouseEvent mouseEvent) { + onPress.accept(mouseEvent); + } + }); + } + + public static void onRelease(final Component component, final Consumer release) { + component.addMouseListener(new MouseListenerImpl() { + @Override + public void mouseReleased(final MouseEvent mouseEvent) { + if (release != null) { + release.accept(mouseEvent); + } + } + }); + } + + public static void onMove(final Component component, final Consumer onMove) { + component.addMouseMotionListener(new MouseMotionListenerImpl() { + @Override + public void mouseMoved(final MouseEvent mouseEvent) { + onMove.accept(mouseEvent); + } + }); + } + + public static void onDrag(final Component component, final Consumer onDrag) { + component.addMouseMotionListener(new MouseMotionListenerImpl() { + @Override + public void mouseDragged(final MouseEvent mouseEvent) { + onDrag.accept(mouseEvent); + } + }); + } + + public static void onEnter(final Component component, final Consumer onEnter) { + component.addMouseListener(new MouseListenerImpl() { + @Override + public void mouseEntered(final MouseEvent mouseEvent) { + onEnter.accept(mouseEvent); + } + }); + } + + public static void onExit(final Component component, final Consumer onExit) { + component.addMouseListener(new MouseListenerImpl() { + @Override + public void mouseExited(final MouseEvent mouseEvent) { + onExit.accept(mouseEvent); + } + }); + } + + private static class MouseListenerImpl implements java.awt.event.MouseListener { + + @Override + public void mouseClicked(final MouseEvent mouseEvent) { + + } + + @Override + public void mousePressed(final MouseEvent mouseEvent) { + + } + + @Override + public void mouseReleased(final MouseEvent mouseEvent) { + + } + + @Override + public void mouseEntered(final MouseEvent mouseEvent) { + + } + + @Override + public void mouseExited(final MouseEvent mouseEvent) { + + } + + } + + private static class MouseMotionListenerImpl implements MouseMotionListener { + + @Override + public void mouseDragged(final MouseEvent mouseEvent) { + + } + + @Override + public void mouseMoved(final MouseEvent mouseEvent) { + + } + + } + +} diff --git a/src/main/java/de/ph87/kindermalen/util/MyGridBagConstraints.java b/src/main/java/de/ph87/kindermalen/util/MyGridBagConstraints.java new file mode 100644 index 0000000..6a0d355 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/util/MyGridBagConstraints.java @@ -0,0 +1,23 @@ +package de.ph87.kindermalen.util; + +import java.awt.*; + +public class MyGridBagConstraints extends GridBagConstraints { + + public MyGridBagConstraints(final int x, final int y, final int w, final int h, final double wX, final double wY, final int anchor, final int fill, final Insets insets, final int padX, final int padY) { + super(x, y, w, h, wX, wY, anchor, fill, insets, padX, padY); + } + + public static MyGridBagConstraints C(final int x, final int y, final int w, final int h, final double wX, final double wY, final int anchor, final int fill) { + return new MyGridBagConstraints(x, y, w, h, wX, wY, anchor, fill, new Insets(0, 0, 0, 0), 0, 0); + } + + public static MyGridBagConstraints C(final int x, final int y, final double wX, final double wY) { + return new MyGridBagConstraints(x, y, 1, 1, wX, wY, FIRST_LINE_START, BOTH, new Insets(0, 0, 0, 0), 0, 0); + } + + public static MyGridBagConstraints C(final int x, final int y, final double wX, final double wY, final int fill) { + return new MyGridBagConstraints(x, y, 1, 1, wX, wY, FIRST_LINE_START, fill, new Insets(0, 0, 0, 0), 0, 0); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/Publisher.java b/src/main/java/de/ph87/kindermalen/util/Publisher.java similarity index 96% rename from src/main/java/de/ph87/kindermalen/Publisher.java rename to src/main/java/de/ph87/kindermalen/util/Publisher.java index af23298..d617282 100644 --- a/src/main/java/de/ph87/kindermalen/Publisher.java +++ b/src/main/java/de/ph87/kindermalen/util/Publisher.java @@ -1,4 +1,4 @@ -package de.ph87.kindermalen; +package de.ph87.kindermalen.util; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/de/ph87/kindermalen/Subscription.java b/src/main/java/de/ph87/kindermalen/util/Subscription.java similarity index 92% rename from src/main/java/de/ph87/kindermalen/Subscription.java rename to src/main/java/de/ph87/kindermalen/util/Subscription.java index 9cc6e7a..5e3ce02 100644 --- a/src/main/java/de/ph87/kindermalen/Subscription.java +++ b/src/main/java/de/ph87/kindermalen/util/Subscription.java @@ -1,4 +1,4 @@ -package de.ph87.kindermalen; +package de.ph87.kindermalen.util; import java.util.function.Consumer; diff --git a/src/main/java/de/ph87/kindermalen/drawing/Vector.java b/src/main/java/de/ph87/kindermalen/util/Vector.java similarity index 64% rename from src/main/java/de/ph87/kindermalen/drawing/Vector.java rename to src/main/java/de/ph87/kindermalen/util/Vector.java index b811bbf..2c1dda2 100644 --- a/src/main/java/de/ph87/kindermalen/drawing/Vector.java +++ b/src/main/java/de/ph87/kindermalen/util/Vector.java @@ -1,6 +1,7 @@ -package de.ph87.kindermalen.drawing; +package de.ph87.kindermalen.util; import java.awt.*; +import java.awt.geom.Point2D; public class Vector { @@ -24,6 +25,10 @@ public class Vector { this.length = Math.sqrt(x * x + y * y); } + public Vector(final Point2D point) { + this(point.getX(), point.getY()); + } + public Vector withLength(final double newLength) { final double factor = newLength / length; return new Vector(x * factor, y * factor); @@ -41,4 +46,16 @@ public class Vector { return (int) Math.round(x); } + public Vector plus(final int x, final int y) { + return new Vector(this.x + x, this.y + y); + } + + public Vector minus(final int x, final int y) { + return new Vector(this.x - x, this.y - y); + } + + public Vector scale(final double factor) { + return new Vector(x * factor, y * factor); + } + }