2020-10-01 13:14:36 +02:00
|
|
|
package buf2d
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2022-03-31 12:37:35 +02:00
|
|
|
// Buffer is a 2-dimensional buffer
|
|
|
|
type Buffer[T any] struct {
|
|
|
|
data [][]T
|
|
|
|
width int
|
|
|
|
height int
|
|
|
|
parent *Buffer[T]
|
|
|
|
StringFunc func(T) string
|
2020-10-01 13:14:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewBuffer makes a new buffer with the given dimensions
|
2022-03-31 12:37:35 +02:00
|
|
|
func NewBuffer[T any](width, height int, defaultValue T) *Buffer[T] {
|
|
|
|
b := make([][]T, height)
|
2020-10-01 13:37:58 +02:00
|
|
|
for y := range b {
|
2022-03-31 12:37:35 +02:00
|
|
|
b[y] = make([]T, width)
|
2020-10-01 13:37:58 +02:00
|
|
|
for x := range b[y] {
|
2022-03-31 12:37:35 +02:00
|
|
|
b[y][x] = defaultValue
|
2020-10-01 13:14:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-31 12:37:35 +02:00
|
|
|
return &Buffer[T]{
|
|
|
|
data: b,
|
|
|
|
width: width,
|
|
|
|
height: height,
|
|
|
|
parent: nil,
|
|
|
|
StringFunc: MakeDefaultStringFunc[T](),
|
2020-10-01 13:14:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-31 12:37:35 +02:00
|
|
|
func (b *Buffer[T]) x(x int) int {
|
2020-10-01 15:54:08 +02:00
|
|
|
return limit(x, 0, b.width-1)
|
|
|
|
}
|
|
|
|
|
2022-03-31 12:37:35 +02:00
|
|
|
func (b *Buffer[T]) y(y int) int {
|
2020-10-01 15:54:08 +02:00
|
|
|
return limit(y, 0, b.height-1)
|
|
|
|
}
|
|
|
|
|
2022-03-31 12:37:35 +02:00
|
|
|
// Set sets the value at position (x,y) to c
|
|
|
|
func (b *Buffer[T]) Set(x, y int, v T) {
|
2022-05-04 09:52:46 +02:00
|
|
|
if b.width > 0 && b.height > 0 {
|
2022-05-04 09:49:22 +02:00
|
|
|
b.data[b.y(y)][b.x(x)] = v
|
|
|
|
}
|
2020-10-01 13:14:36 +02:00
|
|
|
}
|
|
|
|
|
2022-03-31 12:37:35 +02:00
|
|
|
// Get returns the value at position (x,y)
|
|
|
|
func (b *Buffer[T]) Get(x, y int) T {
|
2020-10-01 13:37:58 +02:00
|
|
|
return b.data[y][x]
|
2020-10-01 13:14:36 +02:00
|
|
|
}
|
|
|
|
|
2020-10-01 13:56:05 +02:00
|
|
|
// Size returns width and height of b
|
2022-03-31 12:37:35 +02:00
|
|
|
func (b *Buffer[T]) Size() (w, h int) {
|
2020-10-01 13:14:36 +02:00
|
|
|
return b.width, b.height
|
|
|
|
}
|
|
|
|
|
2020-10-01 13:56:05 +02:00
|
|
|
// Width returns the width of b
|
2022-03-31 12:37:35 +02:00
|
|
|
func (b *Buffer[T]) Width() int {
|
2020-10-01 13:14:36 +02:00
|
|
|
return b.width
|
|
|
|
}
|
|
|
|
|
2020-10-01 13:56:05 +02:00
|
|
|
// Height returns the height of b
|
2022-03-31 12:37:35 +02:00
|
|
|
func (b *Buffer[T]) Height() int {
|
2020-10-01 13:14:36 +02:00
|
|
|
return b.height
|
|
|
|
}
|
|
|
|
|
2022-04-02 11:33:59 +02:00
|
|
|
// ForEach calls f for every value in this buffer
|
|
|
|
func (b *Buffer[T]) ForEachLine(f func(line int, content []T)) {
|
|
|
|
for line, content := range b.data {
|
|
|
|
f(line, content)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-31 12:37:35 +02:00
|
|
|
// ForEach calls f for every value in this buffer
|
|
|
|
func (b *Buffer[T]) ForEach(f func(x, y int, v T)) {
|
2020-10-01 13:37:58 +02:00
|
|
|
for y, col := range b.data {
|
2021-01-15 21:40:09 +01:00
|
|
|
for x, v := range col {
|
|
|
|
f(x, y, v)
|
2020-10-01 13:14:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-31 12:37:35 +02:00
|
|
|
func (b *Buffer[T]) String() string {
|
2020-10-01 13:14:36 +02:00
|
|
|
s := new(strings.Builder)
|
2020-10-01 13:37:58 +02:00
|
|
|
for ci, col := range b.data {
|
2021-01-15 21:40:09 +01:00
|
|
|
for _, v := range col {
|
2022-03-31 12:37:35 +02:00
|
|
|
s.WriteString(b.StringFunc(v))
|
2020-10-01 13:14:36 +02:00
|
|
|
}
|
2020-10-01 13:37:58 +02:00
|
|
|
if ci != len(b.data)-1 {
|
2020-10-01 13:14:36 +02:00
|
|
|
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
|
2022-03-31 12:37:35 +02:00
|
|
|
func (b *Buffer[T]) Sub(x, y, w, h int) *Buffer[T] {
|
2020-10-01 13:14:36 +02:00
|
|
|
// sanitize inputs
|
|
|
|
x = limit(x, 0, b.width-1)
|
|
|
|
y = limit(y, 0, b.height-1)
|
2022-04-02 17:33:52 +02:00
|
|
|
w = limit(w, 0, b.width-x)
|
|
|
|
h = limit(h, 0, b.height-y)
|
2020-10-01 13:14:36 +02:00
|
|
|
|
|
|
|
// make slice references
|
2022-03-31 12:37:35 +02:00
|
|
|
data := make([][]T, h)
|
2020-10-01 13:56:05 +02:00
|
|
|
for dy := 0; dy < h; dy++ {
|
|
|
|
col := b.data[y+dy]
|
2020-10-01 13:37:58 +02:00
|
|
|
data[dy] = col[x : x+w]
|
2020-10-01 13:14:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// make buffer
|
2022-03-31 12:37:35 +02:00
|
|
|
return &Buffer[T]{
|
|
|
|
data: data,
|
|
|
|
width: w,
|
|
|
|
height: h,
|
|
|
|
parent: b,
|
|
|
|
StringFunc: b.StringFunc,
|
2020-10-01 13:14:36 +02:00
|
|
|
}
|
|
|
|
}
|