Commit 846cc4f2 authored by Derek Schaab's avatar Derek Schaab

Support board rotation in PNG and SVG renderers

parent 74222a8a
......@@ -8,6 +8,8 @@ import (
// WriteASCII renders a visual representation of the game g to w in ASCII art
// format.
//
// This function does not yet support board rotation.
func WriteASCII(w io.Writer, g chess.Game, o Options) (err error) {
dst := &asciiWriter{w: w}
for s := chess.First; s <= chess.Last; s++ {
......
package chessboard
import (
"gitlab.com/eightsquared/chess"
)
// label returns the text label for the square s when the board is rotated by
// the rotation r. A non-empty label is returned only if the square is in
// either the leftmost column or the bottommost row.
func label(s chess.Square, r Rotation) (label string) {
x, y := rotate(s, r, 1)
left, bottom := x == 0, y == 7
if !left && !bottom {
return
}
if left && bottom {
label = string([]byte{byte(s.File()), byte(s.Rank())})
return
}
if r == Clockwise || r == CounterClockwise {
if left {
label = string(s.File())
} else {
label = string(s.Rank())
}
} else {
if left {
label = string(s.Rank())
} else {
label = string(s.File())
}
}
return
}
......@@ -54,31 +54,23 @@ func WritePNG(w io.Writer, g chess.Game, o Options) (err error) {
}
}
for square := chess.First; square <= chess.Last; square++ {
file, rank := square.File(), square.Rank()
x, y := int(file-'a')*rowSize, int('8'-rank)*rowSize
x, y := rotate(square, o.Rotate, rowSize)
dr := image.Rectangle{image.Point{x, y}, image.Point{x + rowSize, y + rowSize}}
if square.Light() {
draw.Draw(dst, dr, light, image.Point{}, draw.Src)
} else {
draw.Draw(dst, dr, dark, image.Point{}, draw.Src)
}
if file == 'a' || rank == '1' {
lbl := label(square, o.Rotate)
if lbl != "" {
fx, fy := x+(rowSize>>4), y+rowSize-(rowSize>>4)
var s string
if file == 'a' && rank > '1' {
s = string(rank)
} else if file > 'a' && rank == '1' {
s = string(file)
} else {
s = string([]byte{byte(file), byte(rank)})
}
if square.Light() {
ctx.SetSrc(dark)
} else {
ctx.SetSrc(light)
}
p := fixed.Point26_6{X: fixed.Int26_6(fx << 6), Y: fixed.Int26_6(fy << 6)}
if _, err = ctx.DrawString(s, p); err != nil {
if _, err = ctx.DrawString(lbl, p); err != nil {
return
}
}
......
package chessboard
import (
"gitlab.com/eightsquared/chess"
)
// rotate returns the top-left coordinate of the square s when the board is
// rotated by the rotation r and each rendered square is of the given size.
func rotate(s chess.Square, r Rotation, size int) (x, y int) {
file, rank := s.File(), s.Rank()
switch r {
default:
x, y = int(file-'a')*size, int('8'-rank)*size
case Clockwise:
x, y = int(rank-'1')*size, int(file-'a')*size
case CounterClockwise:
x, y = int('8'-rank)*size, int('h'-file)*size
case Half:
x, y = int('h'-file)*size, int(rank-'1')*size
}
return
}
......@@ -58,30 +58,30 @@ func WriteSVG(w io.Writer, g chess.Game, o Options) (err error) {
dst.writeOpenTag(boardSize)
dst.writeDefs(g, rowSize, rowSizeStr, lightRGBAStr, darkRGBAStr)
dst.writeGroupOpenTag(rowSize)
var file, rank rune
var x, y, tx, ty []byte
var x, y int
var tx, ty []byte
var p chess.Piece
for s := chess.First; s <= chess.Last; s++ {
file, rank = s.File(), s.Rank()
x, y = coordStrs[int(file-'a')], coordStrs[int('8'-rank)]
x, y = rotate(s, o.Rotate, 1)
if s.Light() {
dst.writeSquareBackground(x, y, light)
dst.writeSquareBackground(coordStrs[x], coordStrs[y], light)
} else {
dst.writeSquareBackground(x, y, dark)
dst.writeSquareBackground(coordStrs[x], coordStrs[y], dark)
}
if file == 'a' || rank == '1' {
tx, ty = labelXCoordStrs[int(file-'a')], labelYCoordStrs[int('8'-rank)]
lbl := label(s, o.Rotate)
if lbl != "" {
tx, ty = labelXCoordStrs[x], labelYCoordStrs[y]
if s.Light() {
dst.writeSquareLabel(file, rank, tx, ty, darkRGBAStr)
dst.writeSquareLabel([]byte(lbl), tx, ty, darkRGBAStr)
} else {
dst.writeSquareLabel(file, rank, tx, ty, lightRGBAStr)
dst.writeSquareLabel([]byte(lbl), tx, ty, lightRGBAStr)
}
}
p = g.Board[s]
if p == chess.Empty {
continue
}
dst.writePiece(p, x, y)
dst.writePiece(p, coordStrs[x], coordStrs[y])
}
dst.write(tagGroupClose)
dst.write(tagSVGClose)
......@@ -238,7 +238,7 @@ func (w *svgWriter) writeSquareBackground(x, y, href []byte) {
w.write(bracket)
}
func (w *svgWriter) writeSquareLabel(file, rank rune, x, y, fill []byte) {
func (w *svgWriter) writeSquareLabel(lbl []byte, x, y, fill []byte) {
w.write(tagText)
w.write(attX)
w.write(x)
......@@ -250,14 +250,7 @@ func (w *svgWriter) writeSquareLabel(file, rank rune, x, y, fill []byte) {
w.write(fill)
w.write(quote)
w.write(bracket)
if file == 'a' && rank > '1' {
w.writeByte(byte(rank))
} else if file > 'a' && rank == '1' {
w.writeByte(byte(file))
} else {
w.writeByte(byte(file))
w.writeByte(byte(rank))
}
w.write(lbl)
w.write(tagTextClose)
}
......
......@@ -7,6 +7,22 @@ import (
"gitlab.com/eightsquared/chess"
)
// Rotation enumerates the allowed rotational transforms that can be applied
// to a rendered board.
type Rotation uint8
// By default, no rotation is applied to the board. Clockwise and
// CounterCllockwise indicate that the board should be rotated one
// quarter-turn in the clockwise and counter-clockwise directions,
// respectively. Half indicates that the board should be rotated one
// half-turn, which effectively draws the board from Black's point of view.
const (
None Rotation = iota
Clockwise
CounterClockwise
Half
)
// Options describes the options that may be applied to a rendered image.
type Options struct {
// Size sets the image width and height in pixels.
......@@ -15,13 +31,17 @@ type Options struct {
Light color.RGBA
// Dark holds the color used to draw dark-colored squares.
Dark color.RGBA
// Rotate describes how to transform (typically rotate) the board. (Piece
// images and square labels are not affected by rotation.)
Rotate Rotation
}
// Defaults holds a set of reasonable default options.
var Defaults = Options{
Size: 256,
Light: color.RGBA{255, 206, 158, 255},
Dark: color.RGBA{209, 139, 71, 255},
Size: 256,
Light: color.RGBA{255, 206, 158, 255},
Dark: color.RGBA{209, 139, 71, 255},
Rotate: None,
}
// These constants establish bounds on the rendered image dimensions and the
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment