[MULTI]
+ layout working now BUT WIP + transform + fixed (virtual) paper size + size adjust + transparency adjust + step-adjust
This commit is contained in:
parent
d2c9efe9ea
commit
42db19b253
4
.gitignore
vendored
4
.gitignore
vendored
@ -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 ###
|
||||
|
||||
17
src/main/java/de/ph87/kindermalen/CONFIG.java
Normal file
17
src/main/java/de/ph87/kindermalen/CONFIG.java
Normal file
@ -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;
|
||||
|
||||
}
|
||||
@ -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<MouseEvent> onPress;
|
||||
|
||||
private final Consumer<MouseEvent> onRelease;
|
||||
|
||||
public ClickListener(final Consumer<MouseEvent> onPress, final Consumer<MouseEvent> 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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<KeyEvent> next;
|
||||
|
||||
public KeyListener(final boolean ctrl, final boolean shift, final int keycode, final Consumer<KeyEvent> 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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<MouseEvent> onMove;
|
||||
|
||||
private final Consumer<MouseEvent> onDrag;
|
||||
|
||||
public MotionListener(final Consumer<MouseEvent> onMove, final Consumer<MouseEvent> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
53
src/main/java/de/ph87/kindermalen/Sidebar.java
Normal file
53
src/main/java/de/ph87/kindermalen/Sidebar.java
Normal file
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
73
src/main/java/de/ph87/kindermalen/Window.java
Normal file
73
src/main/java/de/ph87/kindermalen/Window.java
Normal file
@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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<Tool> 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));
|
||||
}
|
||||
|
||||
private void onMove(final MouseEvent e) {
|
||||
cursor = e.getPoint();
|
||||
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();
|
||||
}
|
||||
|
||||
private void onPress(final MouseEvent e) {
|
||||
drawing.getCurrent().newRevision();
|
||||
lastPoint = toolBox.getTool().apply(null, drawing.getCurrent().getCurrent(), new Vector(e.getPoint()));
|
||||
repaint();
|
||||
@Override
|
||||
public void componentMoved(final ComponentEvent e) {
|
||||
calculateTransform();
|
||||
}
|
||||
|
||||
private void onDrag(final MouseEvent e) {
|
||||
cursor = e.getPoint();
|
||||
lastPoint = toolBox.getTool().apply(lastPoint, drawing.getCurrent().getCurrent(), new Vector(e.getPoint()));
|
||||
repaint();
|
||||
@Override
|
||||
public void componentShown(final ComponentEvent e) {
|
||||
}
|
||||
|
||||
private void onRelease(final MouseEvent e) {
|
||||
SAVE_DIR.mkdirs();
|
||||
@Override
|
||||
public void componentHidden(final ComponentEvent e) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
final Image image = drawing.getImage();
|
||||
|
||||
g.setColor(Color.white);
|
||||
g.fillRect(0, 0, drawing.getWidth(), drawing.getHeight());
|
||||
g.drawImage(drawing.getCurrent().getCurrent(), 0, 0, null);
|
||||
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 = toolBox.getTool().getPreview();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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<BufferedImage> history = new ArrayList<>();
|
||||
|
||||
private int index;
|
||||
@Getter
|
||||
private int index = 0;
|
||||
|
||||
@Getter
|
||||
private int revision = 0;
|
||||
|
||||
private final Publisher<BufferedImage> 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<BufferedImage> onChange(final Consumer<BufferedImage> next) {
|
||||
return onChange.subscribe(next);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
}
|
||||
@ -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<T extends Tool> extends JPanel {
|
||||
|
||||
protected final T tool;
|
||||
|
||||
protected ToolPanel(final T tool) {
|
||||
this.tool = tool;
|
||||
setPreferredSize(new Dimension(100, 100));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Stamp> 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<File> 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<File> scan(final File dir) {
|
||||
final List<File> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<StampTool> {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package de.ph87.kindermalen.toolbox;
|
||||
|
||||
public interface Observer<T> {
|
||||
|
||||
void next(final T item);
|
||||
|
||||
}
|
||||
@ -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<Tool> tools = List.of(new StampTool());
|
||||
|
||||
@Getter
|
||||
private Tool tool;
|
||||
|
||||
private final Publisher<Tool> 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<Tool> onToolChange(final Consumer<Tool> next) {
|
||||
return onToolChange.subscribe(next);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
32
src/main/java/de/ph87/kindermalen/tools/ToolButton.java
Normal file
32
src/main/java/de/ph87/kindermalen/tools/ToolButton.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
38
src/main/java/de/ph87/kindermalen/tools/Tools.java
Normal file
38
src/main/java/de/ph87/kindermalen/tools/Tools.java
Normal file
@ -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<Tool> tools = List.of(new StampTool());
|
||||
|
||||
@Getter
|
||||
private Tool tool;
|
||||
|
||||
private final Publisher<Tool> 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<Tool> onToolSelected(final Consumer<Tool> next) {
|
||||
return onToolSelected.subscribe(next);
|
||||
}
|
||||
|
||||
}
|
||||
30
src/main/java/de/ph87/kindermalen/tools/ToolsPanel.java
Normal file
30
src/main/java/de/ph87/kindermalen/tools/ToolsPanel.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
31
src/main/java/de/ph87/kindermalen/tools/tool/Tool.java
Normal file
31
src/main/java/de/ph87/kindermalen/tools/tool/Tool.java
Normal file
@ -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<Tool> 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<Tool> onChange(final Consumer<Tool> next) {
|
||||
return onChange.subscribe(next);
|
||||
}
|
||||
|
||||
}
|
||||
10
src/main/java/de/ph87/kindermalen/tools/tool/ToolPanel.java
Normal file
10
src/main/java/de/ph87/kindermalen/tools/tool/ToolPanel.java
Normal file
@ -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 {
|
||||
|
||||
}
|
||||
@ -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<Integer, BufferedImage> 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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Stamp> 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;
|
||||
}
|
||||
|
||||
}
|
||||
22
src/main/java/de/ph87/kindermalen/util/FileHelper.java
Normal file
22
src/main/java/de/ph87/kindermalen/util/FileHelper.java
Normal file
@ -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<File> SCAN_FILES(final File dir) {
|
||||
final List<File> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package de.ph87.kindermalen;
|
||||
package de.ph87.kindermalen.util;
|
||||
|
||||
import com.mortennobel.imagescaling.ResampleFilters;
|
||||
import com.mortennobel.imagescaling.ResampleOp;
|
||||
39
src/main/java/de/ph87/kindermalen/util/KeyListener.java
Normal file
39
src/main/java/de/ph87/kindermalen/util/KeyListener.java
Normal file
@ -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<KeyEvent> 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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package de.ph87.kindermalen.tool.stamp;
|
||||
package de.ph87.kindermalen.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
109
src/main/java/de/ph87/kindermalen/util/MouseListener.java
Normal file
109
src/main/java/de/ph87/kindermalen/util/MouseListener.java
Normal file
@ -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<MouseEvent> onPress) {
|
||||
component.addMouseListener(new MouseListenerImpl() {
|
||||
@Override
|
||||
public void mousePressed(final MouseEvent mouseEvent) {
|
||||
onPress.accept(mouseEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void onRelease(final Component component, final Consumer<MouseEvent> 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<MouseEvent> onMove) {
|
||||
component.addMouseMotionListener(new MouseMotionListenerImpl() {
|
||||
@Override
|
||||
public void mouseMoved(final MouseEvent mouseEvent) {
|
||||
onMove.accept(mouseEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void onDrag(final Component component, final Consumer<MouseEvent> onDrag) {
|
||||
component.addMouseMotionListener(new MouseMotionListenerImpl() {
|
||||
@Override
|
||||
public void mouseDragged(final MouseEvent mouseEvent) {
|
||||
onDrag.accept(mouseEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void onEnter(final Component component, final Consumer<MouseEvent> onEnter) {
|
||||
component.addMouseListener(new MouseListenerImpl() {
|
||||
@Override
|
||||
public void mouseEntered(final MouseEvent mouseEvent) {
|
||||
onEnter.accept(mouseEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void onExit(final Component component, final Consumer<MouseEvent> 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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package de.ph87.kindermalen;
|
||||
package de.ph87.kindermalen.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -1,4 +1,4 @@
|
||||
package de.ph87.kindermalen;
|
||||
package de.ph87.kindermalen.util;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user