Cut lib/render into its own package, change all imports

This commit is contained in:
Noah 2019-12-22 18:21:58 -08:00
parent 2adffd56c6
commit ea0b41a781
97 changed files with 90 additions and 2398 deletions

View File

@ -11,7 +11,7 @@ import (
"image/png" "image/png"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/branding" "git.kirsle.net/apps/doodle/pkg/branding"
"git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/doodads"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"

View File

@ -3,7 +3,7 @@ package commands
import ( import (
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"
"github.com/urfave/cli" "github.com/urfave/cli"

View File

@ -8,7 +8,7 @@ import (
"sort" "sort"
"time" "time"
"git.kirsle.net/apps/doodle/lib/render/sdl" "git.kirsle.net/go/render/sdl"
doodle "git.kirsle.net/apps/doodle/pkg" doodle "git.kirsle.net/apps/doodle/pkg"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/bindata" "git.kirsle.net/apps/doodle/pkg/bindata"

View File

@ -16,12 +16,12 @@ function main() {
Vx = 0; Vx = 0;
Vy = 0; Vy = 0;
if (ev.Right.Now) { if (ev.Right) {
if (!Self.IsAnimating()) { if (!Self.IsAnimating()) {
Self.PlayAnimation("walk-right", null); Self.PlayAnimation("walk-right", null);
} }
Vx = playerSpeed; Vx = playerSpeed;
} else if (ev.Left.Now) { } else if (ev.Left) {
if (!Self.IsAnimating()) { if (!Self.IsAnimating()) {
Self.PlayAnimation("walk-left", null); Self.PlayAnimation("walk-left", null);
} }

View File

@ -3,7 +3,7 @@ package draw
import ( import (
"math" "math"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
// Line is a generator that returns the X,Y coordinates to draw a line. // Line is a generator that returns the X,Y coordinates to draw a line.

View File

@ -5,7 +5,7 @@ import (
"testing" "testing"
"git.kirsle.net/apps/doodle/lib/draw" "git.kirsle.net/apps/doodle/lib/draw"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
func TestLine(t *testing.T) { func TestLine(t *testing.T) {

View File

@ -1,7 +0,0 @@
# Render: Go Graphics Library
Render is a graphics library written in Go which targets desktop applications on
Windows, MacOS and Linux as well as WebAssembly to run in the browser.
For desktop systems it uses SDL2 under the hood, and in WebAssembly it interacts
with an HTML Canvas element for drawing.

View File

@ -1,39 +0,0 @@
package canvas
import (
"syscall/js"
)
// Canvas represents an HTML5 Canvas object.
type Canvas struct {
Value js.Value
ctx2d js.Value
}
// GetCanvas gets an HTML5 Canvas object from the DOM.
func GetCanvas(id string) Canvas {
canvasEl := js.Global().Get("document").Call("getElementById", id)
canvas2d := canvasEl.Call("getContext", "2d")
c := Canvas{
Value: canvasEl,
ctx2d: canvas2d,
}
canvasEl.Set("width", c.ClientW())
canvasEl.Set("height", c.ClientH())
return c
}
// ClientW returns the client width.
func (c Canvas) ClientW() int {
return js.Global().Get("window").Get("innerWidth").Int()
// return c.Value.Get("clientWidth").Int()
}
// ClientH returns the client height.
func (c Canvas) ClientH() int {
return js.Global().Get("window").Get("innerHeight").Int()
// return c.Value.Get("clientHeight").Int()
}

View File

@ -1,77 +0,0 @@
package canvas
import (
"fmt"
"syscall/js"
"git.kirsle.net/apps/doodle/lib/render"
)
// Methods here implement the drawing functions of the render.Engine
// RGBA turns a color into CSS RGBA string.
func RGBA(c render.Color) string {
return fmt.Sprintf("rgba(%d,%d,%d,%f)",
c.Red,
c.Green,
c.Blue,
float64(c.Alpha)/255,
)
}
// Clear the canvas to a certain color.
func (e *Engine) Clear(color render.Color) {
e.canvas.ctx2d.Set("fillStyle", RGBA(color))
e.canvas.ctx2d.Call("fillRect", 0, 0, e.width, e.height)
}
// SetTitle sets the window title.
func (e *Engine) SetTitle(title string) {
js.Global().Get("document").Set("title", title)
}
// DrawPoint draws a pixel.
func (e *Engine) DrawPoint(color render.Color, point render.Point) {
e.canvas.ctx2d.Set("fillStyle", RGBA(color))
e.canvas.ctx2d.Call("fillRect",
int(point.X),
int(point.Y),
1,
1,
)
}
// DrawLine draws a line between two points.
func (e *Engine) DrawLine(color render.Color, a, b render.Point) {
e.canvas.ctx2d.Set("fillStyle", RGBA(color))
for pt := range render.IterLine(a, b) {
e.canvas.ctx2d.Call("fillRect",
int(pt.X),
int(pt.Y),
1,
1,
)
}
}
// DrawRect draws a rectangle.
func (e *Engine) DrawRect(color render.Color, rect render.Rect) {
e.canvas.ctx2d.Set("strokeStyle", RGBA(color))
e.canvas.ctx2d.Call("strokeRect",
int(rect.X),
int(rect.Y),
int(rect.W),
int(rect.H),
)
}
// DrawBox draws a filled rectangle.
func (e *Engine) DrawBox(color render.Color, rect render.Rect) {
e.canvas.ctx2d.Set("fillStyle", RGBA(color))
e.canvas.ctx2d.Call("fillRect",
int(rect.X),
int(rect.Y),
int(rect.W),
int(rect.H),
)
}

View File

@ -1,86 +0,0 @@
package canvas
import (
"syscall/js"
"time"
"git.kirsle.net/apps/doodle/lib/render/event"
)
// Engine implements a rendering engine targeting an HTML canvas for
// WebAssembly targets.
type Engine struct {
canvas Canvas
startTime time.Time
width int
height int
ticks uint32
// Private fields.
events *event.State
running bool
textures map[string]*Texture // cached texture PNG images
// Event channel. WASM subscribes to events asynchronously using the
// JavaScript APIs, whereas SDL2 polls the event queue which orders them
// all up for processing. This channel will order and queue the events.
queue chan Event
}
// New creates the Canvas Engine.
func New(canvasID string) (*Engine, error) {
canvas := GetCanvas(canvasID)
engine := &Engine{
canvas: canvas,
startTime: time.Now(),
events: event.NewState(),
width: canvas.ClientW(),
height: canvas.ClientH(),
queue: make(chan Event, 1024),
textures: map[string]*Texture{},
}
return engine, nil
}
// WindowSize returns the size of the canvas window.
func (e *Engine) WindowSize() (w, h int) {
// Good time to recompute it first?
var (
window = js.Global().Get("window")
width = window.Get("innerWidth").Int()
height = window.Get("innerHeight").Int()
)
e.canvas.Value.Set("width", width)
e.canvas.Value.Set("height", height)
return e.canvas.ClientW(), e.canvas.ClientH()
}
// GetTicks returns the number of milliseconds since the engine started.
func (e *Engine) GetTicks() uint32 {
ms := time.Since(e.startTime) * time.Millisecond
return uint32(ms)
}
// TO BE IMPLEMENTED...
func (e *Engine) Setup() error {
return nil
}
func (e *Engine) Present() error {
return nil
}
// Delay for a moment.
func (e *Engine) Delay(delay uint32) {
time.Sleep(time.Duration(delay) * time.Millisecond)
}
// Teardown tasks.
func (e *Engine) Teardown() {}
func (e *Engine) Loop() error {
return nil
}

View File

@ -1,216 +0,0 @@
package canvas
import (
"syscall/js"
"git.kirsle.net/apps/doodle/lib/render/event"
)
// EventClass to categorize JavaScript events.
type EventClass int
// EventClass values.
const (
MouseEvent EventClass = iota
ClickEvent
KeyEvent
ResizeEvent
WindowEvent
)
// Event object queues up asynchronous JavaScript events to be processed linearly.
type Event struct {
Name string // mouseup, keydown, etc.
Class EventClass
// Mouse events.
X int
Y int
LeftClick bool
RightClick bool
// Key events.
KeyName string
State bool
Repeat bool
}
// AddEventListeners sets up bindings to collect events from the browser.
func (e *Engine) AddEventListeners() {
// Window resize.
js.Global().Get("window").Call(
"addEventListener",
"resize",
js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e.queue <- Event{
Name: "resize",
Class: WindowEvent,
}
return nil
}),
)
// Mouse movement.
e.canvas.Value.Call(
"addEventListener",
"mousemove",
js.FuncOf(func(this js.Value, args []js.Value) interface{} {
var (
x = args[0].Get("pageX").Int()
y = args[0].Get("pageY").Int()
)
e.queue <- Event{
Name: "mousemove",
Class: MouseEvent,
X: x,
Y: y,
}
return nil
}),
)
// Mouse clicks.
for _, ev := range []string{"mouseup", "mousedown"} {
ev := ev
e.canvas.Value.Call(
"addEventListener",
ev,
js.FuncOf(func(this js.Value, args []js.Value) interface{} {
var (
x = args[0].Get("pageX").Int()
y = args[0].Get("pageY").Int()
which = args[0].Get("which").Int()
)
// Is a mouse button pressed down?
checkDown := func(number int) bool {
if which == number {
return ev == "mousedown"
}
return false
}
e.queue <- Event{
Name: ev,
Class: ClickEvent,
X: x,
Y: y,
LeftClick: checkDown(1),
RightClick: checkDown(3),
}
return false
}),
)
}
// Supress context menu.
e.canvas.Value.Call(
"addEventListener",
"contextmenu",
js.FuncOf(func(this js.Value, args []js.Value) interface{} {
args[0].Call("preventDefault")
return false
}),
)
// Keyboard keys
for _, ev := range []string{"keydown", "keyup"} {
ev := ev
js.Global().Get("document").Call(
"addEventListener",
ev,
js.FuncOf(func(this js.Value, args []js.Value) interface{} {
var (
event = args[0]
key = event.Get("key").String()
repeat = event.Get("repeat").Bool()
pressed = ev == "keydown"
)
if key == "F3" {
args[0].Call("preventDefault")
}
e.queue <- Event{
Name: ev,
Class: KeyEvent,
KeyName: key,
Repeat: repeat,
State: pressed,
}
return nil
}),
)
}
}
// PollEvent returns the next event in the queue, or null.
func (e *Engine) PollEvent() *Event {
select {
case event := <-e.queue:
return &event
default:
return nil
}
return nil
}
// Poll for events.
func (e *Engine) Poll() (*event.State, error) {
s := e.events
for event := e.PollEvent(); event != nil; event = e.PollEvent() {
switch event.Class {
case WindowEvent:
s.WindowResized = true
case MouseEvent:
s.CursorX = event.X
s.CursorY = event.Y
case ClickEvent:
s.CursorX = event.X
s.CursorY = event.Y
s.Button1 = event.LeftClick
s.Button2 = event.RightClick
case KeyEvent:
switch event.KeyName {
case "Escape":
if event.Repeat {
continue
}
s.Escape = event.State
case "Enter":
if event.Repeat {
continue
}
s.Enter = event.State
case "F3":
s.SetKeyDown("F3", event.State)
case "ArrowUp":
s.Up = event.State
case "ArrowLeft":
s.Left = event.State
case "ArrowRight":
s.Right = event.State
case "ArrowDown":
s.Down = event.State
case "Shift":
s.Shift = event.State
continue
case "Alt":
s.Alt = event.State
case "Control":
s.Ctrl = event.State
case "Backspace":
s.SetKeyDown(`\b`, event.State)
default:
s.SetKeyDown(event.KeyName, event.State)
}
}
}
return e.events, nil
}

View File

@ -1,88 +0,0 @@
package canvas
// Text rendering functions using the HTML 5 canvas.
import (
"fmt"
"path/filepath"
"strings"
"git.kirsle.net/apps/doodle/lib/render"
)
// FontFilenameToName converts a FontFilename to its CSS font name.
//
// The CSS font name is set to the base of the filename, without the .ttf
// file extension. For example, "fonts/DejaVuSans.ttf" uses the CSS font
// family name "DejaVuSans" and that's what this function returns.
//
// Fonts must be defined in the index.html style sheet when serving the
// wasm build of Doodle.
//
// If filename is "", returns "serif" as a sensible default.
func FontFilenameToName(filename string) string {
if filename == "" {
return "DejaVuSans,serif"
}
return strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename))
}
// DrawText draws text on the canvas.
func (e *Engine) DrawText(text render.Text, point render.Point) error {
font := FontFilenameToName(text.FontFilename)
e.canvas.ctx2d.Set("font",
fmt.Sprintf("%dpx %s,serif", text.Size, font),
)
e.canvas.ctx2d.Set("textBaseline", "top")
write := func(dx, dy int, color render.Color) {
e.canvas.ctx2d.Set("fillStyle", color.ToHex())
e.canvas.ctx2d.Call("fillText",
text.Text,
int(point.X)+dx,
int(point.Y)+dy,
)
}
// Does the text have a stroke around it?
if text.Stroke != render.Invisible {
e.canvas.ctx2d.Set("fillStyle", text.Stroke.ToHex())
write(-1, -1, text.Stroke)
write(-1, 0, text.Stroke)
write(-1, 1, text.Stroke)
write(1, -1, text.Stroke)
write(1, 0, text.Stroke)
write(1, 1, text.Stroke)
write(0, -1, text.Stroke)
write(0, 1, text.Stroke)
}
// Does it have a drop shadow?
if text.Shadow != render.Invisible {
write(1, 1, text.Shadow)
}
// Draw the text itself.
write(0, 0, text.Color)
return nil
}
// ComputeTextRect computes and returns a Rect for how large the text would
// appear if rendered.
func (e *Engine) ComputeTextRect(text render.Text) (render.Rect, error) {
font := FontFilenameToName(text.FontFilename)
e.canvas.ctx2d.Set("font",
fmt.Sprintf("%dpx %s,serif", text.Size, font),
)
measure := e.canvas.ctx2d.Call("measureText", text.Text)
rect := render.Rect{
// TODO: the only TextMetrics widely supported in browsers is
// the width. For height, use the text size for now.
W: int32(measure.Get("width").Int()),
H: int32(text.Size),
}
return rect, nil
}

View File

@ -1,91 +0,0 @@
package canvas
import (
"bytes"
"encoding/base64"
"errors"
"image"
"image/png"
"syscall/js"
"git.kirsle.net/apps/doodle/lib/render"
)
// Texture can hold on to cached image textures.
type Texture struct {
data string // data:image/png URI
image js.Value // DOM image element
canvas js.Value // Warmed up canvas element
ctx2d js.Value // 2D drawing context for the canvas.
width int
height int
}
// StoreTexture caches a texture from a bitmap.
func (e *Engine) StoreTexture(name string, img image.Image) (render.Texturer, error) {
var (
fh = bytes.NewBuffer([]byte{})
imageSize = img.Bounds().Size()
width = imageSize.X
height = imageSize.Y
)
// Encode to PNG format.
if err := png.Encode(fh, img); err != nil {
return nil, err
}
var dataURI = "data:image/png;base64," + base64.StdEncoding.EncodeToString(fh.Bytes())
tex := &Texture{
data: dataURI,
width: width,
height: height,
}
// Preheat a cached Canvas object.
canvas := js.Global().Get("document").Call("createElement", "canvas")
canvas.Set("width", width)
canvas.Set("height", height)
tex.canvas = canvas
ctx2d := canvas.Call("getContext", "2d")
tex.ctx2d = ctx2d
// Load as a JS Image object.
image := js.Global().Call("eval", "new Image()")
image.Call("addEventListener", "load", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
ctx2d.Call("drawImage", image, 0, 0)
return nil
}))
image.Set("src", tex.data)
tex.image = image
// Cache the texture in memory.
e.textures[name] = tex
return tex, nil
}
// Size returns the dimensions of the texture.
func (t *Texture) Size() render.Rect {
return render.NewRect(int32(t.width), int32(t.height))
}
// LoadTexture recalls a cached texture image.
func (e *Engine) LoadTexture(name string) (render.Texturer, error) {
if tex, ok := e.textures[name]; ok {
return tex, nil
}
return nil, errors.New("no bitmap data stored for " + name)
}
// Copy a texturer bitmap onto the canvas.
func (e *Engine) Copy(t render.Texturer, src, dist render.Rect) {
tex := t.(*Texture)
// e.canvas.ctx2d.Call("drawImage", tex.image, dist.X, dist.Y)
e.canvas.ctx2d.Call("drawImage", tex.canvas, dist.X, dist.Y)
}

View File

@ -1,226 +0,0 @@
package render
import (
"encoding/json"
"errors"
"fmt"
"image/color"
"regexp"
"strconv"
)
var (
// Regexps to parse hex color codes. Three formats are supported:
// * reHexColor3 uses only 3 hex characters, like #F90
// * reHexColor6 uses standard 6 characters, like #FF9900
// * reHexColor8 is the standard 6 plus alpha channel, like #FF9900FF
reHexColor3 = regexp.MustCompile(`^([A-Fa-f0-9])([A-Fa-f0-9])([A-Fa-f0-9])$`)
reHexColor6 = regexp.MustCompile(`^([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})$`)
reHexColor8 = regexp.MustCompile(`^([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})$`)
)
// Color holds an RGBA color value.
type Color struct {
Red uint8
Green uint8
Blue uint8
Alpha uint8
}
// RGBA creates a new Color.
func RGBA(r, g, b, a uint8) Color {
return Color{
Red: r,
Green: g,
Blue: b,
Alpha: a,
}
}
// FromColor creates a render.Color from a Go color.Color
func FromColor(from color.Color) Color {
// downscale a 16-bit color value to 8-bit. input range 0x0000..0xffff
downscale := func(in uint32) uint8 {
var scale = float64(in) / 0xffff
return uint8(scale * 0xff)
}
r, g, b, a := from.RGBA()
return RGBA(
downscale(r),
downscale(g),
downscale(b),
downscale(a),
)
}
// MustHexColor parses a color from hex code or panics.
func MustHexColor(hex string) Color {
color, err := HexColor(hex)
if err != nil {
panic(err)
}
return color
}
// HexColor parses a color from hexadecimal code.
func HexColor(hex string) (Color, error) {
c := Black // default color
if len(hex) > 0 && hex[0] == '#' {
hex = hex[1:]
}
var m []string
if len(hex) == 3 {
m = reHexColor3.FindStringSubmatch(hex)
} else if len(hex) == 6 {
m = reHexColor6.FindStringSubmatch(hex)
} else if len(hex) == 8 {
m = reHexColor8.FindStringSubmatch(hex)
} else {
return c, errors.New("not a valid length for color code; only 3, 6 and 8 supported")
}
// Any luck?
if m == nil {
return c, errors.New("not a valid hex color code")
}
// Parse the color values. 16=base, 8=bit size
red, _ := strconv.ParseUint(m[1], 16, 8)
green, _ := strconv.ParseUint(m[2], 16, 8)
blue, _ := strconv.ParseUint(m[3], 16, 8)
// Alpha channel available?
var alpha uint64 = 255
if len(m) == 5 {
alpha, _ = strconv.ParseUint(m[4], 16, 8)
}
c.Red = uint8(red)
c.Green = uint8(green)
c.Blue = uint8(blue)
c.Alpha = uint8(alpha)
return c, nil
}
func (c Color) String() string {
return fmt.Sprintf(
"Color<#%02x%02x%02x+%02x>",
c.Red, c.Green, c.Blue, c.Alpha,
)
}
// ToHex converts a render.Color to standard #RRGGBB hexadecimal format.
func (c Color) ToHex() string {
return fmt.Sprintf(
"#%02x%02x%02x",
c.Red, c.Green, c.Blue,
)
}
// ToColor converts a render.Color into a Go standard color.Color
func (c Color) ToColor() color.RGBA {
return color.RGBA{
R: c.Red,
G: c.Green,
B: c.Blue,
A: c.Alpha,
}
}
// Transparent returns whether the alpha channel is zeroed out and the pixel
// won't appear as anything when rendered.
func (c Color) Transparent() bool {
return c.Alpha == 0x00
}
// MarshalJSON serializes the Color for JSON.
func (c Color) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(
`"#%02x%02x%02x"`,
c.Red, c.Green, c.Blue,
)), nil
}
// UnmarshalJSON reloads the Color from JSON.
func (c *Color) UnmarshalJSON(b []byte) error {
var hex string
err := json.Unmarshal(b, &hex)
if err != nil {
return err
}
parsed, err := HexColor(hex)
if err != nil {
return err
}
c.Red = parsed.Red
c.Blue = parsed.Blue
c.Green = parsed.Green
c.Alpha = parsed.Alpha
return nil
}
// IsZero returns if the color is all zeroes (invisible).
func (c Color) IsZero() bool {
return c.Red+c.Green+c.Blue+c.Alpha == 0
}
// Add a relative color value to the color.
func (c Color) Add(r, g, b, a int) Color {
var (
R = int(c.Red) + r
G = int(c.Green) + g
B = int(c.Blue) + b
A = int(c.Alpha) + a
)
cap8 := func(v int) uint8 {
if v > 255 {
v = 255
} else if v < 0 {
v = 0
}
return uint8(v)
}
return Color{
Red: cap8(R),
Green: cap8(G),
Blue: cap8(B),
Alpha: cap8(A),
}
}
// AddColor adds another Color to your Color.
func (c Color) AddColor(other Color) Color {
return c.Add(
int(other.Red),
int(other.Green),
int(other.Blue),
int(other.Alpha),
)
}
// Lighten a color value.
func (c Color) Lighten(v int) Color {
return c.Add(v, v, v, 0)
}
// Darken a color value.
func (c Color) Darken(v int) Color {
return c.Add(-v, -v, -v, 0)
}
// Transparentize adjusts the alpha value.
func (c Color) Transparentize(v int) Color {
return c.Add(0, 0, 0, v)
}
// SetAlpha sets the alpha value to a specific setting.
func (c Color) SetAlpha(v uint8) Color {
c.Alpha = v
return c
}

View File

@ -1,64 +0,0 @@
package render
// MidpointEllipse implements an ellipse plotting algorithm.
func MidpointEllipse(center, radius Point) chan Point {
yield := make(chan Point)
go func() {
var (
pos = NewPoint(radius.X, 0)
delta = NewPoint(
2*radius.Y*radius.Y*pos.X,
2*radius.X*radius.X*pos.Y,
)
err = radius.X*radius.X -
radius.Y*radius.Y*radius.X +
(radius.Y*radius.Y)/4
)
for delta.Y < delta.X {
yield <- NewPoint(center.X+pos.X, center.Y+pos.Y)
yield <- NewPoint(center.X+pos.X, center.Y-pos.Y)
yield <- NewPoint(center.X-pos.X, center.Y+pos.Y)
yield <- NewPoint(center.X-pos.X, center.Y-pos.Y)
pos.Y++
if err < 0 {
delta.Y += 2 * radius.X * radius.X
err += delta.Y + radius.X*radius.X
} else {
pos.X--
delta.Y += 2 * radius.X * radius.X
delta.X -= 2 * radius.Y * radius.Y
err += delta.Y - delta.X + radius.X*radius.X
}
}
err = radius.X*radius.X*(pos.Y*pos.Y+pos.Y) +
radius.Y*radius.Y*(pos.X-1)*(pos.X-1) -
radius.Y*radius.Y*radius.X*radius.X
for pos.X >= 0 {
yield <- NewPoint(center.X+pos.X, center.Y+pos.Y)
yield <- NewPoint(center.X+pos.X, center.Y-pos.Y)
yield <- NewPoint(center.X-pos.X, center.Y+pos.Y)
yield <- NewPoint(center.X-pos.X, center.Y-pos.Y)
pos.X--
if err > 0 {
delta.X -= 2 * radius.Y * radius.Y
err += radius.Y*radius.Y - delta.X
} else {
pos.Y++
delta.Y += 2 * radius.X * radius.X
delta.X -= 2 * radius.Y * radius.Y
err += delta.Y - delta.X + radius.Y*radius.Y
}
}
close(yield)
}()
return yield
}

View File

@ -1,109 +0,0 @@
package event
import "strings"
// State holds the current state of key/mouse events.
type State struct {
// Mouse buttons.
Button1 bool // Left
Button2 bool // Middle
Button3 bool // Right
// Special keys
Escape bool
Enter bool
Shift bool
Ctrl bool
Alt bool
Up bool
Left bool
Right bool
Down bool
// Pressed keys.
keydown map[string]interface{}
// Cursor position
CursorX int
CursorY int
// Window resized
WindowResized bool
}
// NewState creates a new event.State.
func NewState() *State {
return &State{
keydown: map[string]interface{}{},
}
}
// SetKeyDown sets that a named key is pressed down.
func (s *State) SetKeyDown(name string, down bool) {
if down {
s.keydown[name] = nil
} else {
delete(s.keydown, name)
}
}
// KeyDown returns whether a named key is currently pressed.
func (s *State) KeyDown(name string) bool {
_, ok := s.keydown[name]
return ok
}
// KeysDown returns a list of all key names currently pressed down.
// Set shifted to True to return the key symbols correctly shifted
// (uppercase, or symbols on number keys, etc.)
func (s *State) KeysDown(shifted bool) []string {
var (
result = make([]string, len(s.keydown))
i = 0
)
for key := range s.keydown {
if shifted && s.Shift {
if symbol, ok := shiftMap[key]; ok {
result[i] = symbol
} else {
result[i] = strings.ToUpper(key)
}
} else {
result[i] = key
}
i++
}
return result
}
// ResetKeyDown clears all key-down states.
func (s *State) ResetKeyDown() {
s.keydown = map[string]interface{}{}
}
// shiftMap maps keys to their Shift versions.
var shiftMap = map[string]string{
"`": "~",
"1": "!",
"2": "@",
"3": "#",
"4": "$",
"5": "%",
"6": "^",
"7": "&",
"8": "*",
"9": "(",
"0": ")",
"-": "_",
"=": "+",
"[": "{",
"]": "}",
`\`: "|",
";": ":",
`'`: `"`,
",": "<",
".": ">",
"/": "?",
}

View File

@ -1,108 +0,0 @@
package render
import (
"fmt"
"regexp"
"strconv"
)
var regexpResolution = regexp.MustCompile(`^(\d+)x(\d+)$`)
// ParseResolution turns a resolution string like "1024x768" and returns the
// width and height values.
func ParseResolution(resi string) (int, int, error) {
m := regexpResolution.FindStringSubmatch(resi)
if m == nil {
return 0, 0, fmt.Errorf("invalid resolution format, should be %s",
regexpResolution.String(),
)
}
width, err := strconv.Atoi(m[1])
if err != nil {
return 0, 0, err
}
height, err := strconv.Atoi(m[2])
if err != nil {
return 0, 0, err
}
return width, height, nil
}
// TrimBox helps with Engine.Copy() to trim a destination box so that it
// won't overflow with the parent container.
func TrimBox(src, dst *Rect, p Point, S Rect, thickness int32) {
// Constrain source width to not bigger than Canvas width.
if src.W > S.W {
src.W = S.W
}
if src.H > S.H {
src.H = S.H
}
// If the destination width will cause it to overflow the widget
// box, trim off the right edge of the destination rect.
//
// Keep in mind we're dealing with chunks here, and a chunk is
// a small part of the image. Example:
// - Canvas is 800x600 (S.W=800 S.H=600)
// - Chunk wants to render at 790,0 width 100,100 or whatever
// dst={790, 0, 100, 100}
// - Chunk box would exceed 800px width (X=790 + W=100 == 890)
// - Find the delta how much it exceeds as negative (800 - 890 == -90)
// - Lower the Source and Dest rects by that delta size so they
// stay proportional and don't scale or anything dumb.
if dst.X+src.W > p.X+S.W {
// NOTE: delta is a negative number,
// so it will subtract from the width.
delta := (p.X + S.W - thickness) - (dst.W + dst.X)
src.W += delta
dst.W += delta
}
if dst.Y+src.H > p.Y+S.H {
// NOTE: delta is a negative number
delta := (p.Y + S.H - thickness) - (dst.H + dst.Y)
src.H += delta
dst.H += delta
}
// The same for the top left edge, so the drawings don't overlap
// menu bars or left side toolbars.
// - Canvas was placed 80px from the left of the screen.
// Canvas.MoveTo(80, 0)
// - A texture wants to draw at 60, 0 which would cause it to
// overlap 20 pixels into the left toolbar. It needs to be cropped.
// - The delta is: p.X=80 - dst.X=60 == 20
// - Set destination X to p.X to constrain it there: 20
// - Subtract the delta from destination W so we don't scale it.
// - Add 20 to X of the source: the left edge of source is not visible
if dst.X < p.X {
// NOTE: delta is a positive number,
// so it will add to the destination coordinates.
delta := p.X - dst.X
dst.X = p.X + thickness
dst.W -= delta
src.X += delta
}
if dst.Y < p.Y {
delta := p.Y - dst.Y
dst.Y = p.Y + thickness
dst.H -= delta
src.Y += delta
}
// Trim the destination width so it doesn't overlap the Canvas border.
if dst.W >= S.W-thickness {
dst.W = S.W - thickness
}
}
// AbsInt32 returns the absolute value of an int32.
func AbsInt32(v int32) int32 {
if v < 0 {
return -v
}
return v
}

View File

@ -1,196 +0,0 @@
package render
import (
"fmt"
"image"
"git.kirsle.net/apps/doodle/lib/render/event"
)
// 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() (*event.State, error)
GetTicks() uint32
WindowSize() (w, h int)
// Present presents the current state to the screen.
Present() error
// Clear the full canvas and set this color.
Clear(Color)
SetTitle(string)
DrawPoint(Color, Point)
DrawLine(Color, Point, Point)
DrawRect(Color, Rect)
DrawBox(Color, Rect)
DrawText(Text, Point) error
ComputeTextRect(Text) (Rect, error)
// Texture caching.
StoreTexture(name string, img image.Image) (Texturer, error)
LoadTexture(name string) (Texturer, error)
Copy(t Texturer, src, dst Rect)
// 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?
}
// Texturer is a stored image texture used by the rendering engine while
// abstracting away its inner workings.
type Texturer interface {
Size() Rect
}
// 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
}
// Intersects with the other rectangle in any way.
func (r Rect) Intersects(other Rect) bool {
// Do a bidirectional compare.
compare := func(a, b Rect) bool {
var corners = []Point{
NewPoint(b.X, b.Y),
NewPoint(b.X, b.Y+b.H),
NewPoint(b.X+b.W, b.Y),
NewPoint(b.X+b.W, b.Y+b.H),
}
for _, pt := range corners {
if pt.Inside(a) {
return true
}
}
return false
}
return compare(r, other) || compare(other, r) || false
}
// 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
}
// Add another rect.
func (r Rect) Add(other Rect) Rect {
return Rect{
X: r.X + other.X,
Y: r.Y + other.Y,
W: r.W + other.W,
H: r.H + other.H,
}
}
// Add a point to move the rect.
func (r Rect) AddPoint(other Point) Rect {
return Rect{
X: r.X + other.X,
Y: r.Y + other.Y,
W: r.W,
H: r.H,
}
}
// SubtractPoint is the inverse of AddPoint. Use this only if you need to invert
// the Point being added.
//
// This does r.X - other.X, r.Y - other.Y and keeps the width/height the same.
func (r Rect) SubtractPoint(other Point) Rect {
return Rect{
X: r.X - other.X,
Y: r.Y - other.Y,
W: r.W,
H: r.H,
}
}
// 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)
Orange = RGBA(255, 153, 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)
)

