package dev.kske.chess.ui; import java.awt.*; import java.awt.geom.AffineTransform; import java.util.ArrayList; import java.util.List; import javax.swing.JComponent; import dev.kske.chess.board.Move; import dev.kske.chess.board.Position; /** * Project: Chess
* File: OverlayComponent.java
* Created: 08.07.2019
* * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ public class OverlayComponent extends JComponent { private static final long serialVersionUID = -7326936060890082183L; private final BoardPane boardPane; private List dots; private Move arrow; /** * Creates an instance of {@link OverlayComponent}. * * @param boardPane the board pane inside which this overlay component is * contained */ public OverlayComponent(BoardPane boardPane) { this.boardPane = boardPane; dots = new ArrayList<>(); setSize(boardPane.getPreferredSize()); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); final int tileSize = boardPane.getTileSize(); // Draw an arrow representing the last move and mark its position and // destination if (arrow != null) { Point pos = new Point( arrow.getPos().x * tileSize + tileSize / 2, arrow.getPos().y * tileSize + tileSize / 2 ); Point dest = new Point( arrow.getDest().x * tileSize + tileSize / 2, arrow.getDest().y * tileSize + tileSize / 2 ); Graphics2D g2d = (Graphics2D) g; g2d.setStroke(new BasicStroke(3)); g2d.setColor(Color.yellow); g2d.drawRect( arrow.getPos().x * tileSize, arrow.getPos().y * tileSize, tileSize, tileSize ); g2d.drawRect( arrow.getDest().x * tileSize, arrow.getDest().y * tileSize, tileSize, tileSize ); Shape arrowShape = createArrowShape(pos, dest); g.setColor(new Color(255, 0, 0, 127)); g2d.fill(arrowShape); g2d.setColor(Color.black); g2d.draw(arrowShape); } // Draw possible moves if a piece was selected if (!dots.isEmpty()) { g.setColor(Color.green); int radius = tileSize / 4; for (Position dot : dots) g.fillOval( dot.x * tileSize + tileSize / 2 - radius / 2, dot.y * tileSize + tileSize / 2 - radius / 2, radius, radius ); } } private Shape createArrowShape(Point pos, Point dest) { Polygon arrowPolygon = new Polygon(); arrowPolygon.addPoint(-6, 1); arrowPolygon.addPoint(3, 1); arrowPolygon.addPoint(3, 3); arrowPolygon.addPoint(6, 0); arrowPolygon.addPoint(3, -3); arrowPolygon.addPoint(3, -1); arrowPolygon.addPoint(-6, -1); Point midPoint = midpoint(pos, dest); double rotate = Math.atan2(dest.y - pos.y, dest.x - pos.x); double ptDistance = pos.distance(dest); double scale = ptDistance / 12.0; // 12 because it's the length of the // arrow // polygon. AffineTransform transform = new AffineTransform(); transform.translate(midPoint.x, midPoint.y); transform.rotate(rotate); transform.scale(scale, 5); return transform.createTransformedShape(arrowPolygon); } private Point midpoint(Point p1, Point p2) { return new Point( (int) ((p1.x + p2.x) / 2.0), (int) ((p1.y + p2.y) / 2.0) ); } /** * Displays green dots at a list of positions. * * @param dots the positions at which the dots should be displayed */ public void displayDots(List dots) { this.dots.clear(); this.dots.addAll(dots); repaint(); } /** * Clears all dots displayed at some positions. */ public void clearDots() { dots.clear(); repaint(); } /** * Displays an arrow from the position to the destination of a move. * * @param arrow the move indicating the arrows position and destination */ public void displayArrow(Move arrow) { this.arrow = arrow; repaint(); } /** * Clears the arrow displayed to indicate a move. */ public void clearArrow() { arrow = null; repaint(); } /** * @return the size of one board tile in pixels. * @see dev.kske.chess.ui.BoardPane#getTileSize() */ public int getTileSize() { return boardPane.getTileSize(); } }