buf2d/buffer.go

120 lines
2.4 KiB
Go

package buf2d
import (
"fmt"
"strings"
)
// Buffer is a 2-dimensional rune buffer
type Buffer struct {
data [][]fmt.Stringer
width int
height int
parent *Buffer
}
// NewBuffer makes a new buffer with the given dimensions
func NewBuffer(width, height int) *Buffer {
b := make([][]fmt.Stringer, height)
for y := range b {
b[y] = make([]fmt.Stringer, width)
for x := range b[y] {
b[y][x] = spaceStringer
}
}
return &Buffer{
data: b,
width: width,
height: height,
parent: nil,
}
}
func (b *Buffer) x(x int) int {
return limit(x, 0, b.width-1)
}
func (b *Buffer) y(y int) int {
return limit(y, 0, b.height-1)
}
// Set sets the fmt.Stringer at position (x,y) to c
func (b *Buffer) Set(x, y int, c fmt.Stringer) {
b.data[b.y(y)][b.x(x)] = c
}
// SetRune sets the fmt.Stringer at position (x,y) to rn
func (b *Buffer) SetRune(x, y int, rn rune) {
b.data[b.y(y)][b.x(x)] = newStringerFromRune(rn)
}
// Get returns the fmt.Stringer at position (x,y)
func (b *Buffer) Get(x, y int) fmt.Stringer {
return b.data[y][x]
}
// Size returns width and height of b
func (b *Buffer) Size() (w, h int) {
return b.width, b.height
}
// Width returns the width of b
func (b *Buffer) Width() int {
return b.width
}
// Height returns the height of b
func (b *Buffer) Height() int {
return b.height
}
// ForEach calls f for every rune in this buffer
func (b *Buffer) ForEach(f func(x, y int, c fmt.Stringer)) {
for y, col := range b.data {
for x, v := range col {
f(x, y, v)
}
}
}
func (b *Buffer) String() string {
s := new(strings.Builder)
for ci, col := range b.data {
for _, v := range col {
s.WriteString(fmt.Sprintf("%v", v))
}
if ci != len(b.data)-1 {
s.WriteRune('\n')
}
}
return s.String()
}
// Sub returns a buffer which is completely contained in this buffer
// Modifying the main buffer or the sub buffer will modify the other one as well
// This method can be used recursively
// If the given dimensions don't fit in the parent buffer, it will be truncated
func (b *Buffer) Sub(x, y, w, h int) *Buffer {
// sanitize inputs
x = limit(x, 0, b.width-1)
y = limit(y, 0, b.height-1)
w = limit(w, 1, b.width-x)
h = limit(h, 1, b.height-y)
// make slice references
data := make([][]fmt.Stringer, h)
for dy := 0; dy < h; dy++ {
col := b.data[y+dy]
data[dy] = col[x : x+w]
}
// make buffer
return &Buffer{
data: data,
width: w,
height: h,
parent: b,
}
}