View File

@ -1,109 +0,0 @@
package render
import (
"fmt"
"strconv"
"strings"
)
// Point holds an X,Y coordinate value.
type Point struct {
X int32
Y int32
}
// Common points.
var (
Origin Point
)
// NewPoint makes a new Point at an X,Y coordinate.
func NewPoint(x, y int32) Point {
return Point{
X: x,
Y: y,
}
}
func (p Point) String() string {
return fmt.Sprintf("%d,%d", p.X, p.Y)
}
// ParsePoint to parse a point from its string representation.
func ParsePoint(v string) (Point, error) {
halves := strings.Split(v, ",")
if len(halves) != 2 {
return Point{}, fmt.Errorf("'%s': not a valid coordinate string", v)
}
x, errX := strconv.Atoi(halves[0])
y, errY := strconv.Atoi(halves[1])
if errX != nil || errY != nil {
return Point{}, fmt.Errorf("invalid coordinate string (X: %v; Y: %v)",
errX,
errY,
)
}
return Point{
X: int32(x),
Y: int32(y),
}, nil
}
// IsZero returns if the point is the zero value.
func (p Point) IsZero() bool {
return p.X == 0 && p.Y == 0
}
// Inside returns whether the Point falls inside the rect.
//
// NOTICE: the W and H are zero-relative, so a 100x100 box at coordinate
// X,Y would still have W,H of 100.
func (p Point) Inside(r Rect) bool {
var (
x1 = r.X
y1 = r.Y
x2 = r.X + r.W
y2 = r.Y + r.H
)
return ((p.X >= x1 && p.X <= x2) &&
(p.Y >= y1 && p.Y <= y2))
}
// Add (or subtract) the other point to your current point.
func (p *Point) Add(other Point) {
p.X += other.X
p.Y += other.Y
}
// Subtract the other point from your current point.
func (p *Point) Subtract(other Point) {
p.X -= other.X
p.Y -= other.Y
}
// MarshalText to convert the point into text so that a render.Point may be used
// as a map key and serialized to JSON.
func (p *Point) MarshalText() ([]byte, error) {
return []byte(fmt.Sprintf("%d,%d", p.X, p.Y)), nil
}
// UnmarshalText to restore it from text.
func (p *Point) UnmarshalText(b []byte) error {
halves := strings.Split(strings.Trim(string(b), `"`), ",")
if len(halves) != 2 {
return fmt.Errorf("'%s': not a valid coordinate string", b)
}
x, errX := strconv.Atoi(halves[0])
y, errY := strconv.Atoi(halves[1])
if errX != nil || errY != nil {
return fmt.Errorf("Point.UnmarshalJSON: Atoi errors (X=%s Y=%s)",
errX,
errY,
)
}
p.X = int32(x)
p.Y = int32(y)
return nil
}

