// Field.java package net.sf.gogui.boardpainter; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Rectangle; import java.awt.Stroke; import java.awt.font.LineMetrics; import java.awt.geom.Point2D; import net.sf.gogui.go.GoColor; import static net.sf.gogui.go.GoColor.BLACK; import static net.sf.gogui.go.GoColor.WHITE; import static net.sf.gogui.go.GoColor.EMPTY; /** State of a field on the board. */ public class Field implements ConstField { public Field() { } public void clearInfluence() { m_influenceSet = false; m_influence = 0; } public void draw(Graphics graphics, int size, int x, int y, Image boardImage, int boardWidth) { if (! graphics.hitClip(x, y, size, size)) return; m_graphics = graphics.create(x, y, size, size); if (m_graphics instanceof Graphics2D) m_graphics2D = (Graphics2D)m_graphics; else m_graphics2D = null; m_size = size; if (m_fieldColor != null) drawFieldColor(); if (m_territory != EMPTY && m_graphics2D == null) drawTerritoryGraphics(); if (m_color != EMPTY) drawStone(m_color, false); if (m_ghostStone != null) drawStone(m_ghostStone, true); if (m_label != null && ! m_label.equals("")) drawLabel(x, y, graphics, boardImage, boardWidth); if (m_territory != EMPTY && m_graphics2D != null) drawTerritoryGraphics2D(); if (m_influenceSet) drawInfluence(); drawMarks(); if (m_crossHair) drawCrossHair(); if (m_lastMoveMarker) drawLastMoveMarker(); if (m_select) drawSelect(); if (m_cursor) drawCursor(); m_graphics = null; } public GoColor getColor() { return m_color; } public boolean getCursor() { return m_cursor; } public boolean getCrossHair() { return m_crossHair; } public Color getFieldBackground() { return m_fieldColor; } public boolean getMark() { return m_mark; } public boolean getMarkCircle() { return m_markCircle; } public boolean getMarkSquare() { return m_markSquare; } public boolean getMarkTriangle() { return m_markTriangle; } public boolean getSelect() { return m_select; } /** @see #setGhostStone */ public GoColor getGhostStone() { return m_ghostStone; } public static int getStoneMargin(int size) { return size / 17; } public String getLabel() { return m_label; } public GoColor getTerritory() { return m_territory; } public boolean isInfluenceSet() { return m_influenceSet; } public void setFieldBackground(Color color) { m_fieldColor = color; } public void setColor(GoColor color) { m_color = color; } public void setCrossHair(boolean crossHair) { m_crossHair = crossHair; } public void setCursor(boolean cursor) { m_cursor = cursor; } public void setInfluence(double value) { if (value > 1.) value = 1.; else if (value < -1.) value = -1.; m_influence = value; m_influenceSet = true; } public void setLastMoveMarker(boolean lastMoveMarker) { m_lastMoveMarker = lastMoveMarker; } public void setMark(boolean mark) { m_mark = mark; } public void setMarkCircle(boolean mark) { m_markCircle = mark; } public void setMarkSquare(boolean mark) { m_markSquare = mark; } public void setMarkTriangle(boolean mark) { m_markTriangle = mark; } public void setSelect(boolean select) { m_select = select; } /** Set a shadow stone at this field. A shadow stone is a stone which is drawn with some transparency. It can be used to display stones which are not part of the current board position (e.g. search variations) @param color The color of the shadow stone or null to remove a shadow stone. */ public void setGhostStone(GoColor color) { m_ghostStone = color; } public void setLabel(String s) { m_label = s; } public void setTerritory(GoColor color) { assert color != null; m_territory = color; } private boolean m_crossHair; private boolean m_cursor; private boolean m_lastMoveMarker; private boolean m_mark; private boolean m_markCircle; private boolean m_markSquare; private boolean m_markTriangle; private boolean m_influenceSet; private boolean m_select; private static int s_cachedFontFieldSize; private int m_paintSizeBlack; private int m_paintSizeWhite; private int m_size; private double m_influence; private static final AlphaComposite COMPOSITE_4 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f); private static final AlphaComposite COMPOSITE_5 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f); private static final AlphaComposite COMPOSITE_6 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f); private static final AlphaComposite COMPOSITE_7 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f); private static final AlphaComposite COMPOSITE_8 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f); private static final AlphaComposite COMPOSITE_95 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.95f); private static final AlphaComposite COMPOSITE_97 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.97f); private static final Stroke THICK_STROKE = new BasicStroke(2f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER); private String m_label = ""; private Color m_fieldColor; private GoColor m_territory = EMPTY; private static final Color COLOR_INFLUENCE_BLACK = Color.gray; private static final Color COLOR_INFLUENCE_WHITE = Color.white; private static final Color COLOR_LAST_MOVE = Color.decode("#888888"); private static final Color COLOR_MARK = Color.decode("#4040ff"); private static final Color COLOR_STONE_BLACK = Color.decode("#3b3d3a"); private static final Color COLOR_STONE_BLACK_BRIGHT = Color.decode("#99998c"); private static final Color COLOR_STONE_WHITE = Color.decode("#d3d7cf"); private static final Color COLOR_STONE_WHITE_BRIGHT = Color.decode("#ffffff"); private static Font s_cachedFont; private GoColor m_color = EMPTY; private GoColor m_ghostStone; private Graphics m_graphics; private Graphics2D m_graphics2D; private RadialGradientPaint m_paintBlack; private RadialGradientPaint m_paintWhite; private void drawCircle(Color color) { m_graphics.setColor(color); int d = m_size * 4 / 10; int w = m_size - 2 * d; m_graphics.fillOval(d, d, w, w); } private void drawCrossHair() { setComposite(COMPOSITE_7); int d = m_size / 5; int center = m_size / 2; m_graphics.setColor(Color.red); m_graphics.drawLine(d, center, m_size - d, center); m_graphics.drawLine(center, d, center, m_size - d); m_graphics.setPaintMode(); } private void drawCursor() { setComposite(COMPOSITE_7); int d = m_size / 6; int w = m_size; int d2 = 2 * d; m_graphics.setColor(COLOR_LAST_MOVE); Stroke oldStroke = null; if (m_graphics2D != null && m_size > 10) { oldStroke = m_graphics2D.getStroke(); m_graphics2D.setStroke(THICK_STROKE); } m_graphics.drawLine(d, d, d2, d); m_graphics.drawLine(d, d, d, d2); m_graphics.drawLine(d, w - d2 - 1, d, w - d - 1); m_graphics.drawLine(d, w - d - 1, d2, w - d - 1); m_graphics.drawLine(w - d2 - 1, d, w - d - 1, d); m_graphics.drawLine(w - d - 1, d, w - d - 1, d2); m_graphics.drawLine(w - d - 1, w - d - 1, w - d - 1, w - d2 - 1); m_graphics.drawLine(w - d - 1, w - d - 1, w - d2 - 1, w - d - 1); if (oldStroke != null) m_graphics2D.setStroke(oldStroke); m_graphics.setPaintMode(); } private void drawFieldColor() { setComposite(COMPOSITE_5); m_graphics.setColor(m_fieldColor); m_graphics.fillRect(0, 0, m_size, m_size); m_graphics.setPaintMode(); } private void drawInfluence() { double d = Math.abs(m_influence); if (d < 0.01) return; setComposite(COMPOSITE_6); if (m_influence > 0) m_graphics.setColor(COLOR_INFLUENCE_BLACK); else m_graphics.setColor(COLOR_INFLUENCE_WHITE); int dd = (int)(m_size * (0.38 + (1 - d) * 0.62)); int width = m_size - dd; m_graphics.fillRect(dd / 2, dd / 2, width, width); } private void drawLabel(int fieldX, int fieldY, Graphics boardGraphics, Image boardImage, int boardWidth) { setComposite(COMPOSITE_97); setFont(m_graphics, m_size); FontMetrics fontMetrics = m_graphics.getFontMetrics(); LineMetrics lineMetrics = fontMetrics.getLineMetrics(m_label, m_graphics); int width = fontMetrics.stringWidth(m_label); int height = fontMetrics.getHeight(); int ascent = (int)lineMetrics.getAscent(); int x = Math.max((m_size - width) / 2, 0); int y = (ascent + m_size) / 2; if (m_ghostStone == null) { if (m_color == BLACK) m_graphics.setColor(Color.white); else m_graphics.setColor(Color.black); } else { if (m_ghostStone == BLACK) m_graphics.setColor(Color.white); else m_graphics.setColor(Color.black); } Rectangle oldClip = m_graphics.getClipBounds(); width = Math.min(width, (int)(0.95 * m_size)); m_graphics.setClip(x, y - ascent, width, height); if (m_color == EMPTY && m_ghostStone == null) { Rectangle oldBoardClip = boardGraphics.getClipBounds(); boardGraphics.setClip(fieldX + x, fieldY + y - ascent, width, height); boardGraphics.drawImage(boardImage, 0, 0, boardWidth, boardWidth, null); boardGraphics.setClip(oldBoardClip); m_graphics.setColor(Color.black); } m_graphics.drawString(m_label, x, y); m_graphics.setClip(oldClip); } private void drawLastMoveMarker() { setComposite(COMPOSITE_7); drawCircle(COLOR_LAST_MOVE); m_graphics.setPaintMode(); } private void drawMarks() { setComposite(COMPOSITE_95); int d = m_size / 4; int width = m_size - 2 * d; m_graphics.setColor(COLOR_MARK); Stroke oldStroke = null; if (m_graphics2D != null && m_size > 10) { oldStroke = m_graphics2D.getStroke(); m_graphics2D.setStroke(THICK_STROKE); } if (m_mark) { m_graphics.drawLine(d, d, d + width, d + width); m_graphics.drawLine(d, d + width, d + width, d); } if (m_markCircle) m_graphics.drawOval(d, d, width - 1, width - 1); if (m_markSquare) m_graphics.drawRect(d, d, width - 1, width - 1); if (m_markTriangle) { int height = (int)(0.866 * width); int top = (int)(0.866 * (width - height) / 2); int bottom = top + height - 1; m_graphics.drawLine(d, d + bottom, d + width / 2, d + top); m_graphics.drawLine(d + width / 2, d + top, d + width, d + bottom); m_graphics.drawLine(d + width, d + bottom, d, d + bottom); } if (oldStroke != null) m_graphics2D.setStroke(oldStroke); m_graphics.setPaintMode(); } private void drawSelect() { setComposite(COMPOSITE_95); drawCircle(COLOR_MARK); m_graphics.setPaintMode(); } private void drawStone(GoColor color, boolean isGhostStone) { if (color == BLACK) drawStone(color, COLOR_STONE_BLACK, COLOR_STONE_BLACK_BRIGHT, isGhostStone); else if (color == WHITE) drawStone(color, COLOR_STONE_WHITE, COLOR_STONE_WHITE_BRIGHT, isGhostStone); } private void drawStone(GoColor color, Color colorNormal, Color colorBright, boolean isGhostStone) { int margin = getStoneMargin(m_size); if (m_graphics2D != null && m_size >= 7) { RadialGradientPaint paint = getPaint(color, m_size, colorNormal, colorBright); m_graphics2D.setPaint(paint); } else { m_graphics.setColor(colorNormal); } if (isGhostStone) setComposite(COMPOSITE_8); m_graphics.fillOval(margin, margin, m_size - 2 * margin, m_size - 2 * margin); } private void drawTerritoryGraphics() { if (m_territory == BLACK) m_graphics.setColor(Color.darkGray); else { assert m_territory == WHITE; m_graphics.setColor(Color.lightGray); } m_graphics.fillRect(0, 0, m_size, m_size); } private void drawTerritoryGraphics2D() { setComposite(COMPOSITE_4); if (m_territory == BLACK) m_graphics2D.setColor(Color.darkGray); else { assert m_territory == WHITE; m_graphics2D.setColor(Color.white); } m_graphics2D.fillRect(0, 0, m_size, m_size); m_graphics2D.setPaintMode(); } private RadialGradientPaint getPaint(GoColor color, int size, Color colorNormal, Color colorBright) { RadialGradientPaint paint; int paintSize; if (color == BLACK) { paint = m_paintBlack; paintSize = m_paintSizeBlack; } else { assert color == WHITE; paint = m_paintWhite; paintSize = m_paintSizeWhite; } if (size == paintSize && paint != null) return paint; Point2D.Double center = new Point2D.Double(0.43 * size, 0.21 * size); Point2D.Double radius1 = new Point2D.Double(0.47 * size, -0.15 * size); Point2D.Double radius2 = new Point2D.Double(0.08 * size, 0.25 * size); double focus = -0.4; paint = new RadialGradientPaint(center, radius1, radius2, focus, colorBright, colorNormal); if (color == BLACK) { m_paintBlack = paint; m_paintSizeBlack = size; } else { m_paintWhite = paint; m_paintSizeWhite = size; } return paint; } private void setComposite(AlphaComposite composite) { if (m_graphics2D != null) m_graphics2D.setComposite(composite); } private static void setFont(Graphics graphics, int fieldSize) { if (s_cachedFont != null && s_cachedFontFieldSize == fieldSize) { graphics.setFont(s_cachedFont); return; } int fontSize; if (fieldSize < 29) fontSize = (int)(0.45 * fieldSize); else if (fieldSize < 40) fontSize = 13; else fontSize = (int)(13 + 0.15 * (fieldSize - 40)); s_cachedFont = new Font("Dialog", Font.PLAIN, fontSize); s_cachedFontFieldSize = fieldSize; graphics.setFont(s_cachedFont); } }