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, } }