View File

@ -1,60 +0,0 @@
package render_test
import (
"strconv"
"testing"
"git.kirsle.net/apps/doodle/lib/render"
)
func TestPointInside(t *testing.T) {
type testCase struct {
rect render.Rect
p render.Point
shouldPass bool
}
tests := []testCase{
testCase{
rect: render.Rect{
X: 0,
Y: 0,
W: 500,
H: 500,
},
p: render.NewPoint(128, 256),
shouldPass: true,
},
testCase{
rect: render.Rect{
X: 100,
Y: 80,
W: 40,
H: 60,
},
p: render.NewPoint(128, 256),
shouldPass: false,
},
testCase{
// true values when debugging why Doodads weren't
// considered inside the viewport.
rect: render.Rect{
X: 0,
Y: -232,
H: 874,
W: 490,
},
p: render.NewPoint(509, 260),
shouldPass: false,
},
}
for _, test := range tests {
if test.p.Inside(test.rect) != test.shouldPass {
t.Errorf("Failed: %s inside %s should be %s",
test.p,
test.rect,
strconv.FormatBool(test.shouldPass),
)
}
}
}

View File

@ -1,81 +0,0 @@
package render_test
import (
"strconv"
"testing"
"git.kirsle.net/apps/doodle/lib/render"
)
func TestIntersection(t *testing.T) {
newRect := func(x, y, w, h int) render.Rect {
return render.Rect{
X: int32(x),
Y: int32(y),
W: int32(w),
H: int32(h),
}
}
type TestCase struct {
A render.Rect
B render.Rect
Expect bool
}
var tests = []TestCase{
{
A: newRect(0, 0, 1000, 1000),
B: newRect(200, 200, 100, 100),
Expect: true,
},
{
A: newRect(200, 200, 100, 100),
B: newRect(0, 0, 1000, 1000),
Expect: true,
},
{
A: newRect(0, 0, 100, 100),
B: newRect(100, 0, 100, 100),
Expect: true,
},
{
A: newRect(0, 0, 99, 99),
B: newRect(100, 0, 99, 99),
Expect: false,
},
{
// Real coords of a test doodad!
A: newRect(183, 256, 283, 356),
B: newRect(0, -232, 874, 490),
Expect: true,
},
{
A: newRect(183, 256, 283, 356),
B: newRect(0, -240, 874, 490),
Expect: false, // XXX: must be true
},
{
A: newRect(0, 30, 9, 62),
B: newRect(16, 0, 32, 64),
Expect: false,
},
{
A: newRect(0, 30, 11, 62),
B: newRect(7, 4, 17, 28),
Expect: false,
},
}
for _, test := range tests {
actual := test.A.Intersects(test.B)
if actual != test.Expect {
t.Errorf(
"%s collision with %s: expected %s, got %s",
test.A,
test.B,
strconv.FormatBool(test.Expect),
strconv.FormatBool(actual),
)
}
}
}

