package buf2d import ( "strings" ) // Buffer is a 2-dimensional rune buffer type Buffer struct { data [][]rune width int height int parent *Buffer } // NewBuffer makes a new buffer with the given dimensions func NewBuffer(width, height int) *Buffer { b := make([][]rune, height) for y := range b { b[y] = make([]rune, width) for x := range b[y] { b[y][x] = ' ' } } return &Buffer{ data: b, width: width, height: height, parent: nil, } } // Set sets the rune at position (x,y) to c func (b *Buffer) Set(x, y int, c rune) { b.data[y][x] = c } // Get returns the rune at position (x,y) func (b *Buffer) Get(x, y int) rune { 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 } // Draw calls drawFunc for every rune in this buffer func (b *Buffer) Draw(drawFunc func(x, y int, c rune)) { for y, col := range b.data { for x, char := range col { drawFunc(x, y, char) } } } func (b *Buffer) String() string { s := new(strings.Builder) for ci, col := range b.data { for _, char := range col { s.WriteRune(char) } 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([][]rune, 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, } }