package buf2d import ( "strings" ) // Buffer is a 2-dimensional buffer type Buffer[T any] struct { data [][]T width int height int parent *Buffer[T] StringFunc func(T) string } // NewBuffer makes a new buffer with the given dimensions func NewBuffer[T any](width, height int, defaultValue T) *Buffer[T] { b := make([][]T, height) for y := range b { b[y] = make([]T, width) for x := range b[y] { b[y][x] = defaultValue } } return &Buffer[T]{ data: b, width: width, height: height, parent: nil, StringFunc: MakeDefaultStringFunc[T](), } } func (b *Buffer[T]) x(x int) int { return limit(x, 0, b.width-1) } func (b *Buffer[T]) y(y int) int { return limit(y, 0, b.height-1) } // Set sets the value at position (x,y) to c func (b *Buffer[T]) Set(x, y int, v T) { b.data[b.y(y)][b.x(x)] = v } // Get returns the value at position (x,y) func (b *Buffer[T]) Get(x, y int) T { return b.data[y][x] } // Size returns width and height of b func (b *Buffer[T]) Size() (w, h int) { return b.width, b.height } // Width returns the width of b func (b *Buffer[T]) Width() int { return b.width } // Height returns the height of b func (b *Buffer[T]) Height() int { return b.height } // ForEach calls f for every value in this buffer func (b *Buffer[T]) ForEach(f func(x, y int, v T)) { for y, col := range b.data { for x, v := range col { f(x, y, v) } } } func (b *Buffer[T]) String() string { s := new(strings.Builder) for ci, col := range b.data { for _, v := range col { s.WriteString(b.StringFunc(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[T]) Sub(x, y, w, h int) *Buffer[T] { // 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([][]T, h) for dy := 0; dy < h; dy++ { col := b.data[y+dy] data[dy] = col[x : x+w] } // make buffer return &Buffer[T]{ data: data, width: w, height: h, parent: b, StringFunc: b.StringFunc, } }