View File

@ -1,57 +0,0 @@
// Package sdl provides an SDL2 renderer for Doodle.
package sdl
import (
"git.kirsle.net/apps/doodle/lib/render"
"github.com/veandco/go-sdl2/sdl"
)
// Clear the canvas and set this color.
func (r *Renderer) Clear(color render.Color) {
if color != r.lastColor {
r.renderer.SetDrawColor(color.Red, color.Green, color.Blue, color.Alpha)
}
r.renderer.Clear()
}
// DrawPoint puts a color at a pixel.
func (r *Renderer) DrawPoint(color render.Color, point render.Point) {
if color != r.lastColor {
r.renderer.SetDrawColor(color.Red, color.Green, color.Blue, color.Alpha)
}
r.renderer.DrawPoint(point.X, point.Y)
}
// DrawLine draws a line between two points.
func (r *Renderer) DrawLine(color render.Color, a, b render.Point) {
if color != r.lastColor {
r.renderer.SetDrawColor(color.Red, color.Green, color.Blue, color.Alpha)
}
r.renderer.DrawLine(a.X, a.Y, b.X, b.Y)
}
// DrawRect draws a rectangle.
func (r *Renderer) DrawRect(color render.Color, rect render.Rect) {
if color != r.lastColor {
r.renderer.SetDrawColor(color.Red, color.Green, color.Blue, color.Alpha)
}
r.renderer.DrawRect(&sdl.Rect{
X: rect.X,
Y: rect.Y,
W: rect.W,
H: rect.H,
})
}
// DrawBox draws a filled rectangle.
func (r *Renderer) DrawBox(color render.Color, rect render.Rect) {
if color != r.lastColor {
r.renderer.SetDrawColor(color.Red, color.Green, color.Blue, color.Alpha)
}
r.renderer.FillRect(&sdl.Rect{
X: rect.X,
Y: rect.Y,
W: rect.W,
H: rect.H,
})
}

