package de.ph87.kindermalen.drawing; import de.ph87.kindermalen.ComponentListener; import de.ph87.kindermalen.Environment; import de.ph87.kindermalen.MyComponent; import de.ph87.kindermalen.toolbox.tool.Tool; import de.ph87.kindermalen.util.Subscription; import de.ph87.kindermalen.util.Vector; import lombok.extern.slf4j.Slf4j; import java.awt.*; 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; @Slf4j public class DrawingPanel extends MyComponent { private static final File SAVE_DIR = new File("./data/images"); private final Environment environment; private Subscription toolChangeSubscription = null; private Vector lastPoint = null; private Vector cursor = null; private AffineTransform transform; private AffineTransform inverseTransform; public DrawingPanel(final Environment environment) { this.environment = environment; subscribe(environment.getToolbox().getOnSelect(), this::onToolSelect); subscribe(environment.getOnDrawingChange(), drawing -> repaint()); ComponentListener.on(this) .componentResized(this::calculateTransform) .componentMoved(this::calculateTransform) .componentShown(this::calculateTransform) .mousePressed(this::mousePressed) .mouseReleased(this::mouseReleased) .mouseDragged(this::mouseDragged) .mouseMoved(this::mouseMoved); } private void onToolSelect(final Tool tool) { repaint(); if (toolChangeSubscription != null) { unsubscribe(toolChangeSubscription); } toolChangeSubscription = subscribe(tool.getOnChange(), ignore -> repaint()); } public void mousePressed(final MouseEvent mouseEvent) { if (mouseEvent.getButton() == 3) { environment.getDrawing().undo(); return; } environment.getDrawing().newRevision(); lastPoint = environment.getToolbox().getTool().apply(null, environment.getDrawing(), transform(mouseEvent.getPoint())); repaint(); } public void mouseReleased(final MouseEvent mouseEvent) { try { environment.getDrawing().save(SAVE_DIR); } catch (IOException ex) { log.error(ex.toString()); } } public void mouseDragged(final MouseEvent mouseEvent) { cursor = transform(mouseEvent.getPoint()); lastPoint = environment.getToolbox().getTool().apply(lastPoint, environment.getDrawing(), transform(mouseEvent.getPoint())); repaint(); } public void mouseMoved(final MouseEvent mouseEvent) { cursor = transform(mouseEvent.getPoint()); repaint(); } private void calculateTransform() { final Drawing drawing = environment.getDrawing(); final Rectangle box = fitInside(drawing.width, drawing.height, getWidth(), getHeight(), false); final double scale = (double) box.width / drawing.width; transform = new AffineTransform(); transform.translate(box.x, box.y); transform.scale(scale, scale); try { inverseTransform = transform.createInverse(); } catch (NoninvertibleTransformException ex) { log.error(ex.toString()); } } private Vector transform(final Point point) { final Point2D transformed = inverseTransform.transform(new Point2D.Double(point.x, point.y), null); return new Vector(transformed); } @Override public void paint(final Graphics graphics) { final Graphics2D g = (Graphics2D) graphics; g.setColor(Color.gray); g.fillRect(0, 0, getWidth(), getHeight()); if (transform == null || environment == null || environment.getDrawing() == null) { return; } g.setTransform(transform); draw(g, environment.getDrawing()); } private void draw(final Graphics2D g, final Drawing drawing) { g.setColor(Color.white); g.fillRect(0, 0, drawing.width, drawing.height); g.drawImage(drawing.getImage(), 0, 0, drawing.width, drawing.height, null); final BufferedImage preview = environment.getToolbox().getTool().getPreview(); if (cursor != null && preview != 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); } }