package jrummikub.view.impl; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Stroke; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import jrummikub.model.Position; import jrummikub.model.Stone; import jrummikub.model.StoneColor; class StonePainter { private static final float ASPECT_RATIO = 0.75f; private static final float DEFAULT_WIDTH = 40; private static final float TEXT_POS = 0.275f; private static final float FACE_WIDTH = 0.475f; private static final float CIRCLE_POS = 0.725f; private static final float CIRCLE_WIDTH = 0.45f; private static final Color BACKGROUND_COLOR = new Color(0.9f, 0.9f, 0.6f); private static final Color SELECTED_COLOR = BACKGROUND_COLOR.darker(); private static final float BRIGHTER_SCALE = 1.15f; public static final float HEIGHT_SCALE = ASPECT_RATIO / DEFAULT_WIDTH; private float scale; private static int even(float f) { return 2 * (int) (f / 2); } private static Color brighter(Color color) { int r = (int) (color.getRed() * BRIGHTER_SCALE); int g = (int) (color.getRed() * BRIGHTER_SCALE); int b = (int) (color.getRed() * BRIGHTER_SCALE); return new Color(r > 255 ? 255 : r, g > 255 ? 255 : g, b > 255 ? 255 : b); } private static Color getColor(StoneColor color) { switch (color) { case BLACK: return new Color(0.15f, 0.15f, 0.15f); case BLUE: return new Color(0.0f, 0.0f, 1.0f); case ORANGE: return new Color(1.0f, 0.6f, 0.0f); case RED: return new Color(0.9f, 0.0f, 0.25f); } return null; } public void setScale(float scale) { this.scale = scale; } public float getScale() { return scale; } /** * @param x * x position in screen coordinates * @param y * y position in screen coordinates * @return position in grid coordinates */ public Position calculatePosition(int x, int y) { float width = getStoneWidth(); float height = getStoneHeight(); return new Position(x / width, y / height); } public int getStoneWidth() { return even(DEFAULT_WIDTH * scale); } public int getStoneHeight() { return (int) (DEFAULT_WIDTH * scale / ASPECT_RATIO); } StonePainter(float scale) { this.scale = scale; } private void paintStoneBackground(Graphics2D g, int x, int y, int width, int height, Color background) { // Paint background g.setColor(background); g.fillRect(x, y, width, height); // Paint bevel border g.setColor(brighter(brighter(background))); g.fillRect(x, y, 1, height); g.setColor(brighter(background)); g.fillRect(x + 1, y + 1, 1, height - 2); g.setColor(brighter(brighter(background))); g.fillRect(x, y, width, 1); g.setColor(brighter(background)); g.fillRect(x + 1, y + 1, width - 2, 1); g.setColor(background.darker().darker()); g.fillRect(x + width - 1, y, 1, height); g.setColor(background.darker()); g.fillRect(x + width - 2, y + 1, 1, height - 2); g.setColor(background.darker().darker()); g.fillRect(x, y + height - 1, width, 1); g.setColor(background.darker()); g.fillRect(x + 1, y + height - 2, width - 2, 1); } private void paintJokerFace(Graphics2D g, int x, int y, int width, int height) { Stroke oldStroke = g.getStroke(); g.setStroke(new BasicStroke(2)); g.drawOval(x, y, width, height); g.setStroke(new BasicStroke(1)); GeneralPath path = new GeneralPath(); // nose path.moveTo(x + 0.5f * width, y + 0.45f * height); path.lineTo(x + 0.53f * width, y + 0.6f * height); path.lineTo(x + 0.47f * width, y + 0.6f * height); path.closePath(); g.fill(path); path.reset(); // mouth, left path.moveTo(x + 0.23f * width, y + 0.75f * width); path.lineTo(x + 0.27f * width, y + 0.65f * width); // mouth, middle path.moveTo(x + 0.25f * width, y + 0.7f * width); path.lineTo(x + 0.5f * width, y + 0.8f * width); path.lineTo(x + 0.75f * width, y + 0.7f * width); // mouth, right path.moveTo(x + 0.77f * width, y + 0.75f * width); path.lineTo(x + 0.73f * width, y + 0.65f * width); g.draw(path); path.reset(); // left eye path.moveTo(x + 0.3f * width, y + 0.41f * height); path.lineTo(x + 0.375f * width, y + 0.375f * height); path.lineTo(x + 0.3f * width, y + 0.34f * height); path.lineTo(x + 0.225f * width, y + 0.375f * height); path.closePath(); g.draw(path); path.reset(); // right eye path.moveTo(x + 0.7f * width, y + 0.41f * height); path.lineTo(x + 0.625f * width, y + 0.375f * height); path.lineTo(x + 0.7f * width, y + 0.34f * height); path.lineTo(x + 0.775f * width, y + 0.375f * height); path.closePath(); g.draw(path); g.setStroke(oldStroke); } private void paintJoker(Graphics2D g, int x, int y, int width, int height, Color color) { int faceSize = even(FACE_WIDTH * width); int pos = y + (int) (TEXT_POS * height); g.setColor(color); paintJokerFace(g, x + width / 2 - faceSize / 2, pos - faceSize / 2, faceSize, faceSize); } private void paintStoneNumber(Graphics2D g, int x, int y, int width, int height, Color color, int v) { int pos = y + (int) (TEXT_POS * height); g.setFont(new Font("SansSerif", Font.BOLD, height / 4)); FontMetrics fm = g.getFontMetrics(); String value = Integer.toString(v); Rectangle2D stringRect = fm.getStringBounds(value, g); g.setColor(color.darker()); g.drawString(value, (int) (x + width / 2 - stringRect.getWidth() / 2) + 1, pos + (fm.getAscent() - fm.getDescent()) / 2 + 1); g.setColor(color); g.drawString(value, (int) (x + width / 2 - stringRect.getWidth() / 2), pos + (fm.getAscent() - fm.getDescent()) / 2); } private void paintCircle(Graphics2D g, int x, int y, int width, int height, Color background) { int size = even(width * CIRCLE_WIDTH); int pos = y + (int) (CIRCLE_POS * height); // Paint circle g.setColor(background.darker()); g.drawArc(x + width / 2 - size / 2, pos - size / 2, size, size, 50, 170); g.setColor(brighter(background)); g.drawArc((int) (x + width / 2 - size / 2), pos - size / 2, size, size, -130, 170); } public void paintStone(Graphics2D g, Stone stone, Position p, boolean selected) { Color background = selected ? SELECTED_COLOR : BACKGROUND_COLOR; int width = getStoneWidth(); int height = getStoneHeight(); int x = (int) (p.getX() * width); int y = (int) (p.getY() * height); paintStoneBackground(g, x, y, width, height, background); Color color = getColor(stone.getColor()); if (selected) color = color.darker(); if (stone.isJoker()) { paintJoker(g, x, y, width, height, color); } else { paintStoneNumber(g, x, y, width, height, color, stone.getValue()); } paintCircle(g, x, y, width, height, background); } }