View File

@ -1,176 +0,0 @@
package sdl
import (
"errors"
"fmt"
"git.kirsle.net/apps/doodle/lib/render/event"
"github.com/veandco/go-sdl2/sdl"
)
// Debug certain SDL events
var (
DebugWindowEvents = false
DebugMouseEvents = false
DebugClickEvents = true
DebugKeyEvents = false
)
// Poll for events.
func (r *Renderer) Poll() (*event.State, error) {
s := r.events
// helper function to push keyboard key names on keyDown events only.
pushKey := func(name string, state uint8) {
s.SetKeyDown(name, state == 1)
}
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch t := event.(type) {
case *sdl.QuitEvent:
return s, errors.New("quit")
case *sdl.WindowEvent:
if DebugWindowEvents {
if t.Event == sdl.WINDOWEVENT_RESIZED {
fmt.Printf("[%d ms] tick:%d Window Resized to %dx%d",
t.Timestamp,
r.ticks,
t.Data1,
t.Data2,
)
}
}
if t.Event == sdl.WINDOWEVENT_RESIZED {
s.WindowResized = true
}
case *sdl.MouseMotionEvent:
if DebugMouseEvents {
fmt.Printf("[%d ms] tick:%d MouseMotion type:%d id:%d x:%d y:%d xrel:%d yrel:%d",
t.Timestamp, r.ticks, t.Type, t.Which, t.X, t.Y, t.XRel, t.YRel,
)
}
// Push the cursor position.
s.CursorX = int(t.X)
s.CursorY = int(t.Y)
case *sdl.MouseButtonEvent:
if DebugClickEvents {
fmt.Printf("[%d ms] tick:%d MouseButton type:%d id:%d x:%d y:%d button:%d state:%d\n",
t.Timestamp, r.ticks, t.Type, t.Which, t.X, t.Y, t.Button, t.State,
)
}
// Push the cursor position.
s.CursorX = int(t.X)
s.CursorY = int(t.Y)
// Store the clicked state of the mouse button.
if t.Button == 1 {
s.Button1 = t.State == 1
} else if t.Button == 2 {
s.Button2 = t.State == 1
} else if t.Button == 3 {
s.Button3 = t.State == 1
}
//
// // Is a mouse button pressed down?
// checkDown := func(number uint8, target *events.BoolTick) bool {
// if t.Button == number {
// var eventName string
// if t.State == 1 && target.Now == false {
// eventName = "DOWN"
// } else if t.State == 0 && target.Now == true {
// eventName = "UP"
// }
//
// if eventName != "" {
// target.Push(eventName == "DOWN")
// }
// return true
// }
// return false
// }
//
// if checkDown(1, s.Button1) || checkDown(3, s.Button2) || checkDown(2, s.Button3) {
// // Return the event immediately.
// return s, nil
// }
case *sdl.MouseWheelEvent:
if DebugMouseEvents {
fmt.Printf("[%d ms] tick:%d MouseWheel type:%d id:%d x:%d y:%d",
t.Timestamp, r.ticks, t.Type, t.Which, t.X, t.Y,
)
}
case *sdl.KeyboardEvent:
if DebugKeyEvents {
fmt.Printf("[%d ms] tick:%d Keyboard type:%d sym:%c modifiers:%d state:%d repeat:%d\n",
t.Timestamp, r.ticks, t.Type, t.Keysym.Sym, t.Keysym.Mod, t.State, t.Repeat,
)
}
switch t.Keysym.Scancode {
case sdl.SCANCODE_ESCAPE:
if t.Repeat == 1 {
continue
}
s.Escape = t.State == 1
case sdl.SCANCODE_RETURN:
if t.Repeat == 1 {
continue
}
s.Enter = t.State == 1
case sdl.SCANCODE_F1:
pushKey("F1", t.State)
case sdl.SCANCODE_F2:
pushKey("F2", t.State)
case sdl.SCANCODE_F3:
pushKey("F3", t.State)
case sdl.SCANCODE_F4:
pushKey("F4", t.State)
case sdl.SCANCODE_F5:
pushKey("F5", t.State)
case sdl.SCANCODE_F6:
pushKey("F6", t.State)
case sdl.SCANCODE_F7:
pushKey("F7", t.State)
case sdl.SCANCODE_F8:
pushKey("F8", t.State)
case sdl.SCANCODE_F9:
pushKey("F9", t.State)
case sdl.SCANCODE_F10:
pushKey("F10", t.State)
case sdl.SCANCODE_F11:
pushKey("F11", t.State)
case sdl.SCANCODE_F12:
pushKey("F12", t.State)
case sdl.SCANCODE_UP:
s.Up = t.State == 1
case sdl.SCANCODE_LEFT:
s.Left = t.State == 1
case sdl.SCANCODE_RIGHT:
s.Right = t.State == 1
case sdl.SCANCODE_DOWN:
s.Down = t.State == 1
case sdl.SCANCODE_LSHIFT:
case sdl.SCANCODE_RSHIFT:
s.Shift = t.State == 1
case sdl.SCANCODE_LALT:
case sdl.SCANCODE_RALT:
s.Alt = t.State == 1
case sdl.SCANCODE_LCTRL:
s.Ctrl = t.State == 1
case sdl.SCANCODE_RCTRL:
s.Ctrl = t.State == 1
case sdl.SCANCODE_BACKSPACE:
// Make it a key event with "\b" as the sequence.
s.SetKeyDown(`\b`, t.State == 1 || t.Repeat == 1)
default:
// Push the string value of the key.
s.SetKeyDown(string(t.Keysym.Sym), t.State == 1)
}
}
}
return s, nil
}

