| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // +build example |
| // |
| // This build tag means that "go install golang.org/x/exp/shiny/..." doesn't |
| // install this example program. Use "go run main.go board.go xy.go" to run it |
| // or "go install -tags=example" to install it. |
| |
| package main |
| |
| import ( |
| "fmt" |
| "image" |
| ) |
| |
| // Note that a "square" is not a drawn square on the board, but |
| // the rectangle (sic) centered on the game point. |
| type Dims struct { |
| dim int // Size of board, always square. Full size is 19. |
| percent int // Scale. |
| xInset int // From left edge to left edge of first square. |
| yInset int // From top edge to top edge of first square. |
| stoneDiam int // Diameter of stone in pixels. |
| stoneRad2 int // Stone radius squared. |
| squareWidth int // Width of square in pixels. |
| squareHeight int // Height of square in pixels. |
| lineWidth int // Width of lines. |
| } |
| |
| // Initial values, in pixels. These numbers match the images that are loaded; they are resized. |
| const ( |
| // These numbers are tuned for the images in asset/. |
| xInset0 = 100 // Distance from the edge of the board image to the left side of the first stone. |
| yInset0 = 115 // Distance from the top of the board image to the top side of the first stone. |
| stoneSize0 = 256 // Size of a stone on the board. |
| stoneDiam0 = 225 // Diameter of the circular part of the stone within the square. |
| // The square is a little smaller than the stone, for crowding. |
| squareWidth0 = 215 // Width of a square on the board. |
| squareHeight0 = 218 // Height of a square on the board. |
| stoneRad2 = stoneDiam0 * stoneDiam0 / 4 |
| ) |
| |
| func (d *Dims) Init(dim, percent int) { |
| d.dim = dim |
| d.Resize(percent) |
| } |
| |
| func (d *Dims) Resize(percent int) { |
| d.percent = percent |
| d.xInset = xInset0 * percent / 100 |
| d.yInset = yInset0 * percent / 100 |
| d.stoneDiam = stoneDiam0 * percent / 100 |
| d.squareWidth = squareWidth0 * percent / 100 |
| d.squareHeight = squareHeight0 * percent / 100 |
| d.stoneRad2 = d.stoneDiam * d.stoneDiam / 4 |
| d.lineWidth = 4 * percent / 100 |
| if d.lineWidth < 2 { |
| d.lineWidth = 2 |
| } |
| if d.lineWidth > 2 { |
| d.lineWidth = 4 |
| } |
| } |
| |
| // An XY represents a pixel in the board image. Y is downwards. |
| type XY struct { |
| x, y int |
| } |
| |
| // An IJ represents a position on the board. It is 1-indexed and J is upwards. |
| type IJ struct { |
| i, j int |
| } |
| |
| func (ij IJ) String() string { |
| return fmt.Sprintf("%c%d", 'A'+int(ij.i-1), ij.j) |
| } |
| |
| // A go board is played on centers, not on squares, but we think of it internally |
| // as IJ squares (1, 1) through (19, 19), and draw the lines through the middle |
| // of those squares. That is how the arithmetic is done converting between |
| // IJ and XY. |
| |
| // IJ converts a position in the board image to a Go position (IJ) on the board. |
| // The boolean is false if the point does not represent a Go position. |
| func (xy XY) IJ(d *Dims) (IJ, bool) { |
| x, y := xy.x, xy.y |
| x -= d.xInset |
| y -= d.yInset |
| i := x / d.squareWidth |
| j := y / d.squareHeight |
| // Now zero indexed. Make j goes up and switch to 1-indexed. |
| j = d.dim - 1 - j |
| i++ |
| j++ |
| if 1 <= i && i <= d.dim && 1 <= j && j <= d.dim { |
| return IJ{i, j}, true |
| } |
| return IJ{}, false |
| } |
| |
| // XY converts a Go position to the upper left corner of the square for that position |
| // on the board image. |
| func (ij IJ) XY(d *Dims) XY { |
| // Change to 0-indexed. |
| ij.i-- |
| ij.j-- |
| // j goes down. |
| ij.j = (d.dim - 1) - ij.j |
| return XY{d.xInset + ij.i*d.squareWidth, d.yInset + ij.j*d.squareHeight} |
| } |
| |
| // IJtoXYCenter converts a Go position to the center of the square for that position |
| // on the board image. |
| func (ij IJ) XYCenter(d *Dims) XY { |
| xy := ij.XY(d) |
| xy.x += d.squareWidth / 2 |
| xy.y += d.squareHeight / 2 |
| return xy |
| } |
| |
| // IJtoXYStone converts a Go position to the square holding the stone for that position |
| // on the board image. |
| func (ij IJ) XYStone(d *Dims) image.Rectangle { |
| center := ij.XYCenter(d) |
| min := image.Point{center.x - d.stoneDiam/2, center.y - d.stoneDiam/2} |
| max := image.Point{center.x + d.stoneDiam/2, center.y + d.stoneDiam/2} |
| return image.Rectangle{Min: min, Max: max} |
| } |