KinderMalen/src/main/java/de/ph87/kindermalen/drawing/DrawingPanel.java
2023-02-28 10:19:46 +01:00

153 lines
4.7 KiB
Java

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<Tool> 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);
}
}