View File

@ -1,120 +0,0 @@
// Package sdl provides an SDL2 renderer for Doodle.
package sdl
import (
"fmt"
"time"
"git.kirsle.net/apps/doodle/lib/render"
"git.kirsle.net/apps/doodle/lib/render/event"
"github.com/veandco/go-sdl2/sdl"
"github.com/veandco/go-sdl2/ttf"
)
// Renderer manages the SDL state.
type Renderer struct {
// Configurable fields.
title string
width int32
height int32
startTime time.Time
// Private fields.
events *event.State
window *sdl.Window
renderer *sdl.Renderer
running bool
ticks uint64
textures map[string]*Texture // cached textures
// Optimizations to minimize SDL calls.
lastColor render.Color
}
// New creates the SDL renderer.
func New(title string, width, height int) *Renderer {
return &Renderer{
events: event.NewState(),
title: title,
width: int32(width),
height: int32(height),
textures: map[string]*Texture{},
}
}
// Teardown tasks when exiting the program.
func (r *Renderer) Teardown() {
r.renderer.Destroy()
r.window.Destroy()
sdl.Quit()
}
// Setup the renderer.
func (r *Renderer) Setup() error {
// Initialize SDL.
if err := sdl.Init(sdl.INIT_EVERYTHING); err != nil {
return fmt.Errorf("sdl.Init: %s", err)
}
// Initialize SDL_TTF.
if err := ttf.Init(); err != nil {
return fmt.Errorf("ttf.Init: %s", err)
}
// Create our window.
window, err := sdl.CreateWindow(
r.title,
sdl.WINDOWPOS_CENTERED,
sdl.WINDOWPOS_CENTERED,
r.width,
r.height,
sdl.WINDOW_SHOWN|sdl.WINDOW_RESIZABLE,
)
if err != nil {
return err
}
r.window = window
// Blank out the window in white.
renderer, err := sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
if err != nil {
panic(err)
}
renderer.SetDrawBlendMode(sdl.BLENDMODE_BLEND)
r.renderer = renderer
return nil
}
// SetTitle sets the SDL window title.
func (r *Renderer) SetTitle(title string) {
r.title = title
r.window.SetTitle(title)
}
// GetTicks gets SDL's current tick count.
func (r *Renderer) GetTicks() uint32 {
return sdl.GetTicks()
}
// WindowSize returns the SDL window size.
func (r *Renderer) WindowSize() (int, int) {
w, h := r.window.GetSize()
return int(w), int(h)
}
// Present the current frame.
func (r *Renderer) Present() error {
r.renderer.Present()
return nil
}
// Delay using sdl.Delay
func (r *Renderer) Delay(time uint32) {
sdl.Delay(time)
}
// Loop is the main loop.
func (r *Renderer) Loop() error {
return nil
}

View File

@ -1,183 +0,0 @@
package sdl
import (
"fmt"
"sync"
"git.kirsle.net/apps/doodle/lib/render"
"github.com/veandco/go-sdl2/sdl"
"github.com/veandco/go-sdl2/ttf"
)
// TODO: font filenames
var defaultFontFilename = "DejaVuSans.ttf"
// Font holds cached SDL_TTF structures for loaded fonts. They are created
// automatically when fonts are either preinstalled (InstallFont) or loaded for
// the first time as demanded by the DrawText method.
type Font struct {
Filename string
data []byte // raw binary data of font
ttf *ttf.Font
}
var (
fonts = map[string]*ttf.Font{} // keys like "DejaVuSans@14" by font size
installedFont = map[string][]byte{} // installed font files' binary handles
fontsMu sync.RWMutex
)
// InstallFont preloads the font cache using TTF binary data in memory.
func InstallFont(filename string, binary []byte) {
fontsMu.Lock()
installedFont[filename] = binary
fontsMu.Unlock()
}
// LoadFont loads and caches the font at a given size.
func LoadFont(filename string, size int) (*ttf.Font, error) {
if filename == "" {
filename = defaultFontFilename
}
// Cached font available?
keyName := fmt.Sprintf("%s@%d", filename, size)
if font, ok := fonts[keyName]; ok {
return font, nil
}
// Do we have this font in memory?
var (
font *ttf.Font
err error
)
if binary, ok := installedFont[filename]; ok {
var RWops *sdl.RWops
RWops, err = sdl.RWFromMem(binary)
if err != nil {
return nil, fmt.Errorf("LoadFont(%s): RWFromMem: %s", filename, err)
}
font, err = ttf.OpenFontRW(RWops, 0, size)
} else {
font, err = ttf.OpenFont(filename, size)
}
// Error opening the font?
if err != nil {
return nil, fmt.Errorf("LoadFont(%s): %s", filename, err)
}
// Cache this font name and size.
fonts[keyName] = font
return font, nil
}
// ComputeTextRect computes and returns a Rect for how large the text would
// appear if rendered.
func (r *Renderer) ComputeTextRect(text render.Text) (render.Rect, error) {
var (
rect render.Rect
font *ttf.Font
surface *sdl.Surface
color = ColorToSDL(text.Color)
err error
)
if font, err = LoadFont(text.FontFilename, text.Size); err != nil {
return rect, err
}
if surface, err = font.RenderUTF8Blended(text.Text, color); err != nil {
return rect, err
}
defer surface.Free()
rect.W = surface.W
rect.H = surface.H
return rect, err
}
// DrawText draws text on the canvas.
func (r *Renderer) DrawText(text render.Text, point render.Point) error {
var (
font *ttf.Font
surface *sdl.Surface
tex *sdl.Texture
err error
)
if font, err = LoadFont(text.FontFilename, text.Size); err != nil {
return err
}
write := func(dx, dy int32, color sdl.Color) {
if surface, err = font.RenderUTF8Blended(text.Text, color); err != nil {
return
}
defer surface.Free()
if tex, err = r.renderer.CreateTextureFromSurface(surface); err != nil {
return
}
defer tex.Destroy()
tmp := &sdl.Rect{
X: point.X + dx,
Y: point.Y + dy,
W: surface.W,
H: surface.H,
}
r.renderer.Copy(tex, nil, tmp)
}
// Does the text have a stroke around it?
if text.Stroke != render.Invisible {
color := ColorToSDL(text.Stroke)
write(-1, -1, color)
write(-1, 0, color)
write(-1, 1, color)
write(1, -1, color)
write(1, 0, color)
write(1, 1, color)
write(0, -1, color)
write(0, 1, color)
}
// Does it have a drop shadow?
if text.Shadow != render.Invisible {
write(1, 1, ColorToSDL(text.Shadow))
}
// Draw the text itself.
write(0, 0, ColorToSDL(text.Color))
return err
}
// shiftMap maps keys to their Shift versions.
var shiftMap = map[string]string{
"`": "~",
"1": "!",
"2": "@",
"3": "#",
"4": "$",
"5": "%",
"6": "^",
"7": "&",
"8": "*",
"9": "(",
"0": ")",
"-": "_",
"=": "+",
"[": "{",
"]": "}",
`\`: "|",
";": ":",
`'`: `"`,
",": "<",
".": ">",
"/": "?",
}

