Cut lib/render into its own package, change all imports
This commit is contained in:
parent
2adffd56c6
commit
ea0b41a781
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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.
|
|
|
@ -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()
|
|
||||||
}
|
|
|
@ -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),
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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": ")",
|
|
||||||
"-": "_",
|
|
||||||
"=": "+",
|
|
||||||
"[": "{",
|
|
||||||
"]": "}",
|
|
||||||
`\`: "|",
|
|
||||||
";": ":",
|
|
||||||
`'`: `"`,
|
|
||||||
",": "<",
|
|
||||||
".": ">",
|
|
||||||
"/": "?",
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
)
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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": ")",
|
|
||||||
"-": "_",
|
|
||||||
"=": "+",
|
|
||||||
"[": "{",
|
|
||||||
"]": "}",
|
|
||||||
`\`: "|",
|
|
||||||
";": ":",
|
|
||||||
`'`: `"`,
|
|
||||||
",": "<",
|
|
||||||
".": ">",
|
|
||||||
"/": "?",
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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().
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user