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 HIGHLIGHTED_COLOR = BACKGROUND_COLOR.darker(); private static final float BRIGHTER_SCALE = 1.15f; public static final float BOARD_SCALE = 75.0f*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 = even(DEFAULT_WIDTH*scale); float height = (int)(DEFAULT_WIDTH*scale/ASPECT_RATIO); return new Position(x/width, y/height); } 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, Stone stone) { 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, Stone stone) { int faceSize = even(FACE_WIDTH*width); int pos = y + (int)(TEXT_POS*height); g.setColor(getColor(stone.getColor())); paintJokerFace(g, x+width/2-faceSize/2, pos-faceSize/2, faceSize, faceSize, stone); } private void paintStoneNumber(Graphics2D g, int x, int y, int width, int height, Stone stone) { int pos = y + (int)(TEXT_POS*height); g.setFont(new Font("SansSerif", Font.BOLD, height/4)); FontMetrics fm = g.getFontMetrics(); String value = Integer.toString(stone.getValue()); Rectangle2D stringRect = fm.getStringBounds(value, g); g.setColor(getColor(stone.getColor()).darker()); g.drawString(value, (int)(x+width/2-stringRect.getWidth()/2)+1, pos+(fm.getAscent()-fm.getDescent())/2+1); g.setColor(getColor(stone.getColor())); 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 highlighted) { Color background = highlighted ? HIGHLIGHTED_COLOR : BACKGROUND_COLOR; int width = even(DEFAULT_WIDTH*scale); int height = (int)(DEFAULT_WIDTH*scale/ASPECT_RATIO); int x = (int)(p.getX()*width); int y = (int)(p.getY()*height); paintStoneBackground(g, x, y, width, height, background); if (stone.isJoker()) { paintJoker(g, x, y, width, height, stone); } else { paintStoneNumber(g, x, y, width, height, stone); } paintCircle(g, x, y, width, height, background); } }