View File

@ -1,84 +0,0 @@
package sdl
import (
"bytes"
"fmt"
"image"
"git.kirsle.net/apps/doodle/lib/render"
"github.com/veandco/go-sdl2/sdl"
"golang.org/x/image/bmp"
)
// Copy a texture into the renderer.
func (r *Renderer) Copy(t render.Texturer, src, dst render.Rect) {
if tex, ok := t.(*Texture); ok {
var (
a = RectToSDL(src)
b = RectToSDL(dst)
)
r.renderer.Copy(tex.tex, &a, &b)
}
}
// Texture can hold on to SDL textures for caching and optimization.
type Texture struct {
tex *sdl.Texture
width int32
height int32
}
// StoreTexture caches an SDL texture from a bitmap.
func (r *Renderer) StoreTexture(name string, img image.Image) (render.Texturer, error) {
var (
fh = bytes.NewBuffer([]byte{})
)
err := bmp.Encode(fh, img)
if err != nil {
return nil, fmt.Errorf("NewTexture: bmp.Encode: %s", err)
}
// Create an SDL RWOps from the bitmap data in memory.
sdlRW, err := sdl.RWFromMem(fh.Bytes())
if err != nil {
return nil, fmt.Errorf("NewTexture: sdl.RWFromMem: %s", err)
}
surface, err := sdl.LoadBMPRW(sdlRW, true)
if err != nil {
return nil, fmt.Errorf("NewTexture: sdl.LoadBMPRW: %s", err)
}
defer surface.Free()
// TODO: chroma key color hardcoded to white here
key := sdl.MapRGB(surface.Format, 255, 255, 255)
surface.SetColorKey(true, key)
texture, err := r.renderer.CreateTextureFromSurface(surface)
if err != nil {
return nil, fmt.Errorf("NewBitmap: create texture: %s", err)
}
tex := &Texture{
width: surface.W,
height: surface.H,
tex: texture,
}
r.textures[name] = tex
return tex, nil
}
// Size returns the dimensions of the texture.
func (t *Texture) Size() render.Rect {
return render.NewRect(t.width, t.height)
}
// LoadTexture initializes a texture from a bitmap image.
func (r *Renderer) LoadTexture(name string) (render.Texturer, error) {
if tex, ok := r.textures[name]; ok {
return tex, nil
}
return nil, fmt.Errorf("LoadTexture(%s): not found in texture cache", name)
}

View File

@ -1,26 +0,0 @@
package sdl
import (
"git.kirsle.net/apps/doodle/lib/render"
"github.com/veandco/go-sdl2/sdl"
)
// ColorToSDL converts Doodle's Color type to an sdl.Color.
func ColorToSDL(c render.Color) sdl.Color {
return sdl.Color{
R: c.Red,
G: c.Green,
B: c.Blue,
A: c.Alpha,
}
}
// RectToSDL converts Doodle's Rect type to an sdl.Rect.
func RectToSDL(r render.Rect) sdl.Rect {
return sdl.Rect{
X: r.X,
Y: r.Y,
W: r.W,
H: r.H,
}
}

View File

@ -1,105 +0,0 @@
package render
import (
"math"
)
// 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(p1 Point, p2 Point) chan Point {
var (
x1 = p1.X
y1 = p1.Y
x2 = p2.X
y2 = p2.Y
)
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
}
// IterRect loops through all the points forming a rectangle between the
// top-left point and the bottom-right point.
func IterRect(p1, p2 Point) chan Point {
generator := make(chan Point)
go func() {
var (
TopLeft = p1
BottomRight = p2
TopRight = Point{
X: BottomRight.X,
Y: TopLeft.Y,
}
BottomLeft = Point{
X: TopLeft.X,
Y: BottomRight.Y,
}
dedupe = map[Point]interface{}{}
)
// Trace all four edges and yield it.
var edges = []struct {
A Point
B Point
}{
{TopLeft, TopRight},
{TopLeft, BottomLeft},
{BottomLeft, BottomRight},
{TopRight, BottomRight},
}
for _, edge := range edges {
for pt := range IterLine(edge.A, edge.B) {
if _, ok := dedupe[pt]; !ok {
generator <- pt
dedupe[pt] = nil
}
}
}
close(generator)
}()
return generator
}
// IterEllipse iterates an Ellipse using two Points as the top-left and
// bottom-right corners of a rectangle that encompasses the ellipse.
func IterEllipse(A, B Point) chan Point {
var (
width = AbsInt32(B.X - A.X)
height = AbsInt32(B.Y - A.Y)
radius = NewPoint(width/2, height/2)
center = NewPoint(AbsInt32(B.X-radius.X), AbsInt32(B.Y-radius.Y))
)
return MidpointEllipse(center, radius)
}

View File

@ -4,7 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/ui/theme" "git.kirsle.net/apps/doodle/lib/ui/theme"
) )

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/ui/theme" "git.kirsle.net/apps/doodle/lib/ui/theme"
) )

View File

@ -1,6 +1,6 @@
package ui package ui
import "git.kirsle.net/apps/doodle/lib/render" import "git.kirsle.net/go/render"
// Checkbox combines a CheckButton with a widget like a Label. // Checkbox combines a CheckButton with a widget like a Label.
type Checkbox struct { type Checkbox struct {

View File

@ -1,7 +1,7 @@
package main package main
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
) )

View File

