Noah Petherbridge
3c185528f9
Starts the implementation of the chunk-based pixel storage system for levels and drawings. Previously the levels had a Pixels structure which was just an array of X,Y and palette index triplets. The new chunk system divides the map up into square chunks, and lets each chunk manage its own memory layout. The "MapAccessor" layout is implemented first which is a map of X,Y coordinates to their Swatches (pointer to an index of the palette). When serialized the MapAccessor maps the "X,Y": "index" similarly to the old Pixels array. The object hierarchy for the chunk system is like: * Chunker: the manager of the chunks who keeps track of the ChunkSize and a map of "chunk coordinates" to the chunk in charge of it. * Chunk: a part of the drawing ChunkSize length square. A chunk has a Type (of how it stores its data, 0 being a map[Point]Swatch and 1 being a [][]Swatch 2D array), and the chunk has an Accessor which implements the underlying type. * Accessor: an interface for a Chunk to provide access to its pixels. * MapAccessor: a "sparse map" of coordinates to their Swatches. * GridAccessor: TBD, will be a "dense" 2D grid of Swatches. The JSON files are loaded in two passes: 1. The chunks only load their swatch indexes from disk. 2. With the palette also loaded, the chunks are "inflated" and linked to their swatch pointers. Misc changes: * The `level.Canvas` UI widget switches from the old Grid data type to being able to directly use a `level.Chunker` * The Chunker is a shared data type between the on-disk level format and the actual renderer (level.Canvas), so saving the level is easy because you can just pull the Chunker out from the canvas. * ChunkSize is stored inside the level file and the default value is at balance/numbers.go: 1000
170 lines
3.9 KiB
Go
170 lines
3.9 KiB
Go
package render
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
|
|
"git.kirsle.net/apps/doodle/events"
|
|
)
|
|
|
|
// Engine is the interface for the rendering engine, keeping SDL-specific stuff
|
|
// far away from the core of Doodle.
|
|
type Engine interface {
|
|
Setup() error
|
|
|
|
// Poll for events like keypresses and mouse clicks.
|
|
Poll() (*events.State, error)
|
|
GetTicks() uint32
|
|
|
|
// Present presents the current state to the screen.
|
|
Present() error
|
|
|
|
// Clear the full canvas and set this color.
|
|
Clear(Color)
|
|
DrawPoint(Color, Point)
|
|
DrawLine(Color, Point, Point)
|
|
DrawRect(Color, Rect)
|
|
DrawBox(Color, Rect)
|
|
DrawText(Text, Point) error
|
|
ComputeTextRect(Text) (Rect, error)
|
|
|
|
// Delay for a moment using the render engine's delay method,
|
|
// implemented by sdl.Delay(uint32)
|
|
Delay(uint32)
|
|
|
|
// Tasks that the Setup function should defer until tear-down.
|
|
Teardown()
|
|
|
|
Loop() error // maybe?
|
|
}
|
|
|
|
// Rect has a coordinate and a width and height.
|
|
type Rect struct {
|
|
X int32
|
|
Y int32
|
|
W int32
|
|
H int32
|
|
}
|
|
|
|
// NewRect creates a rectangle of size `width` and `height`. The X,Y values
|
|
// are initialized to zero.
|
|
func NewRect(width, height int32) Rect {
|
|
return Rect{
|
|
W: width,
|
|
H: height,
|
|
}
|
|
}
|
|
|
|
func (r Rect) String() string {
|
|
return fmt.Sprintf("Rect<%d,%d,%d,%d>",
|
|
r.X, r.Y, r.W, r.H,
|
|
)
|
|
}
|
|
|
|
// Point returns the rectangle's X,Y values as a Point.
|
|
func (r Rect) Point() Point {
|
|
return Point{
|
|
X: r.X,
|
|
Y: r.Y,
|
|
}
|
|
}
|
|
|
|
// Bigger returns if the given rect is larger than the current one.
|
|
func (r Rect) Bigger(other Rect) bool {
|
|
// TODO: don't know why this is !
|
|
return !(other.X < r.X || // Lefter
|
|
other.Y < r.Y || // Higher
|
|
other.W > r.W || // Wider
|
|
other.H > r.H) // Taller
|
|
}
|
|
|
|
// IsZero returns if the Rect is uninitialized.
|
|
func (r Rect) IsZero() bool {
|
|
return r.X == 0 && r.Y == 0 && r.W == 0 && r.H == 0
|
|
}
|
|
|
|
// Text holds information for drawing text.
|
|
type Text struct {
|
|
Text string
|
|
Size int
|
|
Color Color
|
|
Padding int32
|
|
PadX int32
|
|
PadY int32
|
|
Stroke Color // Stroke color (if not zero)
|
|
Shadow Color // Drop shadow color (if not zero)
|
|
FontFilename string // Path to *.ttf file on disk
|
|
}
|
|
|
|
func (t Text) String() string {
|
|
return fmt.Sprintf(`Text<"%s" %dpx %s>`, t.Text, t.Size, t.Color)
|
|
}
|
|
|
|
// IsZero returns if the Text is the zero value.
|
|
func (t Text) IsZero() bool {
|
|
return t.Text == "" && t.Size == 0 && t.Color == Invisible && t.Padding == 0 && t.Stroke == Invisible && t.Shadow == Invisible
|
|
}
|
|
|
|
// Common color names.
|
|
var (
|
|
Invisible = Color{}
|
|
White = RGBA(255, 255, 255, 255)
|
|
Grey = RGBA(153, 153, 153, 255)
|
|
Black = RGBA(0, 0, 0, 255)
|
|
SkyBlue = RGBA(0, 153, 255, 255)
|
|
Blue = RGBA(0, 0, 255, 255)
|
|
DarkBlue = RGBA(0, 0, 153, 255)
|
|
Red = RGBA(255, 0, 0, 255)
|
|
DarkRed = RGBA(153, 0, 0, 255)
|
|
Green = RGBA(0, 255, 0, 255)
|
|
DarkGreen = RGBA(0, 153, 0, 255)
|
|
Cyan = RGBA(0, 255, 255, 255)
|
|
DarkCyan = RGBA(0, 153, 153, 255)
|
|
Yellow = RGBA(255, 255, 0, 255)
|
|
DarkYellow = RGBA(153, 153, 0, 255)
|
|
Magenta = RGBA(255, 0, 255, 255)
|
|
Purple = RGBA(153, 0, 153, 255)
|
|
Pink = RGBA(255, 153, 255, 255)
|
|
)
|
|
|
|
// IterLine is a generator that returns the X,Y coordinates to draw a line.
|
|
// https://en.wikipedia.org/wiki/Digital_differential_analyzer_(graphics_algorithm)
|
|
func IterLine(x1, y1, x2, y2 int32) chan Point {
|
|
generator := make(chan Point)
|
|
|
|
go func() {
|
|
var (
|
|
dx = float64(x2 - x1)
|
|
dy = float64(y2 - y1)
|
|
)
|
|
var step float64
|
|
if math.Abs(dx) >= math.Abs(dy) {
|
|
step = math.Abs(dx)
|
|
} else {
|
|
step = math.Abs(dy)
|
|
}
|
|
|
|
dx = dx / step
|
|
dy = dy / step
|
|
x := float64(x1)
|
|
y := float64(y1)
|
|
for i := 0; i <= int(step); i++ {
|
|
generator <- Point{
|
|
X: int32(x),
|
|
Y: int32(y),
|
|
}
|
|
x += dx
|
|
y += dy
|
|
}
|
|
|
|
close(generator)
|
|
}()
|
|
|
|
return generator
|
|
}
|
|
|
|
// IterLine2 works with two Points rather than four coordinates.
|
|
func IterLine2(p1 Point, p2 Point) chan Point {
|
|
return IterLine(p1.X, p1.Y, p2.X, p2.Y)
|
|
}
|