90 lines
2.1 KiB
Go
90 lines
2.1 KiB
Go
|
package level
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
|
||
|
"git.kirsle.net/apps/doodle/render"
|
||
|
)
|
||
|
|
||
|
// Types of chunks.
|
||
|
const (
|
||
|
MapType int = iota
|
||
|
GridType
|
||
|
)
|
||
|
|
||
|
// Chunk holds a single portion of the pixel canvas.
|
||
|
type Chunk struct {
|
||
|
Type int // map vs. 2D array.
|
||
|
Accessor
|
||
|
}
|
||
|
|
||
|
// JSONChunk holds a lightweight (interface-free) copy of the Chunk for
|
||
|
// unmarshalling JSON files from disk.
|
||
|
type JSONChunk struct {
|
||
|
Type int `json:"type"`
|
||
|
Data json.RawMessage `json:"data"`
|
||
|
}
|
||
|
|
||
|
// Accessor provides a high-level API to interact with absolute pixel coordinates
|
||
|
// while abstracting away the details of how they're stored.
|
||
|
type Accessor interface {
|
||
|
Inflate(*Palette) error
|
||
|
Iter() <-chan Pixel
|
||
|
IterViewport(viewport render.Rect) <-chan Pixel
|
||
|
Get(render.Point) (*Swatch, error)
|
||
|
Set(render.Point, *Swatch) error
|
||
|
Delete(render.Point) error
|
||
|
Len() int
|
||
|
MarshalJSON() ([]byte, error)
|
||
|
UnmarshalJSON([]byte) error
|
||
|
}
|
||
|
|
||
|
// NewChunk creates a new chunk.
|
||
|
func NewChunk() *Chunk {
|
||
|
return &Chunk{
|
||
|
Type: MapType,
|
||
|
Accessor: NewMapAccessor(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Usage returns the percent of free space vs. allocated pixels in the chunk.
|
||
|
func (c *Chunk) Usage(size int) float64 {
|
||
|
return float64(c.Len()) / float64(size)
|
||
|
}
|
||
|
|
||
|
// MarshalJSON writes the chunk to JSON.
|
||
|
func (c *Chunk) MarshalJSON() ([]byte, error) {
|
||
|
data, err := c.Accessor.MarshalJSON()
|
||
|
if err != nil {
|
||
|
return []byte{}, err
|
||
|
}
|
||
|
|
||
|
generic := &JSONChunk{
|
||
|
Type: c.Type,
|
||
|
Data: data,
|
||
|
}
|
||
|
b, err := json.Marshal(generic)
|
||
|
return b, err
|
||
|
}
|
||
|
|
||
|
// UnmarshalJSON loads the chunk from JSON and uses the correct accessor to
|
||
|
// parse the inner details.
|
||
|
func (c *Chunk) UnmarshalJSON(b []byte) error {
|
||
|
// Parse it generically so we can hand off the inner "data" object to the
|
||
|
// right accessor for unmarshalling.
|
||
|
generic := &JSONChunk{}
|
||
|
err := json.Unmarshal(b, generic)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("Chunk.UnmarshalJSON: failed to unmarshal into generic JSONChunk type: %s", err)
|
||
|
}
|
||
|
|
||
|
switch c.Type {
|
||
|
case MapType:
|
||
|
c.Accessor = NewMapAccessor()
|
||
|
return c.Accessor.UnmarshalJSON(generic.Data)
|
||
|
default:
|
||
|
return fmt.Errorf("Chunk.UnmarshalJSON: unsupported chunk type '%d'", c.Type)
|
||
|
}
|
||
|
}
|