@ -3,7 +3,7 @@ package ui
import ( import (
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
// Frame is a widget that contains other widgets. // Frame is a widget that contains other widgets.

View File

@ -1,7 +1,7 @@
package ui package ui
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
// Pack provides configuration fields for Frame.Pack(). // Pack provides configuration fields for Frame.Pack().

View File

@ -1,6 +1,6 @@
package ui package ui
import "git.kirsle.net/apps/doodle/lib/render" import "git.kirsle.net/go/render"
// AbsolutePosition computes a widget's absolute X,Y position on the // AbsolutePosition computes a widget's absolute X,Y position on the
// window on screen by crawling its parent widget tree. // window on screen by crawling its parent widget tree.

View File

@ -5,7 +5,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
// ImageType for supported image formats. // ImageType for supported image formats.

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
// DefaultFont is the default font settings used for a Label. // DefaultFont is the default font settings used for a Label.

View File

@ -6,8 +6,8 @@ import (
"fmt" "fmt"
"time" "time"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/sdl" "git.kirsle.net/go/render/sdl"
) )
// Target frames per second for the MainWindow to render at. // Target frames per second for the MainWindow to render at.

View File

@ -3,7 +3,7 @@ package ui
import ( import (
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
// Menu is a rectangle that holds menu items. // Menu is a rectangle that holds menu items.

View File

@ -4,8 +4,8 @@ import (
"errors" "errors"
"sync" "sync"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
) )
// Event is a named event that the supervisor will send. // Event is a named event that the supervisor will send.

View File

@ -1,6 +1,6 @@
package theme package theme
import "git.kirsle.net/apps/doodle/lib/render" import "git.kirsle.net/go/render"
// Color schemes. // Color schemes.
var ( var (

View File

@ -3,7 +3,7 @@ package ui
import ( import (
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/ui/theme" "git.kirsle.net/apps/doodle/lib/ui/theme"
) )

View File

@ -3,7 +3,7 @@ package ui
import ( import (
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
// Window is a frame with a title bar. // Window is a frame with a title bar.

View File

@ -5,7 +5,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
// Debug related variables that can toggle on or off certain features and // Debug related variables that can toggle on or off certain features and

View File

@ -1,6 +1,6 @@
package balance package balance
import "git.kirsle.net/apps/doodle/lib/render" import "git.kirsle.net/go/render"
// Shell related variables. // Shell related variables.
var ( var (

View File

@ -1,7 +1,7 @@
package balance package balance
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
) )

View File

@ -3,7 +3,7 @@ package collision_test
import ( import (
"testing" "testing"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/collision" "git.kirsle.net/apps/doodle/pkg/collision"
) )

View File

@ -4,7 +4,7 @@ import (
"errors" "errors"
"math" "math"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
// BoxCollision holds the result of a collision BetweenBoxes. // BoxCollision holds the result of a collision BetweenBoxes.

View File

@ -3,7 +3,7 @@ package collision
import ( import (
"sync" "sync"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/doodads"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"
) )

View File

@ -3,7 +3,7 @@ package collision
import ( import (
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
// CollisionBox holds all of the coordinate pairs to draw the collision box // CollisionBox holds all of the coordinate pairs to draw the collision box

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"testing" "testing"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/collision" "git.kirsle.net/apps/doodle/pkg/collision"
"git.kirsle.net/apps/doodle/pkg/doodads/dummy" "git.kirsle.net/apps/doodle/pkg/doodads/dummy"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"

View File

@ -1,7 +1,7 @@
package doodads package doodads
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
// Actor is a reusable run-time drawing component used in Doodle. Actors are an // Actor is a reusable run-time drawing component used in Doodle. Actors are an

View File

@ -1,7 +1,7 @@
package doodads package doodads
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"
) )

View File

@ -1,7 +1,7 @@
package doodads package doodads
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"github.com/google/uuid" "github.com/google/uuid"
) )

View File

@ -1,7 +1,7 @@
package doodads package doodads
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"
) )

View File

@ -6,8 +6,8 @@ import (
"strings" "strings"
"time" "time"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/branding" "git.kirsle.net/apps/doodle/pkg/branding"
"git.kirsle.net/apps/doodle/pkg/enum" "git.kirsle.net/apps/doodle/pkg/enum"

View File

@ -3,7 +3,7 @@ package drawtool
import ( import (
"testing" "testing"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
func TestHistory(t *testing.T) { func TestHistory(t *testing.T) {

View File

@ -1,6 +1,6 @@
package drawtool package drawtool
import "git.kirsle.net/apps/doodle/lib/render" import "git.kirsle.net/go/render"
/* /*
Stroke holds temporary pixel data with a shape and color. Stroke holds temporary pixel data with a shape and color.

View File

@ -6,8 +6,8 @@ import (
"os" "os"
"strings" "strings"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/doodads"
"git.kirsle.net/apps/doodle/pkg/drawtool" "git.kirsle.net/apps/doodle/pkg/drawtool"

View File

@ -5,8 +5,8 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/branding" "git.kirsle.net/apps/doodle/pkg/branding"

View File

@ -7,7 +7,7 @@ package doodle
import ( import (
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/doodads"

View File

@ -3,7 +3,7 @@ package doodle
import ( import (
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"

View File

@ -1,7 +1,7 @@
package doodle package doodle
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/drawtool" "git.kirsle.net/apps/doodle/pkg/drawtool"

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/collision" "git.kirsle.net/apps/doodle/pkg/collision"

View File

@ -3,8 +3,8 @@ package doodle
import ( import (
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/branding" "git.kirsle.net/apps/doodle/pkg/branding"

View File

@ -1,7 +1,7 @@
package level package level
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"github.com/google/uuid" "github.com/google/uuid"
) )

View File

@ -6,7 +6,7 @@ import (
"image" "image"
"math" "math"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/apps/doodle/pkg/shmem" "git.kirsle.net/apps/doodle/pkg/shmem"

View File

@ -5,7 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"github.com/vmihailenco/msgpack" "github.com/vmihailenco/msgpack"
) )

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"testing" "testing"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"
) )

View File

@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"math" "math"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"
"github.com/vmihailenco/msgpack" "github.com/vmihailenco/msgpack"
) )

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"testing" "testing"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"
) )

View File

@ -1,6 +1,6 @@
package level package level
import "git.kirsle.net/apps/doodle/lib/render" import "git.kirsle.net/go/render"
// DefaultPalette returns a sensible default palette. // DefaultPalette returns a sensible default palette.
func DefaultPalette() *Palette { func DefaultPalette() *Palette {

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
// Swatch holds details about a single value in the palette. // Swatch holds details about a single value in the palette.

View File

@ -4,7 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/drawtool" "git.kirsle.net/apps/doodle/pkg/drawtool"
) )

View File

@ -1,8 +1,8 @@
package doodle package doodle
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/branding" "git.kirsle.net/apps/doodle/pkg/branding"

View File

@ -3,8 +3,8 @@ package doodle
import ( import (
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/enum" "git.kirsle.net/apps/doodle/pkg/enum"

View File

@ -3,8 +3,8 @@ package doodle
import ( import (
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/collision" "git.kirsle.net/apps/doodle/pkg/collision"

View File

@ -1,7 +1,7 @@
package doodle package doodle
import ( import (
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"
) )

View File

@ -3,7 +3,7 @@ package scripting
import ( import (
"errors" "errors"
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
) )

View File

@ -5,7 +5,7 @@ import (
"reflect" "reflect"
"time" "time"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/apps/doodle/pkg/shmem" "git.kirsle.net/apps/doodle/pkg/shmem"
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"

View File

@ -5,8 +5,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"

View File

@ -3,7 +3,7 @@ package shmem
import ( import (
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
) )
// Shared globals for easy access throughout the app. // Shared globals for easy access throughout the app.

View File

@ -8,7 +8,7 @@ import (
"os" "os"
"runtime" "runtime"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/bindata" "git.kirsle.net/apps/doodle/pkg/bindata"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"

View File

@ -4,7 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/doodads"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"
"github.com/google/uuid" "github.com/google/uuid"

View File

@ -5,7 +5,7 @@ import (
"sync" "sync"
"time" "time"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/collision" "git.kirsle.net/apps/doodle/pkg/collision"
"git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/doodads"

View File

@ -1,6 +1,6 @@
package uix package uix
import "git.kirsle.net/apps/doodle/lib/render" import "git.kirsle.net/go/render"
// CollideEvent holds data sent to an actor's Collide handler. // CollideEvent holds data sent to an actor's Collide handler.
type CollideEvent struct { type CollideEvent struct {

View File

@ -6,8 +6,8 @@ import (
"runtime" "runtime"
"strings" "strings"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/bindata" "git.kirsle.net/apps/doodle/pkg/bindata"

View File

@ -4,7 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/doodads"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"

View File

@ -1,7 +1,7 @@
package uix package uix
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/shmem" "git.kirsle.net/apps/doodle/pkg/shmem"
) )

View File

@ -1,8 +1,8 @@
package uix package uix
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/drawtool" "git.kirsle.net/apps/doodle/pkg/drawtool"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"

View File

@ -4,8 +4,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/event" "git.kirsle.net/go/render/event"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"
) )

View File

@ -1,7 +1,7 @@
package uix package uix
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/drawtool" "git.kirsle.net/apps/doodle/pkg/drawtool"

View File

@ -1,7 +1,7 @@
package uix package uix
import ( import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"
"git.kirsle.net/apps/doodle/pkg/wallpaper" "git.kirsle.net/apps/doodle/pkg/wallpaper"
) )

View File

@ -6,7 +6,7 @@ import (
"errors" "errors"
"image" "image"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/shmem" "git.kirsle.net/apps/doodle/pkg/shmem"
"git.kirsle.net/apps/doodle/pkg/userdir" "git.kirsle.net/apps/doodle/pkg/userdir"
) )

View File

@ -9,7 +9,7 @@ import (
"runtime" "runtime"
"strings" "strings"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/pkg/bindata" "git.kirsle.net/apps/doodle/pkg/bindata"
) )

View File

@ -7,8 +7,8 @@ import (
"syscall/js" "syscall/js"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/go/render"
"git.kirsle.net/apps/doodle/lib/render/canvas" "git.kirsle.net/go/render/canvas"
doodle "git.kirsle.net/apps/doodle/pkg" doodle "git.kirsle.net/apps/doodle/pkg"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/branding" "git.kirsle.net/apps/doodle/pkg/branding"