diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index a7d1071f..089e2186 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -1,8 +1,30 @@
name: Go
on: [push]
jobs:
- test-arm:
- name: Test (arm)
+ test-windows-x64:
+ name: Test (windows amd64)
+ runs-on: [self-hosted, windows, x64]
+ steps:
+
+ - name: Set up Go 1.18
+ uses: actions/setup-go@v1
+ with:
+ go-version: 1.18
+ id: go
+
+ - name: Check out code into the Go module directory
+ uses: actions/checkout@v1
+
+ - name: Get dependencies
+ run: |
+ go get -v -t -d ./...
+
+ - name: Test
+ run: ./test_examples.sh
+ shell: bash
+
+ test-linux-arm:
+ name: Test (linux arm)
runs-on: [self-hosted, linux, ARM64]
steps:
@@ -22,6 +44,27 @@ jobs:
- name: Test
run: ./test_examples.sh
+ test-linux-x64:
+ name: Test (linux x64)
+ runs-on: [self-hosted, linux, x64]
+ steps:
+
+ - name: Set up Go 1.18
+ uses: actions/setup-go@v1
+ with:
+ go-version: 1.18
+ id: go
+
+ - name: Check out code into the Go module directory
+ uses: actions/checkout@v1
+
+ - name: Get dependencies
+ run: |
+ go get -v -t -d ./...
+
+ - name: Test
+ run: ./test_examples.sh
+
test:
name: Test
runs-on: ubuntu-latest
diff --git a/README.md b/README.md
index 43c6944a..8af2618a 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
## A Pure Go game engine
-[![Go Reference](https://pkg.go.dev/badge/github.com/oakmound/oak/v3.svg)](https://pkg.go.dev/github.com/oakmound/oak/v3)
+[![Go Reference](https://pkg.go.dev/badge/github.com/oakmound/oak/v4.svg)](https://pkg.go.dev/github.com/oakmound/oak/v4)
[![Code Coverage](https://codecov.io/gh/oakmound/oak/branch/develop/graph/badge.svg)](https://codecov.io/gh/oakmound/oak)
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go)
@@ -24,14 +24,14 @@
## Installation
-`go get -u github.com/oakmound/oak/v3`
+`go get -u github.com/oakmound/oak/v4`
## Features and Systems
1. Window Management
- Windows and key events forked from [shiny](https://pkg.go.dev/golang.org/x/exp/shiny)
- Support for multiple windows running at the same time
-1. [Image Rendering](https://pkg.go.dev/github.com/oakmound/oak/v3/render)
+1. [Image Rendering](https://pkg.go.dev/github.com/oakmound/oak/v4/render)
- Manipulation
- `render.Modifiable` interface
- Integrated with optimized image manipulation via [gift](https://github.com/disintegration/gift)
@@ -40,18 +40,18 @@
- Primitive builders, `ColorBox`, `Line`, `Bezier`
- History-tracking `Reverting`
- Primarily 2D
-1. [Particle System](https://pkg.go.dev/github.com/oakmound/oak/v3/render/particle)
-1. [Mouse Handling](https://pkg.go.dev/github.com/oakmound/oak/v3/mouse)
-1. [Joystick Support](https://pkg.go.dev/github.com/oakmound/oak/v3/joystick)
-1. [Audio Support](https://pkg.go.dev/github.com/oakmound/oak/v3/audio)
-1. [Collision](https://pkg.go.dev/github.com/oakmound/oak/v3/collision)
+1. [Particle System](https://pkg.go.dev/github.com/oakmound/oak/v4/render/particle)
+1. [Mouse Handling](https://pkg.go.dev/github.com/oakmound/oak/v4/mouse)
+1. [Joystick Support](https://pkg.go.dev/github.com/oakmound/oak/v4/joystick)
+1. [Audio Support](https://pkg.go.dev/github.com/oakmound/oak/v4/audio)
+1. [Collision](https://pkg.go.dev/github.com/oakmound/oak/v4/collision)
- Collision R-Tree forked from [rtreego](https://github.com/dhconnelly/rtreego)
- - [2D Raycasting](https://pkg.go.dev/github.com/oakmound/oak/v3/collision/ray)
+ - [2D Raycasting](https://pkg.go.dev/github.com/oakmound/oak/v4/collision/ray)
- Collision Spaces
- Attachable to Objects
- Auto React to collisions through events
-1. [2D Physics System](https://pkg.go.dev/github.com/oakmound/oak/v3/physics)
-1. [Event Handler](https://pkg.go.dev/github.com/oakmound/oak/v3/event)
+1. [2D Physics System](https://pkg.go.dev/github.com/oakmound/oak/v4/physics)
+1. [Event Handler](https://pkg.go.dev/github.com/oakmound/oak/v4/event)
## Support
@@ -65,8 +65,8 @@ This is an example of the most basic oak program:
package main
import (
- "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/scene"
)
func main() {
@@ -79,7 +79,7 @@ func main() {
}
```
-See below or the [examples](examples) folder for longer demos, [godoc](https://pkg.go.dev/github.com/oakmound/oak/v3) for reference documentation, and the [wiki](https://github.com/oakmound/oak/wiki) for more guided tutorials and walkthroughs.
+See below or the [examples](examples) folder for longer demos, [godoc](https://pkg.go.dev/github.com/oakmound/oak/v4) for reference documentation, and the [wiki](https://github.com/oakmound/oak/wiki) for more guided tutorials and walkthroughs.
## Examples
diff --git a/alg/floatgeom/point.go b/alg/floatgeom/point.go
index 7335f1ea..2edda851 100644
--- a/alg/floatgeom/point.go
+++ b/alg/floatgeom/point.go
@@ -3,7 +3,7 @@ package floatgeom
import (
"math"
- "github.com/oakmound/oak/v3/alg"
+ "github.com/oakmound/oak/v4/alg"
)
// Point2 represents a 2D point on a plane.
diff --git a/alg/floatgeom/point_test.go b/alg/floatgeom/point_test.go
index 49617207..a0b0c976 100644
--- a/alg/floatgeom/point_test.go
+++ b/alg/floatgeom/point_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/alg"
+ "github.com/oakmound/oak/v4/alg"
)
func Seed() {
diff --git a/alg/floatgeom/polygon.go b/alg/floatgeom/polygon.go
index 92ccca3a..a257dcf9 100644
--- a/alg/floatgeom/polygon.go
+++ b/alg/floatgeom/polygon.go
@@ -1,7 +1,7 @@
package floatgeom
import (
- "github.com/oakmound/oak/v3/alg"
+ "github.com/oakmound/oak/v4/alg"
)
// A Polygon2 is a series of points in 2D space.
diff --git a/alg/intgeom/point.go b/alg/intgeom/point.go
index d8038baf..d95c07dc 100644
--- a/alg/intgeom/point.go
+++ b/alg/intgeom/point.go
@@ -3,7 +3,7 @@ package intgeom
import (
"math"
- "github.com/oakmound/oak/v3/alg"
+ "github.com/oakmound/oak/v4/alg"
)
// Point2 represents a 2D point in space.
diff --git a/alg/intgeom/point_test.go b/alg/intgeom/point_test.go
index 6e28860d..a9869f40 100644
--- a/alg/intgeom/point_test.go
+++ b/alg/intgeom/point_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/alg"
+ "github.com/oakmound/oak/v4/alg"
)
func Seed() {
diff --git a/alg/intgeom/rect.go b/alg/intgeom/rect.go
index f7535205..43d0cd98 100644
--- a/alg/intgeom/rect.go
+++ b/alg/intgeom/rect.go
@@ -1,5 +1,11 @@
package intgeom
+import (
+ "math/rand"
+
+ "github.com/oakmound/oak/v4/alg/span"
+)
+
// A Rect2 represents a span from one point in 2D space to another.
// If Min is less than max on any axis, it will return undefined results
// for methods.
@@ -326,3 +332,93 @@ func (r Rect2) Intersects(r2 Rect2) bool {
return !((r2.Max.X() <= r.Min.X() || r.Max.X() <= r2.Min.X()) ||
(r2.Max.Y() <= r.Min.Y() || r.Max.Y() <= r2.Min.Y()))
}
+
+// MulConst multiplies the boundary points of this rectangle by i.
+func (r Rect2) MulConst(i int) Rect2 {
+ return Rect2{
+ r.Min.MulConst(i),
+ r.Max.MulConst(i),
+ }
+}
+
+// Poll returns a pseudorandom point from within this rectangle
+func (r Rect2) Poll() Point2 {
+ return Point2{
+ r.Min.X() + int(rand.Float64()*float64(r.W())),
+ r.Min.Y() + int(rand.Float64()*float64(r.H())),
+ }
+}
+
+// Clamp returns a version of the provided point such that it is contained within r. If it was already contained in
+// r, it will not be changed.
+func (r Rect2) Clamp(pt Point2) Point2 {
+ for i := 0; i < r.MaxDimensions(); i++ {
+ if pt[i] < r.Min[i] {
+ pt[i] = r.Min[i]
+ } else if pt[i] > r.Max[i] {
+ pt[i] = r.Max[i]
+ }
+ }
+ return pt
+}
+
+// Percentile returns a point within this rectangle along the vector from the top left to the bottom right of the
+// rectangle, where for example, 0.0 will be r.Min, 1.0 will be r.Max, and 2.0 will be project the vector beyond r
+// and return r.Min + {r.W()*2, r.H()*2}
+func (r Rect2) Percentile(f float64) Point2 {
+ return Point2{
+ r.Min.X() + int(f*float64(r.W())),
+ r.Min.Y() + int(f*float64(r.H())),
+ }
+}
+
+// MulSpan returns this rectangle as a Point2 Span after multiplying the boundary points of the rectangle by f.
+func (r Rect2) MulSpan(f float64) span.Span[Point2] {
+ return r.MulConst(int(f))
+}
+
+// MulConst multiplies the boundary points of this rectangle by i.
+func (r Rect3) MulConst(i int) Rect3 {
+ return Rect3{
+ r.Min.MulConst(i),
+ r.Max.MulConst(i),
+ }
+}
+
+// Poll returns a pseudorandom point from within this rectangle
+func (r Rect3) Poll() Point3 {
+ return Point3{
+ r.Min.X() + int(rand.Float64()*float64(r.W())),
+ r.Min.Y() + int(rand.Float64()*float64(r.H())),
+ r.Min.Z() + int(rand.Float64()*float64(r.D())),
+ }
+}
+
+// Clamp returns a version of the provided point such that it is contained within r. If it was already contained in
+// r, it will not be changed.
+func (r Rect3) Clamp(pt Point3) Point3 {
+ for i := 0; i < r.MaxDimensions(); i++ {
+ if pt[i] < r.Min[i] {
+ pt[i] = r.Min[i]
+ } else if pt[i] > r.Max[i] {
+ pt[i] = r.Max[i]
+ }
+ }
+ return pt
+}
+
+// Percentile returns a point within this rectangle along the vector from the top left to the bottom right of the
+// rectangle, where for example, 0.0 will be r.Min, 1.0 will be r.Max, and 2.0 will be project the vector beyond r
+// and return r.Min + {r.W()*2, r.H()*2, r.D()*2}
+func (r Rect3) Percentile(f float64) Point3 {
+ return Point3{
+ r.Min.X() + int(f*float64(r.W())),
+ r.Min.Y() + int(f*float64(r.H())),
+ r.Min.Z() + int(f*float64(r.D())),
+ }
+}
+
+// MulConst multiplies the boundary points of this rectangle by i.
+func (r Rect3) MulSpan(f float64) span.Span[Point3] {
+ return r.MulConst(int(f))
+}
diff --git a/alg/intgeom/rect_test.go b/alg/intgeom/rect_test.go
index b6e12326..b3753f89 100644
--- a/alg/intgeom/rect_test.go
+++ b/alg/intgeom/rect_test.go
@@ -239,3 +239,67 @@ func TestRect3GreaterOf(t *testing.T) {
}
}
}
+
+func TestRect2Span(t *testing.T) {
+ t.Run("Basic", func(t *testing.T) {
+ r := NewRect2WH(1, 1, 9, 9)
+ p1 := r.Percentile(1.0)
+ if p1 != r.Max {
+ t.Errorf("Percentile(1.0) did not return max point: got %v expected %v", p1, r.Max)
+ }
+ p2 := r.Percentile(0.0)
+ if p2 != r.Min {
+ t.Errorf("Percentile(0.0) did not return min point: got %v expected %v", p2, r.Min)
+ }
+ const pollTries = 100
+ for i := 0; i < pollTries; i++ {
+ if !r.Contains(r.Poll()) {
+ t.Fatalf("polled point did not lie within the creating rectangle")
+ }
+ }
+ p3 := r.Clamp(Point2{0, 5})
+ if p3 != (Point2{1, 5}) {
+ t.Errorf("Clamp(0,5) did not return {1,5}: got %v", p3)
+ }
+ p4 := r.Clamp(Point2{2, 11})
+ if p4 != (Point2{2, 10}) {
+ t.Errorf("Clamp(2,11) did not return {2,10}: got %v", p4)
+ }
+ r2 := r.MulSpan(4)
+ if r2 != NewRect2(4, 4, 40, 40) {
+ t.Errorf("MulSpan did not return {4,4,40,40}: got %v", r2)
+ }
+ })
+}
+
+func TestRect3Span(t *testing.T) {
+ t.Run("Basic", func(t *testing.T) {
+ r := NewRect3WH(1, 1, 1, 9, 9, 9)
+ p1 := r.Percentile(1.0)
+ if p1 != r.Max {
+ t.Errorf("Percentile(1.0) did not return max point: got %v expected %v", p1, r.Max)
+ }
+ p2 := r.Percentile(0.0)
+ if p2 != r.Min {
+ t.Errorf("Percentile(0.0) did not return min point: got %v expected %v", p2, r.Min)
+ }
+ const pollTries = 100
+ for i := 0; i < pollTries; i++ {
+ if !r.Contains(r.Poll()) {
+ t.Fatalf("polled point did not lie within the creating rectangle")
+ }
+ }
+ p3 := r.Clamp(Point3{0, -1, 5})
+ if p3 != (Point3{1, 1, 5}) {
+ t.Errorf("Clamp(0,-1,5) did not return {1,1,5}: got %v", p3)
+ }
+ p4 := r.Clamp(Point3{20, 2, 11})
+ if p4 != (Point3{10, 2, 10}) {
+ t.Errorf("Clamp(20, 2,11) did not return {10,2,10}: got %v", p4)
+ }
+ r2 := r.MulSpan(4)
+ if r2 != NewRect3(4, 4, 4, 40, 40, 40) {
+ t.Errorf("MulSpan did not return {4,4,4,40,40,40}: got %v", r2)
+ }
+ })
+}
diff --git a/alg/range/colorrange/linear.go b/alg/range/colorrange/linear.go
deleted file mode 100644
index 5dcc1940..00000000
--- a/alg/range/colorrange/linear.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package colorrange
-
-import (
- "image/color"
-
- "github.com/oakmound/oak/v3/alg/range/intrange"
-)
-
-// linear color ranges return colors on a linear distribution
-type linear struct {
- r, g, b, a intrange.Range
-}
-
-// NewLinear returns a linear color distribution between min and maxColor
-func NewLinear(minColor, maxColor color.Color) Range {
- r, g, b, a := minColor.RGBA()
- r2, g2, b2, a2 := maxColor.RGBA()
- return linear{
- intrange.NewLinear(int(r), int(r2)),
- intrange.NewLinear(int(g), int(g2)),
- intrange.NewLinear(int(b), int(b2)),
- intrange.NewLinear(int(a), int(a2)),
- }
-}
-
-// EnforceRange rounds the input color's components so that they fall in the
-// given range.
-func (l linear) EnforceRange(c color.Color) color.Color {
- r3, g3, b3, a3 := c.RGBA()
- r4 := l.r.EnforceRange(int(r3))
- g4 := l.g.EnforceRange(int(g3))
- b4 := l.b.EnforceRange(int(b3))
- a4 := l.a.EnforceRange(int(a3))
- return rgbaFromInts(r4, g4, b4, a4)
-}
-
-// Poll returns a randomly chosen color in the bounds of this color range
-func (l linear) Poll() color.Color {
- r3 := l.r.Poll()
- g3 := l.g.Poll()
- b3 := l.b.Poll()
- a3 := l.a.Poll()
- return rgbaFromInts(r3, g3, b3, a3)
-}
-
-// Percentile returns a color f percent along the color range
-func (l linear) Percentile(f float64) color.Color {
- r3 := l.r.Percentile(f)
- g3 := l.g.Percentile(f)
- b3 := l.b.Percentile(f)
- a3 := l.a.Percentile(f)
- return rgbaFromInts(r3, g3, b3, a3)
-}
-
-func rgbaFromInts(r, g, b, a int) color.RGBA {
- return color.RGBA{uint8(r / 257), uint8(g / 257), uint8(b / 257), uint8(a / 257)}
-}
diff --git a/alg/range/colorrange/linear_test.go b/alg/range/colorrange/linear_test.go
deleted file mode 100644
index 561da66c..00000000
--- a/alg/range/colorrange/linear_test.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package colorrange
-
-import (
- "image/color"
- "math/rand"
- "testing"
-)
-
-func TestLinear(t *testing.T) {
- rng := NewLinear(color.RGBA{255, 255, 255, 255}, color.RGBA{255, 255, 255, 255})
- if rng.Poll() != (color.RGBA{255, 255, 255, 255}) {
- t.Fatal("false linear range did not return only possible value on Poll")
- }
- for i := 0; i < 100; i++ {
- if rng.Percentile(rand.Float64()) != (color.RGBA{255, 255, 255, 255}) {
- t.Fatal("false linear range did not return only possible value on Percentile")
- }
- }
- rng = NewLinear(color.RGBA{0, 0, 0, 255}, color.RGBA{255, 255, 255, 255})
- for i := 0.0; i < 255; i++ {
- p := i / 255
- uinti := uint8(i)
- if rng.Percentile(p) != (color.RGBA{uinti, uinti, uinti, 255}) {
- t.Fatal("linear color range did not return appropriate scaled color, bottom to top")
- }
- }
- rng = NewLinear(color.RGBA{255, 255, 255, 255}, color.RGBA{0, 0, 0, 255})
- for i := 255.0; i > 0; i-- {
- p := (255 - i) / 255
- uinti := uint8(i)
- if rng.Percentile(p) != (color.RGBA{uinti, uinti, uinti, 255}) {
- t.Fatal("linear color range did not return appropriate scaled color, top to bottom")
- }
- }
- rng = NewLinear(color.RGBA{125, 125, 125, 125}, color.RGBA{200, 200, 200, 200})
- if rng.EnforceRange(color.RGBA{100, 100, 100, 100}) != (color.RGBA{125, 125, 125, 125}) {
- t.Fatal("linear color range did not enforce minimum color")
- }
- if rng.EnforceRange(color.RGBA{225, 225, 225, 225}) != (color.RGBA{200, 200, 200, 200}) {
- t.Fatal("linear color range did not enforce maximum color")
- }
- if rng.EnforceRange(color.RGBA{175, 175, 175, 175}) != (color.RGBA{175, 175, 175, 175}) {
- t.Fatal("linear color range did not pass through value within range")
- }
-}
diff --git a/alg/range/colorrange/range.go b/alg/range/colorrange/range.go
deleted file mode 100644
index d609a7bd..00000000
--- a/alg/range/colorrange/range.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// Package colorrange provides distributions that accept and return color.Colors.
-package colorrange
-
-import (
- "image/color"
-)
-
-// Range represents a range of colors
-type Range interface {
- Poll() color.Color
- EnforceRange(color.Color) color.Color
- Percentile(f float64) color.Color
-}
diff --git a/alg/range/doc.go b/alg/range/doc.go
deleted file mode 100644
index 4e789fe0..00000000
--- a/alg/range/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package range provides helper constructs to represent ranges of values, to poll from or clamp to
-package arange
diff --git a/alg/range/floatrange/constant.go b/alg/range/floatrange/constant.go
deleted file mode 100644
index a2514a48..00000000
--- a/alg/range/floatrange/constant.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package floatrange
-
-// constant is a range that represents some constant float
-type constant float64
-
-// NewConstant returns a range that will always poll to return f
-func NewConstant(f float64) Range {
- return constant(f)
-}
-
-// Poll returns the float behind the constant
-func (c constant) Poll() float64 {
- return float64(c)
-}
-
-// Mult scales the constant by f
-func (c constant) Mult(f float64) Range {
- c = constant(float64(c) * f)
- return c
-}
-
-// EnforceRange returns the float behind the constant
-func (c constant) EnforceRange(float64) float64 {
- return float64(c)
-}
-
-// Percentile returns the float behind the constant
-func (c constant) Percentile(float64) float64 {
- return float64(c)
-}
diff --git a/alg/range/floatrange/constant_test.go b/alg/range/floatrange/constant_test.go
deleted file mode 100644
index cfeebc9c..00000000
--- a/alg/range/floatrange/constant_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package floatrange
-
-import (
- "math/rand"
- "testing"
- "time"
-)
-
-func TestConstant(t *testing.T) {
- rand.Seed(time.Now().Unix())
- const testCount = 100
- const maxInt = 100000
- const minInt = -100000
- for i := 0; i < testCount; i++ {
- val := rand.Float64()*(maxInt-minInt) + minInt
- cons := NewConstant(val)
- if cons.Poll() != val {
- t.Fatal("Constant.Poll did not return initialized value")
- }
- magnitude := rand.Float64()
- cons2 := cons.Mult(magnitude)
- if cons2.Poll() != float64(val)*magnitude {
- t.Fatal("Constant.Mult result did not match expected Poll")
- }
- if cons.EnforceRange(rand.Float64()*(maxInt-minInt)+minInt) != val {
- t.Fatal("Constant.EnforceRange did not return initialized value")
- }
- if cons.Percentile(rand.Float64()) != val {
- t.Fatal("Constant.Percentile did not return initialized value")
- }
- }
-}
diff --git a/alg/range/floatrange/infinite.go b/alg/range/floatrange/infinite.go
deleted file mode 100644
index 725bf402..00000000
--- a/alg/range/floatrange/infinite.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package floatrange
-
-import "math"
-
-// Infinite is an immutable range that will always return math.MaxFloat64
-type Infinite struct{}
-
-// NewInfinite returns an infinite.
-func NewInfinite() Range {
- return Infinite{}
-}
-
-// Poll returns MaxFloat64 on an infinite
-func (i Infinite) Poll() float64 {
- return math.MaxFloat64
-}
-
-// Mult returns an infinite from an infinite.
-func (i Infinite) Mult(f float64) Range {
- return i
-}
-
-// EnforceRange returns math.MaxFloat64
-func (i Infinite) EnforceRange(f float64) float64 {
- return math.MaxFloat64
-}
-
-// Percentile returns the float behind the constant
-func (i Infinite) Percentile(float64) float64 {
- return math.MaxFloat64
-}
diff --git a/alg/range/floatrange/infinite_test.go b/alg/range/floatrange/infinite_test.go
deleted file mode 100644
index 0f7e81b2..00000000
--- a/alg/range/floatrange/infinite_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package floatrange
-
-import (
- "math"
- "math/rand"
- "testing"
- "time"
-)
-
-func TestInfinite(t *testing.T) {
- rand.Seed(time.Now().Unix())
- inf := NewInfinite()
- if inf.Poll() != math.MaxFloat64 {
- t.Fatal("infinite.Poll did not return math.MaxFloat64")
- }
- inf2 := inf.Mult(rand.Float64())
- if inf2 != inf {
- t.Fatal("base infinite did not match multiplied infinite")
- }
- if inf.EnforceRange(rand.Float64()*10000) != math.MaxFloat64 {
- t.Fatal("infinite.EnforceRange did not return math.MaxFloat64")
- }
- if inf.Percentile(rand.Float64()) != math.MaxFloat64 {
- t.Fatal("infinite.Percentile did not return math.MaxFloat64")
- }
-}
diff --git a/alg/range/floatrange/linear.go b/alg/range/floatrange/linear.go
deleted file mode 100644
index ce24e1b4..00000000
--- a/alg/range/floatrange/linear.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package floatrange
-
-import (
- "math/rand"
-
- "github.com/oakmound/oak/v3/alg/range/internal/random"
-)
-
-// NewSpread returns a linear range from base-spread to base+spread
-func NewSpread(base, spread float64) Range {
- if spread == 0 {
- return constant(base)
- }
- return linear{
- Min: base - spread,
- Max: base + spread,
- rng: random.Rand(),
- }
-}
-
-// NewLinear returns a linear range from min to max
-func NewLinear(min, max float64) Range {
- if max == min {
- return constant(min)
- }
- flipped := false
- if max < min {
- max, min = min, max
- flipped = true
- }
- return linear{
- Min: min,
- Max: max,
- rng: random.Rand(),
- flipped: flipped,
- }
-}
-
-// linear is a range from min to max
-type linear struct {
- Max, Min float64
- rng *rand.Rand
- flipped bool
-}
-
-// Poll on a linear float range returns a float at uniform
-// distribution in lfr's range
-func (lfr linear) Poll() float64 {
- return ((lfr.Max - lfr.Min) * lfr.rng.Float64()) + lfr.Min
-}
-
-// Mult scales a Linear by f
-func (lfr linear) Mult(f float64) Range {
- lfr.Max *= f
- lfr.Min *= f
- return lfr
-}
-
-// EnforceRange returns f, if is within the range, or the closest value
-// in the range to f.
-func (lfr linear) EnforceRange(f float64) float64 {
- if f < lfr.Min {
- return lfr.Min
- } else if f > lfr.Max {
- return lfr.Max
- }
- return f
-}
-
-// Percentile returns the fth percentile value along this range
-func (lfr linear) Percentile(f float64) float64 {
- return ((lfr.Max - lfr.Min) * f) + lfr.Min
-}
diff --git a/alg/range/floatrange/linear_test.go b/alg/range/floatrange/linear_test.go
deleted file mode 100644
index e881a7b6..00000000
--- a/alg/range/floatrange/linear_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package floatrange
-
-import (
- "math/rand"
- "testing"
- "time"
-)
-
-func TestNewLinear_Constant(t *testing.T) {
- linear := NewLinear(1, 1)
- if _, ok := linear.(constant); !ok {
- t.Fatalf("NewLinear with no variance did not create constant")
- }
-}
-
-func TestNewSpread_Constant(t *testing.T) {
- linear := NewSpread(1, 0)
- if _, ok := linear.(constant); !ok {
- t.Fatalf("NewSpread with no spread did not create constant")
- }
-}
-
-func TestNewSpread(t *testing.T) {
- linear := NewSpread(10, -10).(linear)
- if linear.flipped {
- t.Fatalf("new spread should not produce flipped linear range")
- }
-}
-
-func TestLinear(t *testing.T) {
- rand.Seed(time.Now().Unix())
- const testCount = 100
- const maxInt = 100000
- const minInt = -100000
- for i := 0; i < testCount; i++ {
- min := rand.Float64()*(maxInt-minInt) + minInt
- max := rand.Float64()*(maxInt-minInt) + minInt
- linear := NewLinear(min, max)
- if max < min {
- min, max = max, min
- }
- poll := linear.Poll()
- if poll < min || poll > max {
- t.Fatal("Linear.Poll did not return a value in its range")
- }
- magnitude := rand.Float64()
- linear2 := linear.Mult(magnitude)
- poll2 := linear2.Poll()
- if poll2 < float64(min)*magnitude || poll2 > float64(max)*magnitude {
- t.Fatal("Linear.Mult result did not match expected Poll")
- }
- underMin := (rand.Float64()*(maxInt-minInt) + minInt) - (maxInt - minInt)
- if linear.EnforceRange(underMin) != min {
- t.Fatal("Linear.EnforceRange under min did not return min")
- }
- overMax := (rand.Float64()*(maxInt-minInt) + minInt) + (maxInt - minInt)
- if linear.EnforceRange(overMax) != max {
- t.Fatal("Linear.EnforceRange over max did not return max")
- }
- within := rand.Float64()*(max-min) + min
- if linear.EnforceRange(within) != within {
- t.Fatal("Linear.EnforceRange within range did not return input")
- }
- percent := rand.Float64()
- if linear.Percentile(percent) != min+float64((max-min))*percent {
- t.Fatal("Linear.Percentile did not return percentile value")
- }
- }
-}
diff --git a/alg/range/floatrange/range.go b/alg/range/floatrange/range.go
deleted file mode 100644
index cafb2ae1..00000000
--- a/alg/range/floatrange/range.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// Package floatrange provides distributions that accept and return float64s.
-package floatrange
-
-// Range represents a range of floating point numbers
-type Range interface {
- Poll() float64
- Mult(f float64) Range
- EnforceRange(f float64) float64
- Percentile(f float64) float64
-}
diff --git a/alg/range/intrange/constant.go b/alg/range/intrange/constant.go
deleted file mode 100644
index a0fcdc4c..00000000
--- a/alg/range/intrange/constant.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package intrange
-
-// NewConstant returns a range which will always return the input constant
-func NewConstant(i int) Range {
- return constant(i)
-}
-
-// constant implements Range as a poll
-// which always returns the same integer.
-type constant int
-
-// Poll returns c cast to an int
-func (c constant) Poll() int {
- return int(c)
-}
-
-// Mult returns this range scaled by i
-func (c constant) Mult(i float64) Range {
- return constant(int(float64(int(c)) * i))
-}
-
-// EnforceRange on a constant must return the constant
-func (c constant) EnforceRange(int) int {
- return int(c)
-}
-
-// Percentile can only return the constant itself
-func (c constant) Percentile(float64) int {
- return int(c)
-}
diff --git a/alg/range/intrange/constant_test.go b/alg/range/intrange/constant_test.go
deleted file mode 100644
index 34c7f9d6..00000000
--- a/alg/range/intrange/constant_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package intrange
-
-import (
- "math/rand"
- "testing"
- "time"
-)
-
-func TestConstant(t *testing.T) {
- rand.Seed(time.Now().Unix())
- const testCount = 100
- const maxInt = 100000
- const minInt = -100000
- for i := 0; i < testCount; i++ {
- val := rand.Intn(maxInt-minInt) + minInt
- cons := NewConstant(val)
- if cons.Poll() != val {
- t.Fatal("Constant.Poll did not return initialized value")
- }
- magnitude := rand.Float64()
- cons2 := cons.Mult(magnitude)
- if cons2.Poll() != int(float64(val)*magnitude) {
- t.Fatal("Constant.Mult result did not match expected Poll")
- }
- if cons.EnforceRange(rand.Intn(maxInt)) != val {
- t.Fatal("Constant.EnforceRange did not return initialized value")
- }
- if cons.Percentile(rand.Float64()) != val {
- t.Fatal("Constant.Percentile did not return initialized value")
- }
- }
-}
diff --git a/alg/range/intrange/infinite.go b/alg/range/intrange/infinite.go
deleted file mode 100644
index 7d494e10..00000000
--- a/alg/range/intrange/infinite.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package intrange
-
-import (
- "math"
-)
-
-// NewInfinite returns a range which will always return math.MaxInt32 and
-// is unchangeable.
-func NewInfinite() Range {
- return Infinite{}
-}
-
-// Infinite is a immutable range which always polls math.MaxInt32
-type Infinite struct{}
-
-// Poll returns math.MaxInt32 on Infinites.
-func (inf Infinite) Poll() int {
- return math.MaxInt32
-}
-
-// Mult does nothing to Infinites.
-func (inf Infinite) Mult(i float64) Range {
- return inf
-}
-
-// EnforceRange for an Infinite returns Infinite
-func (inf Infinite) EnforceRange(i int) int {
- return math.MaxInt32
-}
-
-// Percentile can only return math.MaxInt32
-func (inf Infinite) Percentile(float64) int {
- return math.MaxInt32
-}
diff --git a/alg/range/intrange/infinite_test.go b/alg/range/intrange/infinite_test.go
deleted file mode 100644
index 1d4eaefc..00000000
--- a/alg/range/intrange/infinite_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package intrange
-
-import (
- "math"
- "math/rand"
- "testing"
- "time"
-)
-
-func TestInfinite(t *testing.T) {
- rand.Seed(time.Now().Unix())
- inf := NewInfinite()
- if inf.Poll() != math.MaxInt32 {
- t.Fatal("infinite.Poll did not return math.MaxInt32")
- }
- inf2 := inf.Mult(rand.Float64())
- if inf2 != inf {
- t.Fatal("base infinite did not match multiplied infinite")
- }
- if inf.EnforceRange(rand.Intn(10000)) != math.MaxInt32 {
- t.Fatal("infinite.EnforceRange did not return math.MaxInt32")
- }
- if inf.Percentile(rand.Float64()) != math.MaxInt32 {
- t.Fatal("infinite.Percentile did not return math.MaxInt32")
- }
-}
diff --git a/alg/range/intrange/linear.go b/alg/range/intrange/linear.go
deleted file mode 100644
index 367e23cd..00000000
--- a/alg/range/intrange/linear.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package intrange
-
-import (
- "math/rand"
-
- "github.com/oakmound/oak/v3/alg/range/internal/random"
-)
-
-// NewLinear returns a linear range between min and max
-func NewLinear(min, max int) Range {
- if max == min {
- return constant(min)
- }
- flipped := false
- if max < min {
- max, min = min, max
- flipped = true
- }
- return linear{
- min: min,
- max: max,
- rng: random.Rand(),
- flipped: flipped,
- }
-}
-
-// NewSpread returns a linear range from base - s to base + s
-func NewSpread(base, spread int) Range {
- if spread == 0 {
- return constant(base)
- }
- if spread < 0 {
- spread *= -1
- }
- return linear{base - spread, base + spread, random.Rand(), false}
-}
-
-// linear polls on a linear scale between a minimum and a maximum
-type linear struct {
- min, max int
- rng *rand.Rand
- flipped bool
-}
-
-func (lir linear) Poll() int {
- return int(float64(lir.max-lir.min)*lir.rng.Float64()) + lir.min
-}
-
-func (lir linear) Mult(i float64) Range {
- lir.max = int(float64(lir.max) * i)
- lir.min = int(float64(lir.min) * i)
- return lir
-}
-
-func (lir linear) EnforceRange(i int) int {
- if i < lir.min {
- return lir.min
- } else if i > lir.max {
- return lir.max
- }
- return i
-}
-
-func (lir linear) Percentile(f float64) int {
- diff := float64(lir.max-lir.min) * f // 0 - 255 * .1 = -25 + 255 = 230 // 255 - 0 * .1 = 25
- if lir.flipped {
- return lir.max - int(diff)
- }
- return lir.min + int(diff)
-}
diff --git a/alg/range/intrange/range.go b/alg/range/intrange/range.go
deleted file mode 100644
index 7cee8427..00000000
--- a/alg/range/intrange/range.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// Package intrange provides distributions that return ints.
-package intrange
-
-// Range represents a range of integer numbers
-type Range interface {
- Poll() int
- Mult(float64) Range
- EnforceRange(int) int
- Percentile(float64) int
-}
diff --git a/alg/span/builtin.go b/alg/span/builtin.go
new file mode 100644
index 00000000..b82ecd61
--- /dev/null
+++ b/alg/span/builtin.go
@@ -0,0 +1,98 @@
+package span
+
+import (
+ "math/rand"
+
+ "github.com/oakmound/oak/v4/alg/span/internal/random"
+ "golang.org/x/exp/constraints"
+)
+
+// A Spanable must be usable in basic arithmetic-- addition, subtraction, and multiplication.
+type Spanable interface {
+ constraints.Float | constraints.Integer
+}
+
+// NewConstant returns a span where the minimum and maximum are both i. Poll, Percentile, and Clamp will always return i.
+func NewConstant[T Spanable](i T) Span[T] {
+ return constant[T]{i}
+}
+
+type constant[T Spanable] struct {
+ val T
+}
+
+func (c constant[T]) Poll() T {
+ return c.val
+}
+
+func (c constant[T]) MulSpan(i float64) Span[T] {
+ return constant[T]{T(float64(c.val) * i)}
+}
+
+func (c constant[T]) Clamp(T) T {
+ return c.val
+}
+
+func (c constant[T]) Percentile(float64) T {
+ return c.val
+}
+
+// NewLinear returns a linear span between min and max. The linearity implies that no point in the span is preferred,
+// and Percentile will scale in a constant fashion from min to max.
+func NewLinear[T Spanable](min, max T) Span[T] {
+ if max == min {
+ return constant[T]{min}
+ }
+ flipped := false
+ if max < min {
+ max, min = min, max
+ flipped = true
+ }
+ return linear[T]{
+ min: min,
+ max: max,
+ rng: random.Rand(),
+ flipped: flipped,
+ }
+}
+
+// NewSpread returns a linear span from base-spread to base+spread.
+func NewSpread[T Spanable](base, spread T) Span[T] {
+ if spread < 0 {
+ return NewLinear(base+spread, base-spread)
+ }
+ return NewLinear(base-spread, base+spread)
+}
+
+type linear[T Spanable] struct {
+ min, max T
+ rng *rand.Rand
+ flipped bool
+}
+
+func (lir linear[T]) Poll() T {
+ return T(float64(lir.max-lir.min)*lir.rng.Float64()) + lir.min
+}
+
+func (lir linear[T]) MulSpan(i float64) Span[T] {
+ lir.max = T(float64(lir.max) * i)
+ lir.min = T(float64(lir.min) * i)
+ return lir
+}
+
+func (lir linear[T]) Clamp(i T) T {
+ if i < lir.min {
+ return lir.min
+ } else if i > lir.max {
+ return lir.max
+ }
+ return i
+}
+
+func (lir linear[T]) Percentile(f float64) T {
+ diff := float64(lir.max-lir.min) * f // 0 - 255 * .1 = -25 + 255 = 230 // 255 - 0 * .1 = 25
+ if lir.flipped {
+ return lir.max - T(diff)
+ }
+ return lir.min + T(diff)
+}
diff --git a/alg/range/intrange/linear_test.go b/alg/span/builtin_test.go
similarity index 58%
rename from alg/range/intrange/linear_test.go
rename to alg/span/builtin_test.go
index 20c0fc87..ecb5be1e 100644
--- a/alg/range/intrange/linear_test.go
+++ b/alg/span/builtin_test.go
@@ -1,4 +1,4 @@
-package intrange
+package span
import (
"math/rand"
@@ -8,20 +8,20 @@ import (
func TestNewLinear_Constant(t *testing.T) {
linear := NewLinear(1, 1)
- if _, ok := linear.(constant); !ok {
+ if _, ok := linear.(constant[int]); !ok {
t.Fatalf("NewLinear with no variance did not create constant")
}
}
func TestNewSpread_Constant(t *testing.T) {
linear := NewSpread(1, 0)
- if _, ok := linear.(constant); !ok {
+ if _, ok := linear.(constant[int]); !ok {
t.Fatalf("NewSpread with no spread did not create constant")
}
}
func TestNewSpread(t *testing.T) {
- linear := NewSpread(10, -10).(linear)
+ linear := NewSpread[float32](10, -10).(linear[float32])
if linear.flipped {
t.Fatalf("new spread should not produce flipped linear range")
}
@@ -46,22 +46,22 @@ func TestLinear(t *testing.T) {
t.Fatal("Linear.Poll did not return a value in its range")
}
magnitude := rand.Float64()
- linear2 := linear.Mult(magnitude)
+ linear2 := linear.MulSpan(magnitude)
poll2 := linear2.Poll()
if poll2 < int(float64(min)*magnitude) || poll2 > int(float64(max)*magnitude) {
t.Fatal("Linear.Mult result did not match expected Poll")
}
underMin := (rand.Intn(maxInt-minInt) + minInt) - (maxInt - minInt)
- if linear.EnforceRange(underMin) != min {
- t.Fatal("Linear.EnforceRange under min did not return min")
+ if linear.Clamp(underMin) != min {
+ t.Fatal("Linear.Clamp under min did not return min")
}
overMax := (rand.Intn(maxInt-minInt) + minInt) + (maxInt - minInt)
- if linear.EnforceRange(overMax) != max {
- t.Fatal("Linear.EnforceRange over max did not return max")
+ if linear.Clamp(overMax) != max {
+ t.Fatal("Linear.Clamp over max did not return max")
}
within := rand.Intn(max-min) + min
- if linear.EnforceRange(within) != within {
- t.Fatal("Linear.EnforceRange within range did not return input")
+ if linear.Clamp(within) != within {
+ t.Fatal("Linear.Clamp within range did not return input")
}
percent := rand.Float64()
if !flipped {
@@ -75,3 +75,28 @@ func TestLinear(t *testing.T) {
}
}
}
+
+func TestConstant(t *testing.T) {
+ rand.Seed(time.Now().Unix())
+ const testCount = 100
+ const maxInt = 100000
+ const minInt = -100000
+ for i := 0; i < testCount; i++ {
+ val := rand.Intn(maxInt-minInt) + minInt
+ cons := NewConstant(val)
+ if cons.Poll() != val {
+ t.Fatal("Constant.Poll did not return initialized value")
+ }
+ magnitude := rand.Float64()
+ cons2 := cons.MulSpan(magnitude)
+ if cons2.Poll() != int(float64(val)*magnitude) {
+ t.Fatal("Constant.Mult result did not match expected Poll")
+ }
+ if cons.Clamp(rand.Intn(maxInt)) != val {
+ t.Fatal("Constant.Clamp did not return initialized value")
+ }
+ if cons.Percentile(rand.Float64()) != val {
+ t.Fatal("Constant.Percentile did not return initialized value")
+ }
+ }
+}
diff --git a/alg/span/color.go b/alg/span/color.go
new file mode 100644
index 00000000..0d670059
--- /dev/null
+++ b/alg/span/color.go
@@ -0,0 +1,57 @@
+package span
+
+import "image/color"
+
+type linearColor struct {
+ r, g, b, a Span[uint32]
+}
+
+// NewLinearColor returns a linear color distribution between min and maxColor
+func NewLinearColor(minColor, maxColor color.Color) Span[color.Color] {
+ r, g, b, a := minColor.RGBA()
+ r2, g2, b2, a2 := maxColor.RGBA()
+ return linearColor{
+ NewLinear(r, r2),
+ NewLinear(g, g2),
+ NewLinear(b, b2),
+ NewLinear(a, a2),
+ }
+}
+
+func (l linearColor) Clamp(c color.Color) color.Color {
+ r3, g3, b3, a3 := c.RGBA()
+ r4 := l.r.Clamp(r3)
+ g4 := l.g.Clamp(g3)
+ b4 := l.b.Clamp(b3)
+ a4 := l.a.Clamp(a3)
+ return rgbaFromInts(r4, g4, b4, a4)
+}
+
+func (l linearColor) MulSpan(i float64) Span[color.Color] {
+ return linearColor{
+ l.r.MulSpan(i),
+ l.g.MulSpan(i),
+ l.b.MulSpan(i),
+ l.a.MulSpan(i),
+ }
+}
+
+func (l linearColor) Poll() color.Color {
+ r3 := l.r.Poll()
+ g3 := l.g.Poll()
+ b3 := l.b.Poll()
+ a3 := l.a.Poll()
+ return rgbaFromInts(r3, g3, b3, a3)
+}
+
+func (l linearColor) Percentile(f float64) color.Color {
+ r3 := l.r.Percentile(f)
+ g3 := l.g.Percentile(f)
+ b3 := l.b.Percentile(f)
+ a3 := l.a.Percentile(f)
+ return rgbaFromInts(r3, g3, b3, a3)
+}
+
+func rgbaFromInts(r, g, b, a uint32) color.RGBA {
+ return color.RGBA{uint8(r / 257), uint8(g / 257), uint8(b / 257), uint8(a / 257)}
+}
diff --git a/alg/span/color_test.go b/alg/span/color_test.go
new file mode 100644
index 00000000..3b712cda
--- /dev/null
+++ b/alg/span/color_test.go
@@ -0,0 +1,56 @@
+package span
+
+import (
+ "image/color"
+ "math/rand"
+ "testing"
+)
+
+func TestLinearColor(t *testing.T) {
+ rng := NewLinearColor(color.RGBA{255, 255, 255, 255}, color.RGBA{255, 255, 255, 255})
+ if rng.Poll() != (color.RGBA{255, 255, 255, 255}) {
+ t.Fatal("false linear range did not return only possible value on Poll")
+ }
+ for i := 0; i < 100; i++ {
+ if rng.Percentile(rand.Float64()) != (color.RGBA{255, 255, 255, 255}) {
+ t.Fatal("false linear range did not return only possible value on Percentile")
+ }
+ }
+ rng = NewLinearColor(color.RGBA{0, 0, 0, 255}, color.RGBA{255, 255, 255, 255})
+ for i := 0.0; i < 255; i++ {
+ p := i / 255
+ uinti := uint8(i)
+ if rng.Percentile(p) != (color.RGBA{uinti, uinti, uinti, 255}) {
+ t.Fatal("linear color range did not return appropriate scaled color, bottom to top")
+ }
+ }
+ rng = NewLinearColor(color.RGBA{255, 255, 255, 255}, color.RGBA{0, 0, 0, 255})
+ for i := 255.0; i > 0; i-- {
+ p := (255 - i) / 255
+ uinti := uint8(i)
+ if rng.Percentile(p) != (color.RGBA{uinti, uinti, uinti, 255}) {
+ t.Fatal("linear color range did not return appropriate scaled color, top to bottom")
+ }
+ }
+ rng = NewLinearColor(color.RGBA{125, 125, 125, 125}, color.RGBA{200, 200, 200, 200})
+ if rng.Clamp(color.RGBA{100, 100, 100, 100}) != (color.RGBA{125, 125, 125, 125}) {
+ t.Fatal("linear color range did not enforce minimum color")
+ }
+ if rng.Clamp(color.RGBA{225, 225, 225, 225}) != (color.RGBA{200, 200, 200, 200}) {
+ t.Fatal("linear color range did not enforce maximum color")
+ }
+ if rng.Clamp(color.RGBA{175, 175, 175, 175}) != (color.RGBA{175, 175, 175, 175}) {
+ t.Fatal("linear color range did not pass through value within range")
+ }
+
+ rng = rng.MulSpan(1.1)
+ if rng.Clamp(color.RGBA{100, 100, 100, 100}) != (color.RGBA{137, 137, 137, 137}) {
+ t.Fatal("linear color range did not enforce minimum color")
+ }
+ if rng.Clamp(color.RGBA{225, 225, 225, 225}) != (color.RGBA{220, 220, 220, 220}) {
+ t.Fatal("linear color range did not enforce maximum color")
+ }
+ if rng.Clamp(color.RGBA{175, 175, 175, 175}) != (color.RGBA{175, 175, 175, 175}) {
+ t.Fatal("linear color range did not pass through value within range")
+ }
+}
diff --git a/alg/span/doc.go b/alg/span/doc.go
new file mode 100644
index 00000000..efec8b41
--- /dev/null
+++ b/alg/span/doc.go
@@ -0,0 +1,2 @@
+// Package span provides helper constructs to represent ranges of values, to poll from or clamp to
+package span
diff --git a/alg/range/internal/random/rand.go b/alg/span/internal/random/rand.go
similarity index 100%
rename from alg/range/internal/random/rand.go
rename to alg/span/internal/random/rand.go
diff --git a/alg/span/span.go b/alg/span/span.go
new file mode 100644
index 00000000..b339bee2
--- /dev/null
+++ b/alg/span/span.go
@@ -0,0 +1,19 @@
+package span
+
+// A Span represents some enumerable range.
+type Span[T any] interface {
+ // Poll returns a pseudorandom value within this span.
+ Poll() T
+ // Clamp, if v lies within the boundary of this span, returns v.
+ // Otherwise, CLamp returns a modified version of v that is rounded to the closest value
+ // that does lie within the boundary of this span.
+ Clamp(v T) T
+ // Percentile returns the value along this span that is at the provided percentile through the span,
+ // e.g. providing .5 will return the middle of the span, providing 1 will return the maximum value in
+ // the span. Providing a value less than 0 or greater than 1 may extend the span by where it would theoretically
+ // progress, but should not be relied upon unless a given implementation specifies what it will do. If this span
+ // represents multiple degrees of freedom, this will pin all those degrees to the single provided percent.
+ Percentile(float64) T
+ // MulSpan returns this span with its entire range multiplied by the given constant.
+ MulSpan(float64) Span[T]
+}
diff --git a/audio/driver.go b/audio/driver.go
index fb860a86..b59f5e18 100644
--- a/audio/driver.go
+++ b/audio/driver.go
@@ -1,7 +1,6 @@
package audio
-// A Driver defines the underlying interface that should be used for initializing PCM audio writers
-// by this package.
+// A Driver defines the underlying interface that should be used for initializing PCM audio writers.
type Driver int
const (
diff --git a/audio/fade.go b/audio/fade.go
index 8abc91e2..8311653a 100644
--- a/audio/fade.go
+++ b/audio/fade.go
@@ -3,9 +3,10 @@ package audio
import (
"time"
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/pcm"
)
+// FadeIn wraps a reader such that it will linearly fades in over the given duration.
func FadeIn(dur time.Duration, in pcm.Reader) pcm.Reader {
perSec := in.PCMFormat().BytesPerSecond()
bytesToFadeIn := int((time.Duration(perSec) / 1000) * (dur / time.Millisecond))
@@ -74,6 +75,7 @@ func (fir *fadeInReader) ReadPCM(b []byte) (n int, err error) {
return read, nil
}
+// FadeOut wraps a reader such that it will linearly fades out over the given duration.
func FadeOut(dur time.Duration, in pcm.Reader) pcm.Reader {
perSec := in.PCMFormat().BytesPerSecond()
bytestoFadeOut := int((time.Duration(perSec) / 1000) * (dur / time.Millisecond))
diff --git a/audio/file_cache.go b/audio/file_cache.go
index 229b98f2..a6a021c5 100644
--- a/audio/file_cache.go
+++ b/audio/file_cache.go
@@ -4,7 +4,7 @@ import (
"path/filepath"
"sync"
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/pcm"
)
// DefaultCache is the receiver for package level loading operations.
diff --git a/audio/file_formats.go b/audio/file_formats.go
deleted file mode 100644
index 38284083..00000000
--- a/audio/file_formats.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package audio
-
-import (
- "io"
- "sync"
-
- "github.com/oakmound/oak/v3/audio/format/flac"
- "github.com/oakmound/oak/v3/audio/format/mp3"
- "github.com/oakmound/oak/v3/audio/format/wav"
- "github.com/oakmound/oak/v3/audio/pcm"
-)
-
-type fileLoader func(r io.Reader) (pcm.Reader, error)
-
-var fileLoadersLock sync.RWMutex
-var fileLoaders = map[string]func(r io.Reader) (pcm.Reader, error){
- "mp3": mp3.Load,
- "wav": wav.Load,
- "flac": flac.Load,
-}
-
-func RegisterFormat(extension string, fn fileLoader) {
- fileLoadersLock.Lock()
- fileLoaders[extension] = fn
- fileLoadersLock.Unlock()
-}
-
-func LoaderForExtension(extension string) (fileLoader, bool) {
- fileLoadersLock.RLock()
- defer fileLoadersLock.RUnlock()
- loader, ok := fileLoaders[extension]
- return loader, ok
-}
diff --git a/audio/file_load.go b/audio/file_load.go
index 843d1fa1..640dedf5 100644
--- a/audio/file_load.go
+++ b/audio/file_load.go
@@ -6,10 +6,11 @@ import (
"golang.org/x/sync/errgroup"
- "github.com/oakmound/oak/v3/audio/pcm"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/fileutil"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/audio/format"
+ "github.com/oakmound/oak/v4/audio/pcm"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/fileutil"
+ "github.com/oakmound/oak/v4/oakerr"
)
// Get will read cached audio data from Load, or error if the given
@@ -38,9 +39,19 @@ func (c *Cache) Load(file string) (pcm.Reader, error) {
ext := filepath.Ext(file)
ext = strings.ToLower(ext)
- reader, ok := LoaderForExtension(ext)
+ reader, ok := format.LoaderForExtension(ext)
if !ok {
- return nil, oakerr.UnsupportedFormat{Format: filepath.Ext(file)}
+ // provide an error message suggesting a missing import for cases where we know about a
+ // common provider
+ knownFormats := map[string]string{
+ ".mp3": "github.com/oakmound/oak/v4/audio/format/mp3",
+ ".flac": "github.com/oakmound/oak/v4/audio/format/flac",
+ ".wav": "github.com/oakmound/oak/v4/audio/format/wav",
+ }
+ if path, ok := knownFormats[ext]; ok {
+ dlog.Error("unable to parse audio format %v, did you mean to import %v?", ext, path)
+ }
+ return nil, oakerr.UnsupportedFormat{Format: ext}
}
r, err := reader(f)
if err != nil {
@@ -50,8 +61,8 @@ func (c *Cache) Load(file string) (pcm.Reader, error) {
return r, nil
}
-// BatchLoad attempts to load all files within a given directory
-// depending on their file ending
+// BatchLoad attempts to load all audio files within a given directory
+// should their file ending match a registered audio file parser
func BatchLoad(baseFolder string) error {
return batchLoad(baseFolder, false)
}
diff --git a/audio/format/ceol/ceol.go b/audio/format/ceol/ceol.go
index ff024a7a..75f1d461 100644
--- a/audio/format/ceol/ceol.go
+++ b/audio/format/ceol/ceol.go
@@ -61,27 +61,6 @@ type Filter struct {
LPFResonance int
}
-// ChordPattern converts a Ceol's patterns and arrangement into a playable chord
-// pattern for sequences
-// func (c Ceol) ChordPattern() sequence.ChordPattern {
-// chp := sequence.ChordPattern{}
-// chp.Pitches = make([][]synth.Pitch, c.PatternLength*len(c.Arrangement))
-// chp.Holds = make([][]time.Duration, c.PatternLength*len(c.Arrangement))
-// for i, m := range c.Arrangement {
-// for _, p := range m {
-// if p != -1 {
-// for _, n := range c.Patterns[p].Notes {
-// chp.Pitches[n.Offset+i*c.PatternLength] =
-// append(chp.Pitches[n.Offset+i*c.PatternLength], synth.NoteFromIndex(n.PitchIndex))
-// chp.Holds[n.Offset+i*c.PatternLength] =
-// append(chp.Holds[n.Offset+i*c.PatternLength], DurationFromQuarters(c.Bpm, n.Length))
-// }
-// }
-// }
-// }
-// return chp
-// }
-
// DurationFromQuarters should not be here, should be in a package
// managing bpm and time
// Duration from quarters expects four quarters to occur per beat,
diff --git a/audio/format/dls/dls.go b/audio/format/dls/dls.go
index ed9fa9b4..28961920 100644
--- a/audio/format/dls/dls.go
+++ b/audio/format/dls/dls.go
@@ -1,7 +1,7 @@
// Package dls contains data structures for DLS (.dls) file types.
package dls
-import "github.com/oakmound/oak/v3/audio/format/riff"
+import "github.com/oakmound/oak/v4/audio/format/riff"
// The DLS is the major struct we care about in this package
// DLS files contain instrument and wave sample information, and
diff --git a/audio/format/doc.go b/audio/format/doc.go
deleted file mode 100644
index 91df2189..00000000
--- a/audio/format/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package format provides audio file and format parsers
-package format
diff --git a/audio/format/flac/flac.go b/audio/format/flac/flac.go
index 46858383..d7899942 100644
--- a/audio/format/flac/flac.go
+++ b/audio/format/flac/flac.go
@@ -1,4 +1,12 @@
// Package flac provides functionality to handle .flac files and .flac encoded data.
+//
+//
+// This package may be imported solely to register flacs as a parseable file type within oak:
+//
+// import (
+// _ "github.com/oakmound/oak/v4/audio/format/flac"
+// )
+//
package flac
import (
@@ -6,10 +14,18 @@ import (
"io"
"github.com/eaburns/flac"
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/format"
+ "github.com/oakmound/oak/v4/audio/pcm"
)
-// Load loads flac data from the incoming reader as an audio
+func init() {
+ format.Register(".flac", Load)
+}
+
+// Load reads a FLAC header from a reader, parsing it's PCM format and returning
+// a pcm Reader for the data following the header. It will error if the reader
+// does not contain enough data to fill a FLAC header or if the header does not
+// look like a FLAC header.
func Load(r io.Reader) (pcm.Reader, error) {
d, err := flac.NewDecoder(r)
if err != nil {
diff --git a/audio/format/mp3/mp3.go b/audio/format/mp3/mp3.go
index c19a721d..14fb6ad7 100644
--- a/audio/format/mp3/mp3.go
+++ b/audio/format/mp3/mp3.go
@@ -1,15 +1,30 @@
// Package mp3 provides functionality to handle .mp3 files and .mp3 encoded data.
+//
+// This package may be imported solely to register mp3s as a parseable file type within oak:
+//
+// import (
+// _ "github.com/oakmound/oak/v4/audio/format/mp3"
+// )
+//
package mp3
import (
"io"
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/format"
+ "github.com/oakmound/oak/v4/audio/pcm"
"github.com/hajimehoshi/go-mp3"
)
-// Load loads an mp3-encoded reader into an audio
+func init() {
+ format.Register(".mp3", Load)
+}
+
+// Load reads MP3 data from a reader, parsing it's PCM format and returning
+// a pcm Reader for the data contained within. It will error if the reader
+// does not contain enough data to fill a file header. The resulting format
+// will always be 16 bits and 2 channels.
func Load(r io.Reader) (pcm.Reader, error) {
d, err := mp3.NewDecoder(r)
if err != nil {
diff --git a/audio/format/register.go b/audio/format/register.go
new file mode 100644
index 00000000..1059030b
--- /dev/null
+++ b/audio/format/register.go
@@ -0,0 +1,31 @@
+// Package format provides audio file and format parsers
+package format
+
+import (
+ "io"
+ "sync"
+
+ "github.com/oakmound/oak/v4/audio/pcm"
+)
+
+// A Loader can parse the data from an io.Reader and convert it into PCM encoded audio data with
+// a known format.
+type Loader func(r io.Reader) (pcm.Reader, error)
+
+var fileLoadersLock sync.RWMutex
+var fileLoaders = map[string]func(r io.Reader) (pcm.Reader, error){}
+
+// Register registers a format by file extension (eg '.mp3') with its parsing function.
+func Register(extension string, fn Loader) {
+ fileLoadersLock.Lock()
+ fileLoaders[extension] = fn
+ fileLoadersLock.Unlock()
+}
+
+// LoaderForExtension returns a previously registered loader.
+func LoaderForExtension(extension string) (Loader, bool) {
+ fileLoadersLock.RLock()
+ defer fileLoadersLock.RUnlock()
+ loader, ok := fileLoaders[extension]
+ return loader, ok
+}
diff --git a/audio/format/wav/wav.go b/audio/format/wav/wav.go
index 03a19362..e3168c59 100644
--- a/audio/format/wav/wav.go
+++ b/audio/format/wav/wav.go
@@ -1,4 +1,11 @@
// Package wav provides functionality to handle .wav files and .wav encoded data.
+//
+// This package may be imported solely to register wavs as a parseable file type within oak:
+//
+// import (
+// _ "github.com/oakmound/oak/v4/audio/format/wav"
+// )
+//
package wav
import (
@@ -6,11 +13,18 @@ import (
"encoding/binary"
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/format"
+ "github.com/oakmound/oak/v4/audio/pcm"
)
-// Read reads a WAV header from the provided reader, returning the PCM format and
-// leaving the PCM data in the reader available for consumption.
+func init() {
+ format.Register(".wav", Load)
+}
+
+// Load reads a WAV header from a reader, parsing it's PCM format and returning
+// a pcm Reader for the data following the header. It will error if the reader
+// does not contain enough data to fill a WAV header. It does not validate that the
+// WAV header makes sense.
func Load(r io.Reader) (pcm.Reader, error) {
data, err := readData(r)
if err != nil {
diff --git a/audio/init_darwin.go b/audio/init_darwin.go
index cd4c922b..6b8512f9 100644
--- a/audio/init_darwin.go
+++ b/audio/init_darwin.go
@@ -6,8 +6,8 @@ import (
"fmt"
"github.com/jfreymuth/pulse"
- "github.com/oakmound/oak/v3/oakerr"
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/pcm"
+ "github.com/oakmound/oak/v4/oakerr"
)
func initOS(driver Driver) error {
diff --git a/audio/init_linux.go b/audio/init_linux.go
index 33646b18..4c562495 100644
--- a/audio/init_linux.go
+++ b/audio/init_linux.go
@@ -7,8 +7,8 @@ import (
"os"
"github.com/jfreymuth/pulse"
- "github.com/oakmound/oak/v3/audio/pcm"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/audio/pcm"
+ "github.com/oakmound/oak/v4/oakerr"
)
func initOS(driver Driver) error {
diff --git a/audio/internal/dsound/dsound.go b/audio/internal/dsound/dsound.go
index 96472a3f..1295a2da 100644
--- a/audio/internal/dsound/dsound.go
+++ b/audio/internal/dsound/dsound.go
@@ -7,8 +7,8 @@ import (
"sync"
"syscall"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/oakerr"
"github.com/oov/directsound-go/dsound"
)
@@ -23,7 +23,6 @@ var cfg Config
var initLock sync.Mutex
// Init initializes directsound or returns an already intialized direct sound instance.
-// It may (but should probably not) be called outside of other oakmound/oak/* packages.
func Init() (Config, error) {
initLock.Lock()
defer initLock.Unlock()
diff --git a/audio/pcm/interface.go b/audio/pcm/interface.go
index e5fcf3ae..73ef5737 100644
--- a/audio/pcm/interface.go
+++ b/audio/pcm/interface.go
@@ -1,7 +1,10 @@
// Package pcm provides a interface for interacting with PCM audio streams
package pcm
-import "io"
+import (
+ "fmt"
+ "io"
+)
var _ Reader = &IOReader{}
@@ -25,7 +28,7 @@ func (ior *IOReader) ReadPCM(p []byte) (n int, err error) {
type Writer interface {
io.Closer
Formatted
- // WritePCM expects PCM bytes matching the format this speaker was initialized with.
+ // WritePCM expects PCM bytes matching this Writer's format.
// WritePCM will block until all of the bytes are consumed.
WritePCM([]byte) (n int, err error)
}
@@ -67,7 +70,7 @@ func (f Format) SampleSize() int {
// ReadFloat reads a single sample from an audio stream, respecting bits and channels:
// f.Bits / 8 bytes * f.Channels bytes will be read from b, and this count will be returned as 'read'.
// the length of values will be equal to f.Channels, if no error is returned. If an error is returned,
-// it will be io.ErrUnexpectedEOF. If bits is an unexpected value
+// it will be io.ErrUnexpectedEOF or ErrUnsupportedBits
func (f Format) SampleFloat(b []byte) (values []float64, read int, err error) {
values = make([]float64, 0, f.Channels)
read = f.SampleSize()
@@ -95,6 +98,11 @@ func (f Format) SampleFloat(b []byte) (values []float64, read int, err error) {
int32(b[i+3])<<24
values = append(values, float64(v))
}
+ default:
+ return nil, read, ErrUnsupportedBits
}
return
}
+
+// ErrUnsupportedBits represents that the Bits value for a Format was not supported for some operation.
+var ErrUnsupportedBits = fmt.Errorf("unsupported bits in pcm format")
diff --git a/audio/play.go b/audio/play.go
index 0cf822ac..cbcd299a 100644
--- a/audio/play.go
+++ b/audio/play.go
@@ -8,7 +8,7 @@ import (
"io"
"time"
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/pcm"
)
// WriterBufferLengthInSeconds defines how much data os-level writers provided by this package will rotate through
@@ -32,8 +32,11 @@ type PlayOption func(*PlayOptions)
// PlayOptions define ways to configure how playback of some audio proceeds
type PlayOptions struct {
+ // If FadeOutOnStop is non-zero, when this play is stopped early it will fade out for this duration.
FadeOutOnStop time.Duration
+ // If Destination is not provided, Play will create a new writer which will be
+ // closed after Play is complete.
Destination pcm.Writer
// The span of data that should be copied from reader to writer
diff --git a/audio/play_test.go b/audio/play_test.go
index 5042f82e..e6939421 100644
--- a/audio/play_test.go
+++ b/audio/play_test.go
@@ -9,10 +9,10 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/audio"
- "github.com/oakmound/oak/v3/audio/format/wav"
- "github.com/oakmound/oak/v3/audio/pcm"
- "github.com/oakmound/oak/v3/audio/synth"
+ "github.com/oakmound/oak/v4/audio"
+ "github.com/oakmound/oak/v4/audio/format/wav"
+ "github.com/oakmound/oak/v4/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/synth"
)
func TestMain(m *testing.M) {
diff --git a/audio/reader.go b/audio/reader.go
index 12b777a5..ac460b31 100644
--- a/audio/reader.go
+++ b/audio/reader.go
@@ -4,7 +4,7 @@ import (
"errors"
"io"
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/pcm"
)
var _ pcm.Reader = &LoopingReader{}
@@ -127,4 +127,3 @@ func ReadAtLeast(r pcm.Reader, buf []byte, min int) (n int, err error) {
}
return
}
-
diff --git a/audio/synth/filter.go b/audio/synth/filter.go
deleted file mode 100644
index 40c863db..00000000
--- a/audio/synth/filter.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package synth
-
-// Detune detunes between -1.0 and 1.0, 1.0 representing a half step up.
-// Q: What is detuning? A: It's taking the pitch of the audio and adjusting it less than
-// a single tone up or down. If you detune too far, you've just made the next pitch,
-// but if you detune a little, you get a resonant sound.
-func Detune(percent float64) func(src Source) Source {
- return func(src Source) Source {
- curPitch := src.Pitch
- var nextPitch Pitch
- if percent > 0 {
- nextPitch = curPitch.Up(HalfStep)
- } else {
- nextPitch = curPitch.Down(HalfStep)
- }
- rawDelta := float64(int16(curPitch) - int16(nextPitch))
- delta := rawDelta * percent
- // TODO: does pitch need to be a float?
- src.Pitch = Pitch(float64(curPitch) + delta)
- return src
- }
-}
diff --git a/audio/synth/filter_test.go b/audio/synth/filter_test.go
index ef078d3f..d760d921 100644
--- a/audio/synth/filter_test.go
+++ b/audio/synth/filter_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/audio"
+ "github.com/oakmound/oak/v4/audio"
)
func TestMain(m *testing.M) {
diff --git a/audio/synth/option.go b/audio/synth/option.go
index 59300db7..c2e64dbe 100644
--- a/audio/synth/option.go
+++ b/audio/synth/option.go
@@ -16,12 +16,12 @@ func Duration(t time.Duration) Option {
// Volume sets the volume of a generated waveform. It guarantees that 0 <= v <= 1
// (silent <= v <= max volume)
func Volume(v float64) Option {
+ if v > 1.0 {
+ v = 1.0
+ } else if v < 0 {
+ v = 0
+ }
return func(s Source) Source {
- if v > 1.0 {
- v = 1.0
- } else if v < 0 {
- v = 0
- }
s.Volume = v
return s
}
@@ -35,7 +35,7 @@ func AtPitch(p Pitch) Option {
}
}
-// Mono sets the format to play mono audio.
+// Mono sets a synth source to play mono audio.
func Mono() Option {
return func(s Source) Source {
s.Channels = 1
@@ -43,10 +43,31 @@ func Mono() Option {
}
}
-// Stereo sets the format to play stereo audio.
+// Stereo sets a synth source to play stereo audio.
func Stereo() Option {
return func(s Source) Source {
s.Channels = 2
return s
}
}
+
+// Detune detunes between -1.0 and 1.0, 1.0 representing a half step up.
+// Q: What is detuning? A: It's taking the pitch of the audio and adjusting it less than
+// a single tone up or down. If you detune too far, you've just made the next pitch,
+// but if you detune a little, you get a resonant sound.
+func Detune(percent float64) Option {
+ return func(src Source) Source {
+ curPitch := src.Pitch
+ var nextPitch Pitch
+ if percent > 0 {
+ nextPitch = curPitch.Up(HalfStep)
+ } else {
+ nextPitch = curPitch.Down(HalfStep)
+ }
+ rawDelta := float64(int16(curPitch) - int16(nextPitch))
+ delta := rawDelta * percent
+ // TODO: does pitch need to be a float?
+ src.Pitch = Pitch(float64(curPitch) + delta)
+ return src
+ }
+}
diff --git a/audio/synth/pitch.go b/audio/synth/pitch.go
index 7fecbb17..fbafa528 100644
--- a/audio/synth/pitch.go
+++ b/audio/synth/pitch.go
@@ -3,17 +3,16 @@ package synth
import (
"sort"
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/pcm"
)
-// A Pitch is a helper type for synth functions so
-// a user can write A4 instead of a frequency value
-// for a desired tone
+// A Pitch is a frequency value which represents how fast a wave should oscillate to produce a specific tone.
type Pitch uint16
-// Pitch frequencies
-// Values taken from http://peabody.sapp.org/class/st2/lab/notehz/
+// Pitch frequencies, taken from http://peabody.sapp.org/class/st2/lab/notehz/
+// These span octave 0 through octave 8, with sharps suffixed 's' and flats suffixed 'b'
const (
+ // 0 is reserved as representing a 'rest' for the purpose of composition
Rest Pitch = 0
C0 Pitch = 16
C0s Pitch = 17
@@ -588,12 +587,6 @@ func (p Pitch) Down(s Step) Pitch {
return allPitches[i-int(s)]
}
-// NoteFromIndex is a utility for pitch converters that for some reason have
-// integers representing their notes to get a pitch from said integer
-func NoteFromIndex(i int) Pitch {
- return allPitches[i]
-}
-
// IsAccidental reports true if this pitch is represented with a single sharp or a flat, usually.
func (p Pitch) IsAccidental() bool {
_, ok := accidentals[p]
@@ -605,26 +598,25 @@ type PitchDetector struct {
format pcm.Format
- // Will be 0 if unknown
- DetectedPitch Pitch
- DetectedRawPitch float64
-
- // Channel defines which audio channel (0 for mono, 0-1 for stereo) should
- // be analyzed. ReadPCM will panic if this value is invalid. If this scares you,
- // don't change this value-- the consequence is that a specific channel for stereo
- // audio will be analyzed, which won't be a problem unless you're running this on
- // Queen's The Prophet's Song
- Channel int
+ // DetectedPitches and DetectedRawPitches store the calculated pitch values as this reader parses data. The length
+ // of these slices will be equal to this reader's format's channel count. Consumers should not modify these slices.
+ DetectedPitches []Pitch
+ DetectedRawPitches []float64
- index int
- lastValue float64
- crossedZero bool
+ indices []int
+ lastValues []float64
+ crossedZero []bool
}
func NewPitchDetector(r pcm.Reader) *PitchDetector {
return &PitchDetector{
- Reader: r,
- format: r.PCMFormat(),
+ Reader: r,
+ format: r.PCMFormat(),
+ DetectedPitches: make([]Pitch, r.PCMFormat().Channels),
+ DetectedRawPitches: make([]float64, r.PCMFormat().Channels),
+ indices: make([]int, r.PCMFormat().Channels),
+ lastValues: make([]float64, r.PCMFormat().Channels),
+ crossedZero: make([]bool, r.PCMFormat().Channels),
}
}
@@ -633,39 +625,37 @@ func (pd *PitchDetector) ReadPCM(b []byte) (n int, err error) {
if err != nil {
return n, err
}
- var lastValue float64
var read int
sampleSize := pd.format.SampleSize()
for len(b[read:]) > sampleSize {
- pd.index++
vals, valReadBytes, err := pd.format.SampleFloat(b[read:])
if err != nil {
break
}
read += valReadBytes
- // ignore stereo audio; sorry it makes this really complicated
- val := vals[pd.Channel]
- if lastValue < 0 && val > 0 || val < 0 && lastValue > 0 {
- // we've crossed zero
- if !pd.crossedZero {
- pd.crossedZero = true
- } else {
- // assuming this is pitched audio (if it isn't we can't give a correct answer),
- // pd.index is now the number of samples since the last time this audio
- // stream crossed zero. The second last time this audio stream crossed zero defines how
- // frequently this audio is cycling-- the speed the audio cycles at defines the pitch
- // of the audio in hertz; our pitch constants above are also defined in hertz.
- periodLength := pd.index * 2
- samplesPerSecond := pd.format.SampleRate
- periodHz := 1 / (float64(periodLength) / float64(samplesPerSecond))
- pd.DetectedRawPitch = periodHz
- pd.DetectedPitch = Pitch(periodHz).Round()
+ for i, val := range vals {
+ pd.indices[i]++
+ if pd.lastValues[i] < 0 && val > 0 || val < 0 && pd.lastValues[i] > 0 {
+ // we've crossed zero
+ if !pd.crossedZero[i] {
+ pd.crossedZero[i] = true
+ } else {
+ // assuming this is pitched audio (if it isn't we can't give a correct answer),
+ // pd.index is now the number of samples since the last time this audio
+ // stream crossed zero. The second last time this audio stream crossed zero defines how
+ // frequently this audio is cycling-- the speed the audio cycles at defines the pitch
+ // of the audio in hertz; our pitch constants above are also defined in hertz.
+ periodLength := pd.indices[i] * 2
+ samplesPerSecond := pd.format.SampleRate
+ periodHz := 1 / (float64(periodLength) / float64(samplesPerSecond))
+ pd.DetectedRawPitches[i] = periodHz
+ pd.DetectedPitches[i] = Pitch(periodHz).Round()
+ }
+ pd.indices[i] = 0
}
- pd.index = 0
+ pd.lastValues[i] = val
}
- lastValue = val
}
- pd.lastValue = lastValue
return
}
@@ -676,17 +666,17 @@ func (pd *PitchDetector) ReadPCM(b []byte) (n int, err error) {
// fmt.Println(hz2, int(hz2))) // "C6", 1047
// }
//
-func (hz Pitch) Round() Pitch {
+func (p Pitch) Round() Pitch {
// binary search
i := sort.Search(len(allPitches)-1, func(i int) bool {
- return Pitch(hz) < allPitches[i]
+ return p < allPitches[i]
})
// adjust for near matches
// we know hz < allPitches[i]
if i == 0 {
return allPitches[i]
}
- if hz-allPitches[i-1] < allPitches[i]-hz {
+ if p-allPitches[i-1] < allPitches[i]-p {
return allPitches[i-1]
}
return allPitches[i]
diff --git a/audio/synth/source.go b/audio/synth/source.go
index 2c643486..48ec4948 100644
--- a/audio/synth/source.go
+++ b/audio/synth/source.go
@@ -3,7 +3,7 @@ package synth
import (
"time"
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/pcm"
)
// A Source stores necessary information for generating waveform data
diff --git a/audio/synth/waves.go b/audio/synth/waves.go
index 64b92d58..45695163 100644
--- a/audio/synth/waves.go
+++ b/audio/synth/waves.go
@@ -5,7 +5,7 @@ import (
"math"
"math/rand"
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/pcm"
)
// Wave functions take a set of options and return an audio
@@ -17,10 +17,10 @@ func phase(freq Pitch, i int, sampleRate uint32) float64 {
}
// Sin produces a Sin wave
-// __
-// -- --
-// / \
-//--__-- --__--
+// __
+// -- --
+// / \
+// --__-- --__--
func (s Source) Sin(opts ...Option) pcm.Reader {
return s.Wave(Source.SinWave, opts...)
}
@@ -37,9 +37,9 @@ func (s Source) Square(opts ...Option) pcm.Reader {
// pulse the time up and down will change so that 1/pulse time the wave will
// be up.
//
-// __ __
-// || ||
-// ____||____||____
+// __ __
+// || ||
+// ____||____||____
func (s Source) Pulse(pulse float64) func(opts ...Option) pcm.Reader {
return func(opts ...Option) pcm.Reader {
return s.Wave(PulseWave(pulse), opts...)
@@ -58,9 +58,9 @@ func PulseWave(pulse float64) Waveform {
// Saw produces a saw wave
//
-// ^ ^ ^
-// / | / | /
-// / |/ |/
+// ^ ^ ^
+// / | / | /
+// / |/ |/
func (s Source) Saw(opts ...Option) pcm.Reader {
return s.Wave(Source.SawWave, opts...)
}
@@ -71,9 +71,9 @@ func (s Source) SawWave(idx int) float64 {
// Triangle produces a Triangle wave
//
-// ^ ^
-// / \ / \
-// v v v
+// ^ ^
+// / \ / \
+// v v v
func (s Source) Triangle(opts ...Option) pcm.Reader {
return s.Wave(Source.TriangleWave, opts...)
}
@@ -87,12 +87,14 @@ func (s Source) TriangleWave(idx int) float64 {
return 3*s.Volume - m
}
+// Noise produces random audio data.
func (s Source) Noise(opts ...Option) pcm.Reader {
return s.Wave(Source.NoiseWave, opts...)
}
var _ Waveform = Source.NoiseWave
+// NoiseWave returns noise pcm data bounded by this source's volume.
func (s Source) NoiseWave(idx int) float64 {
return ((rand.Float64() * 2) - 1) * s.Volume
}
@@ -101,34 +103,16 @@ func (s Source) modPhase(idx int) float64 {
return math.Mod(s.Phase(idx), 2*math.Pi)
}
-// Could have pulse triangle
-
-type Wave8Reader struct {
- Source
- lastIndex int
- waveFunc func(s Source, idx int) int8
-}
-
-func (pr *Wave8Reader) ReadPCM(b []byte) (n int, err error) {
- bytesPerI8 := int(pr.Channels)
- for i := 0; i+bytesPerI8 <= len(b); i += bytesPerI8 {
- i8 := pr.waveFunc(pr.Source, pr.lastIndex)
- pr.lastIndex++
- for c := 0; c < int(pr.Channels); c++ {
- b[i+c] = byte(i8)
- }
- n += bytesPerI8
- }
- return
-}
-
+// A Waveform is a function that can report a point of audio data given some source parameters for generating the audio
+// and an index of where in the generated waveform the requested point lies
type Waveform func(s Source, idx int) float64
+// Wave converts a waveform function into a pcm.Reader
func (s Source) Wave(waveFn Waveform, opts ...Option) pcm.Reader {
switch s.Bits {
case 8:
s.Volume *= math.MaxInt8
- return &Wave8Reader{
+ return &wave8Reader{
Source: s.Update(opts...),
waveFunc: func(s Source, idx int) int8 {
return int8(waveFn(s, idx))
@@ -136,7 +120,7 @@ func (s Source) Wave(waveFn Waveform, opts ...Option) pcm.Reader {
}
case 32:
s.Volume *= math.MaxInt32
- return &Wave32Reader{
+ return &wave32Reader{
Source: s.Update(opts...),
waveFunc: func(s Source, idx int) int32 {
return int32(waveFn(s, idx))
@@ -146,7 +130,7 @@ func (s Source) Wave(waveFn Waveform, opts ...Option) pcm.Reader {
fallthrough
default:
s.Volume *= math.MaxInt16
- return &Wave16Reader{
+ return &wave16Reader{
Source: s.Update(opts...),
waveFunc: func(s Source, idx int) int16 {
return int16(waveFn(s, idx))
@@ -155,6 +139,8 @@ func (s Source) Wave(waveFn Waveform, opts ...Option) pcm.Reader {
}
}
+// MultiWave converts a series of waveform functions into a combined reader, outputting the average
+// of all of the source waveforms at any given index
func (s Source) MultiWave(waveFns []Waveform, opts ...Option) pcm.Reader {
return s.Wave(func(s Source, idx int) float64 {
var out float64
@@ -166,13 +152,32 @@ func (s Source) MultiWave(waveFns []Waveform, opts ...Option) pcm.Reader {
}, opts...)
}
-type Wave16Reader struct {
+type wave8Reader struct {
+ Source
+ lastIndex int
+ waveFunc func(s Source, idx int) int8
+}
+
+func (pr *wave8Reader) ReadPCM(b []byte) (n int, err error) {
+ bytesPerI8 := int(pr.Channels)
+ for i := 0; i+bytesPerI8 <= len(b); i += bytesPerI8 {
+ i8 := pr.waveFunc(pr.Source, pr.lastIndex)
+ pr.lastIndex++
+ for c := 0; c < int(pr.Channels); c++ {
+ b[i+c] = byte(i8)
+ }
+ n += bytesPerI8
+ }
+ return
+}
+
+type wave16Reader struct {
Source
lastIndex int
waveFunc func(s Source, idx int) int16
}
-func (pr *Wave16Reader) ReadPCM(b []byte) (n int, err error) {
+func (pr *wave16Reader) ReadPCM(b []byte) (n int, err error) {
bytesPerI16 := int(pr.Channels) * 2
for i := 0; i+bytesPerI16 <= len(b); i += bytesPerI16 {
i16 := pr.waveFunc(pr.Source, pr.lastIndex)
@@ -186,13 +191,13 @@ func (pr *Wave16Reader) ReadPCM(b []byte) (n int, err error) {
return
}
-type Wave32Reader struct {
+type wave32Reader struct {
Source
lastIndex int
waveFunc func(s Source, idx int) int32
}
-func (pr *Wave32Reader) ReadPCM(b []byte) (n int, err error) {
+func (pr *wave32Reader) ReadPCM(b []byte) (n int, err error) {
bytesPerF32 := int(pr.Channels) * 4
for i := 0; i+bytesPerF32 <= len(b); i += bytesPerF32 {
i32 := pr.waveFunc(pr.Source, pr.lastIndex)
diff --git a/audio/writer.go b/audio/writer.go
index 2af11c4c..07d07013 100644
--- a/audio/writer.go
+++ b/audio/writer.go
@@ -1,7 +1,7 @@
package audio
import (
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/pcm"
)
// NewWriter returns a writer which can accept audio streamed matching the given format
diff --git a/audio/writer_alsa.go b/audio/writer_alsa.go
index aeebbe2a..f26257a0 100644
--- a/audio/writer_alsa.go
+++ b/audio/writer_alsa.go
@@ -9,7 +9,7 @@ import (
"sync"
"github.com/oakmound/alsa"
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/pcm"
)
func newALSAWriter(f pcm.Format) (pcm.Writer, error) {
diff --git a/audio/writer_dsound.go b/audio/writer_dsound.go
index cf28decf..348d9d2a 100644
--- a/audio/writer_dsound.go
+++ b/audio/writer_dsound.go
@@ -7,9 +7,9 @@ import (
"io"
"sync"
- intdsound "github.com/oakmound/oak/v3/audio/internal/dsound"
- "github.com/oakmound/oak/v3/audio/pcm"
- "github.com/oakmound/oak/v3/oakerr"
+ intdsound "github.com/oakmound/oak/v4/audio/internal/dsound"
+ "github.com/oakmound/oak/v4/audio/pcm"
+ "github.com/oakmound/oak/v4/oakerr"
"github.com/oov/directsound-go/dsound"
)
diff --git a/audio/writer_js.go b/audio/writer_js.go
index cc7d6ef5..17cc428c 100644
--- a/audio/writer_js.go
+++ b/audio/writer_js.go
@@ -9,7 +9,7 @@ import (
"sync/atomic"
"syscall/js"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/oakerr"
)
func initOS() error {
diff --git a/audio/writer_other.go b/audio/writer_other.go
index 70b53202..b38e8d24 100644
--- a/audio/writer_other.go
+++ b/audio/writer_other.go
@@ -2,7 +2,7 @@
package audio
-import "github.com/oakmound/oak/v3/oakerr"
+import "github.com/oakmound/oak/v4/oakerr"
func initOS(driver Driver) error {
return oakerr.UnsupportedPlatform{
diff --git a/audio/writer_pulse.go b/audio/writer_pulse.go
index fc892648..490b9e42 100644
--- a/audio/writer_pulse.go
+++ b/audio/writer_pulse.go
@@ -11,7 +11,7 @@ import (
"github.com/jfreymuth/pulse"
"github.com/jfreymuth/pulse/proto"
- "github.com/oakmound/oak/v3/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/pcm"
)
// This mutex may be unneeded
diff --git a/collision/attachSpace.go b/collision/attachSpace.go
index e1a7a9e3..4955ea25 100644
--- a/collision/attachSpace.go
+++ b/collision/attachSpace.go
@@ -3,8 +3,8 @@ package collision
import (
"errors"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/physics"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/physics"
)
// An AttachSpace is a composable struct that provides attachment
diff --git a/collision/attachSpace_test.go b/collision/attachSpace_test.go
index 34eeb965..22a83a09 100644
--- a/collision/attachSpace_test.go
+++ b/collision/attachSpace_test.go
@@ -5,8 +5,8 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/physics"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/physics"
)
type aspace struct {
diff --git a/collision/filter.go b/collision/filter.go
index d0be173e..60beebf4 100644
--- a/collision/filter.go
+++ b/collision/filter.go
@@ -1,6 +1,6 @@
package collision
-import "github.com/oakmound/oak/v3/event"
+import "github.com/oakmound/oak/v4/event"
// A Filter will take a set of collision spaces
// and return the subset that match some requirement
diff --git a/collision/geom.go b/collision/geom.go
index 62901050..7a39ca2a 100644
--- a/collision/geom.go
+++ b/collision/geom.go
@@ -7,7 +7,7 @@ package collision
import (
"math"
- "github.com/oakmound/oak/v3/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
)
// minDist computes the square of the distance from a point to a rectangle.
diff --git a/collision/onCollision.go b/collision/onCollision.go
index 8c82aacc..bd4782da 100644
--- a/collision/onCollision.go
+++ b/collision/onCollision.go
@@ -3,7 +3,7 @@ package collision
import (
"errors"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/event"
)
// A Phase is a struct that other structs who want to use PhaseCollision
diff --git a/collision/onCollision_test.go b/collision/onCollision_test.go
index 110036d5..838e6b16 100644
--- a/collision/onCollision_test.go
+++ b/collision/onCollision_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/event"
)
type cphase struct {
diff --git a/collision/point.go b/collision/point.go
index 168110da..b165d183 100644
--- a/collision/point.go
+++ b/collision/point.go
@@ -1,6 +1,6 @@
package collision
-import "github.com/oakmound/oak/v3/alg/floatgeom"
+import "github.com/oakmound/oak/v4/alg/floatgeom"
// A Point is a specific point where
// collision occurred and a zone to identify
diff --git a/collision/ray/castFilter.go b/collision/ray/castFilter.go
index 71ba7d7b..6f41a22e 100644
--- a/collision/ray/castFilter.go
+++ b/collision/ray/castFilter.go
@@ -1,8 +1,8 @@
package ray
import (
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/event"
)
// A CastFilter is a function that can be applied to a Caster
diff --git a/collision/ray/castLimit.go b/collision/ray/castLimit.go
index 46bc245d..583249e4 100644
--- a/collision/ray/castLimit.go
+++ b/collision/ray/castLimit.go
@@ -1,8 +1,8 @@
package ray
import (
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/event"
)
// A CastLimit is a function that can be applied to
diff --git a/collision/ray/caster.go b/collision/ray/caster.go
index 90adcd5b..5d2ae1ac 100644
--- a/collision/ray/caster.go
+++ b/collision/ray/caster.go
@@ -3,8 +3,8 @@ package ray
import (
"math"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/collision"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/collision"
)
var (
diff --git a/collision/ray/caster_test.go b/collision/ray/caster_test.go
index c8e7fd00..25e9384c 100644
--- a/collision/ray/caster_test.go
+++ b/collision/ray/caster_test.go
@@ -4,8 +4,8 @@ import (
"reflect"
"testing"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/collision"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/collision"
)
func TestCasterScene(t *testing.T) {
diff --git a/collision/ray/coneCaster.go b/collision/ray/coneCaster.go
index 2f935cd1..db7108a0 100644
--- a/collision/ray/coneCaster.go
+++ b/collision/ray/coneCaster.go
@@ -1,9 +1,9 @@
package ray
import (
- "github.com/oakmound/oak/v3/alg"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/collision"
+ "github.com/oakmound/oak/v4/alg"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/collision"
)
var (
diff --git a/collision/ray/coneCaster_test.go b/collision/ray/coneCaster_test.go
index 2a73765b..b7d84ca3 100644
--- a/collision/ray/coneCaster_test.go
+++ b/collision/ray/coneCaster_test.go
@@ -7,9 +7,9 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/alg"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/collision"
+ "github.com/oakmound/oak/v4/alg"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/collision"
)
func TestConeCasterSettings(t *testing.T) {
diff --git a/collision/ray/raycast_test.go b/collision/ray/raycast_test.go
index 2060d0fc..a1eae541 100644
--- a/collision/ray/raycast_test.go
+++ b/collision/ray/raycast_test.go
@@ -3,15 +3,15 @@ package ray
import (
"testing"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/alg/range/floatrange"
- "github.com/oakmound/oak/v3/collision"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/span"
+ "github.com/oakmound/oak/v4/collision"
)
func TestEmptyRaycasts(t *testing.T) {
t.Skip()
collision.DefaultTree.Clear()
- vRange := floatrange.NewLinear(3, 359)
+ vRange := span.NewLinear(3.0, 359.0)
tests := 100
for i := 0; i < tests; i++ {
p1 := floatgeom.Point2{vRange.Poll(), vRange.Poll()}
diff --git a/collision/rtree.go b/collision/rtree.go
index 836ade53..d8e3a5d5 100644
--- a/collision/rtree.go
+++ b/collision/rtree.go
@@ -8,7 +8,7 @@ import (
"math"
"sort"
- "github.com/oakmound/oak/v3/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
)
// Rtree represents an R-tree, a balanced search tree for storing and querying
diff --git a/collision/rtree_test.go b/collision/rtree_test.go
index e2ab9662..76dd50aa 100644
--- a/collision/rtree_test.go
+++ b/collision/rtree_test.go
@@ -10,7 +10,7 @@ import (
"strings"
"testing"
- "github.com/oakmound/oak/v3/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
)
var (
diff --git a/collision/space.go b/collision/space.go
index c217ac48..7634abdc 100644
--- a/collision/space.go
+++ b/collision/space.go
@@ -1,9 +1,9 @@
package collision
import (
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/physics"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/physics"
)
// ID Types constant
diff --git a/collision/space_test.go b/collision/space_test.go
index 8c41e398..3a4fe14b 100644
--- a/collision/space_test.go
+++ b/collision/space_test.go
@@ -3,9 +3,9 @@ package collision
import (
"testing"
- "github.com/oakmound/oak/v3/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
- "github.com/oakmound/oak/v3/physics"
+ "github.com/oakmound/oak/v4/physics"
)
func TestSpaceFuncs(t *testing.T) {
diff --git a/collision/tree.go b/collision/tree.go
index e32cd5b4..70069a5a 100644
--- a/collision/tree.go
+++ b/collision/tree.go
@@ -4,8 +4,8 @@ import (
"errors"
"sync"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/oakerr"
)
// A Tree provides a space for managing collisions between rectangles
diff --git a/collision/tree_test.go b/collision/tree_test.go
index 26f025a9..8795ec8e 100644
--- a/collision/tree_test.go
+++ b/collision/tree_test.go
@@ -4,8 +4,8 @@ import (
"math/rand"
"testing"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/alg/range/floatrange"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/span"
)
func TestNewTreeInvalidChildren(t *testing.T) {
@@ -150,8 +150,8 @@ func randomSpace() *Space {
}
var (
- xRange = floatrange.NewLinear(0, 10000)
- yRange = floatrange.NewLinear(0, 10000)
- wRange = floatrange.NewLinear(1, 50)
- hRange = floatrange.NewLinear(1, 50)
+ xRange = span.NewLinear(0.0, 10000.0)
+ yRange = span.NewLinear(0.0, 10000.0)
+ wRange = span.NewLinear(1.0, 50.0)
+ hRange = span.NewLinear(1.0, 50.0)
)
diff --git a/config.go b/config.go
index 6e0a14b3..d6a43b41 100644
--- a/config.go
+++ b/config.go
@@ -4,32 +4,43 @@ import (
"encoding/json"
"io"
- "github.com/oakmound/oak/v3/fileutil"
- "github.com/oakmound/oak/v3/shiny/driver"
+ "github.com/oakmound/oak/v4/fileutil"
+ "github.com/oakmound/oak/v4/shiny/driver"
)
-// Config stores initialization settings for oak.
+// A Config defines the settings oak accepts on initialization. Some of these settings may be ignored depending
+// on the target platform.
type Config struct {
- Driver Driver `json:"-"`
- Assets Assets `json:"assets"`
- Debug Debug `json:"debug"`
- Screen Screen `json:"screen"`
- BatchLoadOptions BatchLoadOptions `json:"batchLoadOptions"`
- FrameRate int `json:"frameRate"`
- DrawFrameRate int `json:"drawFrameRate"`
- IdleDrawFrameRate int `json:"idleDrawFrameRate"`
- Language string `json:"language"`
- Title string `json:"title"`
- BatchLoad bool `json:"batchLoad"`
- GestureSupport bool `json:"gestureSupport"`
- LoadBuiltinCommands bool `json:"loadBuiltinCommands"`
- TrackInputChanges bool `json:"trackInputChanges"`
- EnableDebugConsole bool `json:"enableDebugConsole"`
- TopMost bool `json:"topmost"`
- Borderless bool `json:"borderless"`
- Fullscreen bool `json:"fullscreen"`
- SkipRNGSeed bool `json:"skip_rng_seed"`
- UnlimitedDrawFrameRate bool `json:"unlimitedDrawFrameRate"`
+ Driver Driver `json:"-"`
+ // Assets defines where assets should be loaded from by default. Defaults to
+ // 'assets/audio' and 'assets/images'.
+ Assets Assets `json:"assets"`
+ Debug Debug `json:"debug"`
+ Screen Screen `json:"screen"`
+ BatchLoadOptions BatchLoadOptions `json:"batchLoadOptions"`
+ // FrameRate, representing the rate enter frame events are triggered, defaults to 60.
+ FrameRate int `json:"frameRate"`
+ // DrawFrameRate is ignored on JS. It defaults to 60.
+ DrawFrameRate int `json:"drawFrameRate"`
+ // IdleDrawFrameRate defaults to 60. When a window goes out of focus, this setting can be lowered to
+ // reduce resource consumption by drawing.
+ IdleDrawFrameRate int `json:"idleDrawFrameRate"`
+ // Language defines the language oak logs are attempted to be translated to. Defaults to English.
+ Language string `json:"language"`
+ // Title defaults to 'Oak Window'.
+ Title string `json:"title"`
+ BatchLoad bool `json:"batchLoad"`
+ GestureSupport bool `json:"gestureSupport"`
+ LoadBuiltinCommands bool `json:"loadBuiltinCommands"`
+ TrackInputChanges bool `json:"trackInputChanges"`
+ // EnableDebugConsole is ignored on JS.
+ EnableDebugConsole bool `json:"enableDebugConsole"`
+ TopMost bool `json:"topmost"`
+ Borderless bool `json:"borderless"`
+ Fullscreen bool `json:"fullscreen"`
+ SkipRNGSeed bool `json:"skip_rng_seed"`
+ // UnlimitedDrawFrameRate is ignored on JS (it is effectively always true).
+ UnlimitedDrawFrameRate bool `json:"unlimitedDrawFrameRate"`
}
// NewConfig creates a config from a set of transformation options.
@@ -87,16 +98,10 @@ type Screen struct {
Height int `json:"height"`
Width int `json:"width"`
Scale float64 `json:"scale"`
- // Target sets the expected dimensions of the monitor the game will be opened on, in pixels.
- // If Fullscreen is false, then a scaling will be applied to correct the game screen size to be
- // appropriate for the Target size. If no TargetWidth or Height is provided, scaling will not
- // be adjusted.
- TargetWidth int `json:"targetHeight"`
- TargetHeight int `json:"targetWidth"`
}
// BatchLoadOptions is a json type storing customizations for batch loading.
-// These settings do not take effect unless batch load is true.
+// These settings do not take effect unless Config.BatchLoad is true.
type BatchLoadOptions struct {
BlankOutAudio bool `json:"blankOutAudio"`
MaxImageFileSize int64 `json:"maxImageFileSize"`
@@ -164,12 +169,6 @@ func (c Config) overwriteFrom(c2 Config) Config {
if c2.Screen.Scale != 0 {
c.Screen.Scale = c2.Screen.Scale
}
- if c2.Screen.TargetWidth != 0 {
- c.Screen.TargetWidth = c2.Screen.TargetWidth
- }
- if c2.Screen.TargetHeight != 0 {
- c.Screen.TargetHeight = c2.Screen.TargetHeight
- }
c.BatchLoadOptions.BlankOutAudio = c2.BatchLoadOptions.BlankOutAudio
if c2.BatchLoadOptions.MaxImageFileSize != 0 {
c.BatchLoadOptions.MaxImageFileSize = c2.BatchLoadOptions.MaxImageFileSize
diff --git a/config_test.go b/config_test.go
index bff8501c..527da619 100644
--- a/config_test.go
+++ b/config_test.go
@@ -5,6 +5,8 @@ import (
"os"
"path/filepath"
"testing"
+
+ "github.com/oakmound/oak/v4/shiny/screen"
)
func TestDefaultConfigFileMatchesEmptyConfig(t *testing.T) {
@@ -109,14 +111,13 @@ func TestConfig_overwriteFrom(t *testing.T) {
Filter: "filter",
},
Screen: Screen{
- X: 1,
- Y: 1,
- TargetWidth: 1,
- TargetHeight: 1,
+ X: 1,
+ Y: 1,
},
BatchLoadOptions: BatchLoadOptions{
MaxImageFileSize: 10000,
},
+ Driver: func(f func(screen.Screen)) { panic("fake") },
}
c1 := Config{}
c1.overwriteFrom(c2)
diff --git a/debugstream/commands.go b/debugstream/commands.go
index 8ca6e69a..c37fd31f 100644
--- a/debugstream/commands.go
+++ b/debugstream/commands.go
@@ -10,7 +10,7 @@ import (
"strings"
"sync"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/oakerr"
)
// ScopedCommands for the debug stream commands.
diff --git a/debugstream/defaultcommands.go b/debugstream/defaultcommands.go
index 3433e175..b189b731 100644
--- a/debugstream/defaultcommands.go
+++ b/debugstream/defaultcommands.go
@@ -5,7 +5,7 @@ import (
"io"
"sync"
- "github.com/oakmound/oak/v3/window"
+ "github.com/oakmound/oak/v4/window"
)
var (
@@ -28,7 +28,7 @@ func AddCommand(c Command) error {
}
// AttachToStream if possible to start consuming the stream
-// and executing commands per the stored infomraiton in the ScopeCommands.
+// and executing commands per the stored information in the ScopeCommands.
func AttachToStream(ctx context.Context, input io.Reader, output io.Writer) {
checkOrCreateDefaults()
DefaultCommands.AttachToStream(ctx, input, output)
diff --git a/debugstream/scopeHelper.go b/debugstream/scopeHelper.go
index 465e3c93..0dd9a6dd 100644
--- a/debugstream/scopeHelper.go
+++ b/debugstream/scopeHelper.go
@@ -5,15 +5,15 @@ import (
"strconv"
"strings"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/debugtools"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/oakerr"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/render/mod"
- "github.com/oakmound/oak/v3/window"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/debugtools"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/oakerr"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/render/mod"
+ "github.com/oakmound/oak/v4/window"
)
// AddDefaultsForScope for debugging.
@@ -39,8 +39,9 @@ func moveWindow(w window.Window) func([]string) string {
InputName: "coordinates",
}.Error()
}
- width := parseTokenAsInt(sub, 2, w.Width())
- height := parseTokenAsInt(sub, 3, w.Height())
+ bds := w.Bounds()
+ width := parseTokenAsInt(sub, 2, bds.X())
+ height := parseTokenAsInt(sub, 3, bds.Y())
v := w.Viewport()
x := parseTokenAsInt(sub, 0, v.X())
y := parseTokenAsInt(sub, 1, v.Y())
@@ -85,7 +86,7 @@ func mouseDetails(w window.Window) func(*mouse.Event) event.Response {
if len(results) == 0 {
results = mouse.Hits(loc)
}
- cm := w.GetCallerMap()
+ cm := w.EventHandler().GetCallerMap()
if len(results) > 0 {
i := results[0].CID
diff --git a/debugstream/scopeHelper_test.go b/debugstream/scopeHelper_test.go
index 62ed8485..955589a1 100644
--- a/debugstream/scopeHelper_test.go
+++ b/debugstream/scopeHelper_test.go
@@ -6,11 +6,11 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/debugtools"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/window"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/debugtools"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/window"
)
type fakeWindow struct {
@@ -45,12 +45,8 @@ func (f *fakeWindow) MoveWindow(x, y, w, h int) error {
return nil
}
-func (f *fakeWindow) Width() int {
- return 1
-}
-
-func (f *fakeWindow) Height() int {
- return 1
+func (f *fakeWindow) Bounds() intgeom.Point2 {
+ return intgeom.Point2{1, 1}
}
func (f *fakeWindow) Viewport() intgeom.Point2 {
diff --git a/debugtools/inputviz/joystick.go b/debugtools/inputviz/joystick.go
index a1c0c16f..ec2e382f 100644
--- a/debugtools/inputviz/joystick.go
+++ b/debugtools/inputviz/joystick.go
@@ -9,14 +9,14 @@ import (
"math"
"time"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/joystick"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/render/mod"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/joystick"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/render/mod"
+ "github.com/oakmound/oak/v4/scene"
)
//go:embed controllerOutline.png
diff --git a/debugtools/inputviz/keyboard.go b/debugtools/inputviz/keyboard.go
index d259dd69..165271f7 100644
--- a/debugtools/inputviz/keyboard.go
+++ b/debugtools/inputviz/keyboard.go
@@ -3,11 +3,11 @@ package inputviz
import (
"image/color"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
type KeyboardLayout interface {
diff --git a/debugtools/inputviz/mouse.go b/debugtools/inputviz/mouse.go
index 350c4ebf..65c81310 100644
--- a/debugtools/inputviz/mouse.go
+++ b/debugtools/inputviz/mouse.go
@@ -7,11 +7,11 @@ import (
"sync"
"time"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
type Mouse struct {
diff --git a/debugtools/mouse.go b/debugtools/mouse.go
index 2c8aae56..eb8391ac 100644
--- a/debugtools/mouse.go
+++ b/debugtools/mouse.go
@@ -1,18 +1,18 @@
package debugtools
import (
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/scene"
)
// DebugMouseRelease will print the position and button pressed of the mouse when the mouse is released, if the given
// key is held down at the time. If 0 is given, it will always be printed
func DebugMouseRelease(ctx *scene.Context, k key.Code) {
event.GlobalBind(ctx, mouse.Release, func(mev *mouse.Event) event.Response {
- if k == 0 || ctx.KeyState.IsDown(k) {
+ if k == 0 || ctx.IsDown(k) {
dlog.Info(mev)
}
return 0
diff --git a/debugtools/renderable.go b/debugtools/renderable.go
index cdf18499..02371cd1 100644
--- a/debugtools/renderable.go
+++ b/debugtools/renderable.go
@@ -1,7 +1,7 @@
package debugtools
import (
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/render"
"golang.org/x/sync/syncmap"
)
diff --git a/debugtools/tree.go b/debugtools/tree.go
index 46a5a969..7340eeba 100644
--- a/debugtools/tree.go
+++ b/debugtools/tree.go
@@ -4,10 +4,10 @@ import (
"image/color"
"image/draw"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
- "github.com/oakmound/oak/v3/collision"
+ "github.com/oakmound/oak/v4/collision"
)
// NewRTree creates a wrapper around a tree that supports coloring the spaces
@@ -46,7 +46,8 @@ type Rtree struct {
// GetDims returns the total possible area to draw this on.
func (r *Rtree) GetDims() (int, int) {
- return r.Context.Window.Width(), r.Context.Window.Height()
+ bds := r.Context.Window.Bounds()
+ return bds.X(), bds.Y()
}
// Draw will draw the collision outlines
@@ -55,12 +56,13 @@ func (r *Rtree) Draw(buff draw.Image, xOff, yOff float64) {
return
}
vp := r.Context.Window.Viewport()
+ bds := r.Context.Window.Bounds()
// Get all spaces on screen
screen := collision.NewUnassignedSpace(
float64(vp.X()),
float64(vp.Y()),
- float64(r.Context.Window.Width()+vp.X()),
- float64(r.Context.Window.Height()+vp.Y()))
+ float64(bds.X()+vp.X()),
+ float64(bds.Y()+vp.Y()))
hits := r.Tree.Hits(screen)
// Draw spaces that are on screen (as outlines)
for _, h := range hits {
diff --git a/default.go b/default.go
index 47732101..4b14c2f0 100644
--- a/default.go
+++ b/default.go
@@ -5,11 +5,11 @@ import (
"sync"
"time"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
var defaultWindow *Window
@@ -55,16 +55,16 @@ func SetViewportBounds(rect intgeom.Rect2) {
defaultWindow.SetViewportBounds(rect)
}
-// ShiftScreen calls ShiftScreen on the default window.
-func ShiftScreen(x, y int) {
+// ShiftViewport calls ShiftViewport on the default window.
+func ShiftViewport(pt intgeom.Point2) {
initDefaultWindow()
- defaultWindow.ShiftScreen(x, y)
+ defaultWindow.ShiftViewport(pt)
}
-// SetScreen calls SetScreen on the default window.
-func SetScreen(x, y int) {
+// SetViewport calls SetViewport on the default window.
+func SetViewport(pt intgeom.Point2) {
initDefaultWindow()
- defaultWindow.SetScreen(x, y)
+ defaultWindow.SetViewport(pt)
}
// UpdateViewSize calls UpdateViewSize on the default window.
@@ -97,20 +97,8 @@ func SetColorBackground(img image.Image) {
defaultWindow.SetColorBackground(img)
}
-// GetBackgroundImage calls GetBackgroundImage on the default window.
-func GetBackgroundImage() image.Image {
+// Bounds returns the default window's boundary.
+func Bounds() intgeom.Point2 {
initDefaultWindow()
- return defaultWindow.GetBackgroundImage()
-}
-
-// Width calls Width on the default window.
-func Width() int {
- initDefaultWindow()
- return defaultWindow.Width()
-}
-
-// Height calls Height on the default window.
-func Height() int {
- initDefaultWindow()
- return defaultWindow.Height()
+ return defaultWindow.Bounds()
}
diff --git a/default_test.go b/default_test.go
new file mode 100644
index 00000000..00a9feb4
--- /dev/null
+++ b/default_test.go
@@ -0,0 +1,32 @@
+package oak
+
+import (
+ "testing"
+
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
+)
+
+func TestDefaultFunctions(t *testing.T) {
+ t.Run("SuperficialCoverage", func(t *testing.T) {
+ IsDown(key.A)
+ IsHeld(key.A)
+ AddScene("test", scene.Scene{
+ Start: func(ctx *scene.Context) {
+ ScreenShot()
+ ctx.Window.Quit()
+ },
+ })
+ SetViewportBounds(intgeom.NewRect2(0, 0, 1, 1))
+ SetViewport(intgeom.Point2{})
+ ShiftViewport(intgeom.Point2{})
+ UpdateViewSize(10, 10)
+ Bounds()
+ SetLoadingRenderable(render.EmptyRenderable())
+ SetColorBackground(nil)
+ SetBackground(render.EmptyRenderable())
+ Init("test")
+ })
+}
diff --git a/dlog/default.go b/dlog/default.go
index e3e2a666..bd042a1e 100644
--- a/dlog/default.go
+++ b/dlog/default.go
@@ -10,7 +10,7 @@ import (
"strings"
"sync"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/oakerr"
)
var (
@@ -109,7 +109,9 @@ func (l *logger) SetFilter(filter func(string) bool) {
// will be printed.
func (l *logger) SetLogLevel(level Level) error {
if level < NONE || level > VERBOSE {
- return oakerr.InvalidInput{}
+ return oakerr.InvalidInput{
+ InputName: "level",
+ }
}
l.debugLevel = level
return nil
diff --git a/dlog/default_test.go b/dlog/default_test.go
index 8d938658..8ad57f21 100644
--- a/dlog/default_test.go
+++ b/dlog/default_test.go
@@ -5,7 +5,7 @@ import (
"strings"
"testing"
- "github.com/oakmound/oak/v3/dlog"
+ "github.com/oakmound/oak/v4/dlog"
)
func TestLogger(t *testing.T) {
diff --git a/dlog/dlog_test.go b/dlog/dlog_test.go
index 4ca38f1a..5791d1ff 100644
--- a/dlog/dlog_test.go
+++ b/dlog/dlog_test.go
@@ -6,7 +6,7 @@ import (
"os"
"testing"
- "github.com/oakmound/oak/v3/dlog"
+ "github.com/oakmound/oak/v4/dlog"
)
func TestErrorCheck(t *testing.T) {
diff --git a/dlog/levels.go b/dlog/levels.go
index 01559a89..dde2c489 100644
--- a/dlog/levels.go
+++ b/dlog/levels.go
@@ -3,7 +3,7 @@ package dlog
import (
"strings"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/oakerr"
)
// Level represents the levels a debug message can have
diff --git a/dlog/levels_test.go b/dlog/levels_test.go
index bd3cb585..95923ecf 100644
--- a/dlog/levels_test.go
+++ b/dlog/levels_test.go
@@ -3,7 +3,7 @@ package dlog_test
import (
"testing"
- "github.com/oakmound/oak/v3/dlog"
+ "github.com/oakmound/oak/v4/dlog"
)
func TestLevelsString(t *testing.T) {
diff --git a/dlog/strings.go b/dlog/strings.go
index f0c19433..ee65f911 100644
--- a/dlog/strings.go
+++ b/dlog/strings.go
@@ -1,6 +1,6 @@
package dlog
-import "github.com/oakmound/oak/v3/oakerr"
+import "github.com/oakmound/oak/v4/oakerr"
type logCode int
diff --git a/doc.go b/doc.go
deleted file mode 100644
index 572588d8..00000000
--- a/doc.go
+++ /dev/null
@@ -1,4 +0,0 @@
-// Package oak is a game engine. It provides scene control, control over windows
-// and what is drawn to them, propagates regular events to evaluate game logic,
-// and so on.
-package oak
diff --git a/drawLoop.go b/drawLoop.go
index c76d49c9..23ff333e 100644
--- a/drawLoop.go
+++ b/drawLoop.go
@@ -57,8 +57,6 @@ func (w *Window) drawLoop() {
loadingSelectUnlimited:
for {
select {
- case <-w.ParentContext.Done():
- return
case <-w.quitCh:
return
case <-w.drawCh:
@@ -92,8 +90,6 @@ func (w *Window) drawLoop() {
loadingSelect:
for {
select {
- case <-w.ParentContext.Done():
- return
case <-w.quitCh:
return
case <-w.drawCh:
@@ -115,7 +111,7 @@ func (w *Window) drawLoop() {
}
func (w *Window) publish() {
- w.prePublish(w, w.windowTextures[w.bufferIdx])
+ w.prePublish(w.winBuffers[w.bufferIdx].RGBA())
w.windowTextures[w.bufferIdx].Upload(zeroPoint, w.winBuffers[w.bufferIdx], w.winBuffers[w.bufferIdx].Bounds())
w.Window.Scale(w.windowRect, w.windowTextures[w.bufferIdx], w.windowTextures[w.bufferIdx].Bounds(), draw.Src)
w.Window.Publish()
@@ -124,9 +120,9 @@ func (w *Window) publish() {
w.bufferIdx = (w.bufferIdx + 1) % bufferCount
}
-// DoBetweenDraws will execute the given function in-between draw frames
+// DoBetweenDraws will execute the given function in-between draw frames. It will prevent draws from happening until
+// the provided function has terminated. DoBetweenDraws will block until the provided function is called within the
+// draw loop's schedule, but will not wait for that function itself to terminate.
func (w *Window) DoBetweenDraws(f func()) {
- go func() {
- w.betweenDrawCh <- f
- }()
+ w.betweenDrawCh <- f
}
diff --git a/driver.go b/driver.go
index 87f04f3c..94ff3afa 100644
--- a/driver.go
+++ b/driver.go
@@ -1,7 +1,7 @@
package oak
import (
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
// A Driver is a function which can take in our lifecycle function
diff --git a/entities/entity.go b/entities/entity.go
index f6e4f60d..02e14106 100644
--- a/entities/entity.go
+++ b/entities/entity.go
@@ -3,13 +3,13 @@ package entities
import (
"image/color"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/render/mod"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/render/mod"
+ "github.com/oakmound/oak/v4/scene"
)
type Generator struct {
@@ -22,8 +22,6 @@ type Generator struct {
Color color.Color
Renderable render.Renderable
- ScaleRenderable *mod.Resampling
-
Mod mod.Mod
Label collision.Label
@@ -243,12 +241,6 @@ func New(ctx *scene.Context, opts ...Option) *Entity {
e.Renderable = m.Modify(g.Mod)
}
- if g.ScaleRenderable != nil {
- if m, ok := g.Renderable.(render.Modifiable); ok {
- e.Renderable = m.Modify(mod.Resize(int(g.Dimensions[0]), int(g.Dimensions[1]), *g.ScaleRenderable))
- }
- }
-
e.Renderable.SetPos(e.X(), e.Y())
if g.Parent == nil {
diff --git a/entities/move.go b/entities/move.go
index eb17d039..05b71c2b 100644
--- a/entities/move.go
+++ b/entities/move.go
@@ -1,8 +1,9 @@
package entities
import (
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/key"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/key"
)
// WASD moves the given mover based on its speed as W,A,S, and D are pressed
@@ -18,16 +19,16 @@ func Arrows(mvr *Entity) {
// TopDown moves the given mover based on its speed as the given keys are pressed
func TopDown(mvr *Entity, up, down, left, right key.Code) {
mvr.Delta = floatgeom.Point2{}
- if mvr.ctx.KeyState.IsDown(up) {
+ if mvr.ctx.IsDown(up) {
mvr.Delta[1] -= mvr.Speed[1]
}
- if mvr.ctx.KeyState.IsDown(down) {
+ if mvr.ctx.IsDown(down) {
mvr.Delta[1] += mvr.Speed[1]
}
- if mvr.ctx.KeyState.IsDown(left) {
+ if mvr.ctx.IsDown(left) {
mvr.Delta[0] -= mvr.Speed[0]
}
- if mvr.ctx.KeyState.IsDown(right) {
+ if mvr.ctx.IsDown(right) {
mvr.Delta[0] += mvr.Speed[0]
}
mvr.ShiftDelta()
@@ -36,10 +37,10 @@ func TopDown(mvr *Entity, up, down, left, right key.Code) {
// CenterScreenOn will cause the screen to center on the given mover, obeying
// viewport limits if they have been set previously
func CenterScreenOn(mvr *Entity) {
- mvr.ctx.Window.SetScreen(
- int(mvr.X())-mvr.ctx.Window.Width()/2,
- int(mvr.Y())-mvr.ctx.Window.Height()/2,
- )
+ bds := mvr.ctx.Window.Bounds()
+ pos := intgeom.Point2{int(mvr.X()), int(mvr.Y())}
+ target := pos.Sub(bds).DivConst(2)
+ mvr.ctx.Window.SetViewport(target)
}
// Limit restricts the movement of the mover to stay within a given rectangle
diff --git a/entities/opts_gen.go b/entities/opts_gen.go
index 1be9efbf..4be0dbfe 100644
--- a/entities/opts_gen.go
+++ b/entities/opts_gen.go
@@ -5,11 +5,11 @@ package entities
import (
"image/color"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/render/mod"
)
type Option func(Generator) Generator
@@ -56,13 +56,6 @@ func WithRenderable(v render.Renderable) Option {
}
}
-func WithScaleRenderable(v *mod.Resampling) Option {
- return func(s Generator) Generator {
- s.ScaleRenderable = v
- return s
- }
-}
-
func WithMod(v mod.Mod) Option {
return func(s Generator) Generator {
s.Mod = v
diff --git a/entities/x/btn/button.go b/entities/x/btn/button.go
index 460aa762..5f8b34a2 100644
--- a/entities/x/btn/button.go
+++ b/entities/x/btn/button.go
@@ -4,15 +4,15 @@ import (
"fmt"
"image/color"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/entities"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/render/mod"
- "github.com/oakmound/oak/v3/scene"
- "github.com/oakmound/oak/v3/shape"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/entities"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/render/mod"
+ "github.com/oakmound/oak/v4/scene"
+ "github.com/oakmound/oak/v4/shape"
)
// A Generator defines the variables used to create buttons from optional arguments
diff --git a/entities/x/btn/grid/grid.go b/entities/x/btn/grid/grid.go
index 87cefc28..1a83c008 100644
--- a/entities/x/btn/grid/grid.go
+++ b/entities/x/btn/grid/grid.go
@@ -2,9 +2,9 @@
package grid
import (
- "github.com/oakmound/oak/v3/entities"
- "github.com/oakmound/oak/v3/entities/x/btn"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/entities"
+ "github.com/oakmound/oak/v4/entities/x/btn"
+ "github.com/oakmound/oak/v4/scene"
)
// A Grid is a 2D slice of entities
diff --git a/entities/x/btn/grid/option.go b/entities/x/btn/grid/option.go
index 47b02609..d044f5f0 100644
--- a/entities/x/btn/grid/option.go
+++ b/entities/x/btn/grid/option.go
@@ -1,8 +1,8 @@
package grid
import (
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/entities/x/btn"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/entities/x/btn"
)
// An Option modifies a generator prior to grid generation
diff --git a/entities/x/btn/option.go b/entities/x/btn/option.go
index 4d846ad6..97e8e440 100644
--- a/entities/x/btn/option.go
+++ b/entities/x/btn/option.go
@@ -3,14 +3,14 @@ package btn
import (
"image/color"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/entities"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/scene"
-
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/entities"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/scene"
+
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/render/mod"
)
// And combines a variadic number of options
diff --git a/entities/x/btn/textOptions.go b/entities/x/btn/textOptions.go
index e48e2683..c8ba4bfa 100644
--- a/entities/x/btn/textOptions.go
+++ b/entities/x/btn/textOptions.go
@@ -3,7 +3,7 @@ package btn
import (
"fmt"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/render"
)
//Text sets the text of the button to be generated
diff --git a/event/bind.go b/event/bind.go
index 4e66fc38..9e8276d2 100644
--- a/event/bind.go
+++ b/event/bind.go
@@ -3,7 +3,7 @@ package event
import (
"sync/atomic"
- "github.com/oakmound/oak/v3/dlog"
+ "github.com/oakmound/oak/v4/dlog"
)
// Q: Why do Bind / Unbind / etc not immediately take effect?
@@ -46,8 +46,9 @@ type BindID int64
// UnsafeBind registers a callback function to be called whenever the provided event is triggered
// against this bus. The binding is concurrently bound, and therefore may not be immediately
-// available to be triggered. When Reset is called on a Bus, all prior bindings are unbound. This
-// call is 'unsafe' because UnsafeBindables use bare interface{} types.
+// available to be triggered. When Reset is called on a Bus, all prior bindings are unbound and any
+// concurrent calls to UnsafeBind will not take effect. This call is 'unsafe' because UnsafeBindables
+// use bare interface{} types.
func (bus *Bus) UnsafeBind(eventID UnsafeEventID, callerID CallerID, fn UnsafeBindable) Binding {
expectedResetCount := bus.resetCount
bindID := BindID(atomic.AddInt64(bus.nextBindID, 1))
@@ -73,7 +74,7 @@ func (bus *Bus) UnsafeBind(eventID UnsafeEventID, callerID CallerID, fn UnsafeBi
}
}
-// PersistentBind acts like UnsafeBind, but cause Bind to be called with these inputs after a Bus is Reset, i.e.
+// PersistentBind calls UnsafeBind, and causes UnsafeBind to be called with these inputs when a Bus is Reset, i.e.
// persisting the binding through bus resets. Unbinding this will not stop it from being rebound on the next
// Bus Reset-- ClearPersistentBindings will. If called concurrently during a bus Reset, the request may not be
// bound until the next bus Reset.
diff --git a/event/bind_test.go b/event/bind_test.go
index 4fe1d942..12d0414e 100644
--- a/event/bind_test.go
+++ b/event/bind_test.go
@@ -4,7 +4,7 @@ import (
"sync/atomic"
"testing"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/event"
)
func TestBus_UnsafeBind(t *testing.T) {
diff --git a/event/bus_test.go b/event/bus_test.go
index 8b227ddb..31441f7f 100644
--- a/event/bus_test.go
+++ b/event/bus_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/event"
)
func TestNewBus(t *testing.T) {
@@ -90,7 +90,7 @@ func TestBus_EnterLoop(t *testing.T) {
})
<-b1.Bound
cancel := event.EnterLoop(b, 50*time.Millisecond)
- time.Sleep(1 * time.Second)
+ time.Sleep(1*time.Second + 15*time.Millisecond)
cancel()
if calls != 20 {
t.Fatal(expectedError("calls", 20, calls))
diff --git a/event/caller_test.go b/event/caller_test.go
index 10707660..4011981a 100644
--- a/event/caller_test.go
+++ b/event/caller_test.go
@@ -4,7 +4,7 @@ import (
"math/rand"
"testing"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/event"
)
func TestCallerID_CID(t *testing.T) {
diff --git a/event/handler.go b/event/handler.go
index 5537121d..6a28fd03 100644
--- a/event/handler.go
+++ b/event/handler.go
@@ -16,4 +16,6 @@ type Handler interface {
UnbindAllFrom(CallerID) <-chan struct{}
SetCallerMap(*CallerMap)
GetCallerMap() *CallerMap
+ PersistentBind(eventID UnsafeEventID, callerID CallerID, fn UnsafeBindable) Binding
+ ClearPersistentBindings()
}
diff --git a/event/response_test.go b/event/response_test.go
index 7064e353..4d3616ec 100644
--- a/event/response_test.go
+++ b/event/response_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/event"
)
func TestBindingResponses(t *testing.T) {
diff --git a/event/trigger_test.go b/event/trigger_test.go
index 2dae1215..aa233564 100644
--- a/event/trigger_test.go
+++ b/event/trigger_test.go
@@ -7,7 +7,7 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/event"
)
func TestMain(m *testing.M) {
diff --git a/examples/bezier/main.go b/examples/bezier/main.go
index eca12490..7abe6ebe 100644
--- a/examples/bezier/main.go
+++ b/examples/bezier/main.go
@@ -5,13 +5,13 @@ import (
"image/color"
"strconv"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/debugstream"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
- "github.com/oakmound/oak/v3/shape"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/debugstream"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
+ "github.com/oakmound/oak/v4/shape"
)
var (
@@ -97,7 +97,7 @@ func bezierDrawRec(b shape.Bezier, list *render.CompositeM, alpha uint8) {
bezierDrawRec(bzn.Right, list, uint8(float64(alpha)*.5))
case shape.BezierPoint:
sp := render.NewColorBox(5, 5, color.RGBA{255, 255, 255, 255})
- sp.SetPos(bzn.X()-2, bzn.Y()-2)
+ sp.SetPos(bzn[0]-2, bzn[1]-2)
list.Append(sp)
}
}
diff --git a/examples/blank/main.go b/examples/blank/main.go
index 9e9fceee..6de09b14 100644
--- a/examples/blank/main.go
+++ b/examples/blank/main.go
@@ -5,9 +5,9 @@ import (
"net/http"
_ "net/http/pprof"
- "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
// This example is a blank, default scene with a pprof server. Useful for
diff --git a/examples/click-propagation/main.go b/examples/click-propagation/main.go
index 7ada4e5a..973c3095 100644
--- a/examples/click-propagation/main.go
+++ b/examples/click-propagation/main.go
@@ -6,18 +6,20 @@ import (
"image/color"
"image/draw"
- "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
// This example demonstrates the use of the Propagated boolean on
// mouse event payloads to prevent mouse interactions from falling
// through to lower UI elements after interacting with a higher layer
+// TODO: make the boxes here more real, more like a real UI
+
func main() {
oak.AddScene("click-propagation", scene.Scene{
Start: func(ctx *scene.Context) {
diff --git a/examples/clipboard/go.mod b/examples/clipboard/go.mod
index e0670ea7..1f6ce691 100644
--- a/examples/clipboard/go.mod
+++ b/examples/clipboard/go.mod
@@ -4,7 +4,7 @@ go 1.18
require (
github.com/atotto/clipboard v0.1.4
- github.com/oakmound/oak/v3 v3.0.0-alpha.1
+ github.com/oakmound/oak/v4 v4.0.0-alpha.1
)
require (
@@ -12,21 +12,18 @@ require (
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 // indirect
github.com/disintegration/gift v1.2.1 // indirect
- github.com/eaburns/bit v0.0.0-20131029213740-7bd5cd37375d // indirect
- github.com/eaburns/flac v0.0.0-20171003200620-9a6fb92396d1 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220320163800-277f93cfa958 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
- github.com/hajimehoshi/go-mp3 v0.3.2 // indirect
github.com/jfreymuth/pulse v0.1.0 // indirect
github.com/oakmound/alsa v0.0.2 // indirect
github.com/oakmound/libudev v0.2.1 // indirect
github.com/oakmound/w32 v2.1.0+incompatible // indirect
github.com/oov/directsound-go v0.0.0-20141101201356-e53e59c700bf // indirect
- golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
+ golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd // indirect
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a // indirect
golang.org/x/mobile v0.0.0-20220325161704-447654d348e3 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb // indirect
)
-replace github.com/oakmound/oak/v3 => ../..
+replace github.com/oakmound/oak/v4 => ../..
diff --git a/examples/clipboard/go.sum b/examples/clipboard/go.sum
index 22b7be7c..31afb773 100644
--- a/examples/clipboard/go.sum
+++ b/examples/clipboard/go.sum
@@ -9,17 +9,10 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc=
github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
-github.com/eaburns/bit v0.0.0-20131029213740-7bd5cd37375d h1:HB5J9+f1xpkYLgWQ/RqEcbp3SEufyOIMYLoyKNKiG7E=
-github.com/eaburns/bit v0.0.0-20131029213740-7bd5cd37375d/go.mod h1:CHkHWWZ4kbGY6jEy1+qlitDaCtRgNvCOQdakj/1Yl/Q=
-github.com/eaburns/flac v0.0.0-20171003200620-9a6fb92396d1 h1:wl/ggSfTHqAy46hyzw1IlrUYwjqmXYvbJyPdH3rT7YE=
-github.com/eaburns/flac v0.0.0-20171003200620-9a6fb92396d1/go.mod h1:frG94byMNy+1CgGrQ25dZ+17tf98EN+OYBQL4Zh612M=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220320163800-277f93cfa958 h1:TL70PMkdPCt9cRhKTqsm+giRpgrd0IGEj763nNr2VFY=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220320163800-277f93cfa958/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
-github.com/hajimehoshi/go-mp3 v0.3.2 h1:xSYNE2F3lxtOu9BRjCWHHceg7S91IHfXfXp5+LYQI7s=
-github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
-github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/jfreymuth/pulse v0.1.0 h1:KN38/9hoF9PJvP5DpEVhMRKNuwnJUonc8c9ARorRXUA=
github.com/jfreymuth/pulse v0.1.0/go.mod h1:cpYspI6YljhkUf1WLXLLDmeaaPFc3CnGLjDZf9dZ4no=
github.com/oakmound/alsa v0.0.2 h1:JbOUckkJqVvhABth7qy2JgAjqsWuBPggyoYOk1L6eK0=
@@ -34,15 +27,14 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
+golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd h1:zVFyTKZN/Q7mNRWSs1GOYnHM9NiFSJ54YVRsD0rNWT4=
+golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a h1:LnH9RNcpPv5Kzi15lXg42lYMPUf0x8CuPv1YnvBWZAg=
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20220325161704-447654d348e3 h1:ZDL7hDvJEQEcHVkoZawKmRUgbqn1pOIzb8EinBh5csU=
golang.org/x/mobile v0.0.0-20220325161704-447654d348e3/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
@@ -55,9 +47,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/examples/clipboard/main.go b/examples/clipboard/main.go
index fbd31d67..07751f20 100644
--- a/examples/clipboard/main.go
+++ b/examples/clipboard/main.go
@@ -4,14 +4,14 @@ import (
"fmt"
"github.com/atotto/clipboard"
- "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/entities"
- "github.com/oakmound/oak/v3/entities/x/btn"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/entities"
+ "github.com/oakmound/oak/v4/entities/x/btn"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
func main() {
diff --git a/examples/collision-demo/main.go b/examples/collision-demo/main.go
index 809ea868..30998bfb 100644
--- a/examples/collision-demo/main.go
+++ b/examples/collision-demo/main.go
@@ -4,15 +4,15 @@ import (
"image/color"
"time"
- "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/entities"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
- "github.com/oakmound/oak/v3/shake"
+ "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/entities"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
+ "github.com/oakmound/oak/v4/shake"
)
const (
diff --git a/examples/custom-cursor/main.go b/examples/custom-cursor/main.go
index a7d391a9..d9ed51bf 100644
--- a/examples/custom-cursor/main.go
+++ b/examples/custom-cursor/main.go
@@ -4,11 +4,11 @@ import (
"fmt"
"image/color"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
func main() {
diff --git a/examples/error-scene/main.go b/examples/error-scene/main.go
index 02de8666..a188ec48 100644
--- a/examples/error-scene/main.go
+++ b/examples/error-scene/main.go
@@ -1,9 +1,9 @@
package main
import (
- "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
func main() {
diff --git a/examples/fallback-font/go.mod b/examples/fallback-font/go.mod
index cb22a8de..a3506b83 100644
--- a/examples/fallback-font/go.mod
+++ b/examples/fallback-font/go.mod
@@ -4,7 +4,7 @@ go 1.18
require (
github.com/flopp/go-findfont v0.0.0-20201114153133-e7393a00c15b
- github.com/oakmound/oak/v3 v3.0.0-alpha.1
+ github.com/oakmound/oak/v4 v4.0.0-alpha.1
)
require (
@@ -12,21 +12,18 @@ require (
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 // indirect
github.com/disintegration/gift v1.2.1 // indirect
- github.com/eaburns/bit v0.0.0-20131029213740-7bd5cd37375d // indirect
- github.com/eaburns/flac v0.0.0-20171003200620-9a6fb92396d1 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220320163800-277f93cfa958 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
- github.com/hajimehoshi/go-mp3 v0.3.2 // indirect
github.com/jfreymuth/pulse v0.1.0 // indirect
github.com/oakmound/alsa v0.0.2 // indirect
github.com/oakmound/libudev v0.2.1 // indirect
github.com/oakmound/w32 v2.1.0+incompatible // indirect
github.com/oov/directsound-go v0.0.0-20141101201356-e53e59c700bf // indirect
- golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
+ golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd // indirect
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a // indirect
golang.org/x/mobile v0.0.0-20220325161704-447654d348e3 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb // indirect
)
-replace github.com/oakmound/oak/v3 => ../..
+replace github.com/oakmound/oak/v4 => ../..
diff --git a/examples/fallback-font/go.sum b/examples/fallback-font/go.sum
index 5a424fe2..a0a089a6 100644
--- a/examples/fallback-font/go.sum
+++ b/examples/fallback-font/go.sum
@@ -7,19 +7,12 @@ github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 h1:O/r2Sj+8QcMF
github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k=
github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc=
github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
-github.com/eaburns/bit v0.0.0-20131029213740-7bd5cd37375d h1:HB5J9+f1xpkYLgWQ/RqEcbp3SEufyOIMYLoyKNKiG7E=
-github.com/eaburns/bit v0.0.0-20131029213740-7bd5cd37375d/go.mod h1:CHkHWWZ4kbGY6jEy1+qlitDaCtRgNvCOQdakj/1Yl/Q=
-github.com/eaburns/flac v0.0.0-20171003200620-9a6fb92396d1 h1:wl/ggSfTHqAy46hyzw1IlrUYwjqmXYvbJyPdH3rT7YE=
-github.com/eaburns/flac v0.0.0-20171003200620-9a6fb92396d1/go.mod h1:frG94byMNy+1CgGrQ25dZ+17tf98EN+OYBQL4Zh612M=
github.com/flopp/go-findfont v0.0.0-20201114153133-e7393a00c15b h1:/wqXgpZNTP8qV1dPEApjJXlDQd5N/F9U/WEvy5SawUI=
github.com/flopp/go-findfont v0.0.0-20201114153133-e7393a00c15b/go.mod h1:wKKxRDjD024Rh7VMwoU90i6ikQRCr+JTHB5n4Ejkqvw=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220320163800-277f93cfa958 h1:TL70PMkdPCt9cRhKTqsm+giRpgrd0IGEj763nNr2VFY=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220320163800-277f93cfa958/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
-github.com/hajimehoshi/go-mp3 v0.3.2 h1:xSYNE2F3lxtOu9BRjCWHHceg7S91IHfXfXp5+LYQI7s=
-github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
-github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/jfreymuth/pulse v0.1.0 h1:KN38/9hoF9PJvP5DpEVhMRKNuwnJUonc8c9ARorRXUA=
github.com/jfreymuth/pulse v0.1.0/go.mod h1:cpYspI6YljhkUf1WLXLLDmeaaPFc3CnGLjDZf9dZ4no=
github.com/oakmound/alsa v0.0.2 h1:JbOUckkJqVvhABth7qy2JgAjqsWuBPggyoYOk1L6eK0=
@@ -34,15 +27,14 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
+golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd h1:zVFyTKZN/Q7mNRWSs1GOYnHM9NiFSJ54YVRsD0rNWT4=
+golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a h1:LnH9RNcpPv5Kzi15lXg42lYMPUf0x8CuPv1YnvBWZAg=
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20220325161704-447654d348e3 h1:ZDL7hDvJEQEcHVkoZawKmRUgbqn1pOIzb8EinBh5csU=
golang.org/x/mobile v0.0.0-20220325161704-447654d348e3/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
@@ -55,9 +47,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/examples/fallback-font/main.go b/examples/fallback-font/main.go
index c32b8d26..cc8e5e1d 100644
--- a/examples/fallback-font/main.go
+++ b/examples/fallback-font/main.go
@@ -7,9 +7,9 @@ import (
"image"
findfont "github.com/flopp/go-findfont"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
func main() {
diff --git a/examples/flappy-bird/main.go b/examples/flappy-bird/main.go
index 3aa814f3..2cde1e34 100644
--- a/examples/flappy-bird/main.go
+++ b/examples/flappy-bird/main.go
@@ -4,17 +4,17 @@ import (
"image/color"
"time"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/alg/range/floatrange"
- "github.com/oakmound/oak/v3/mouse"
-
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/entities"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/span"
+ "github.com/oakmound/oak/v4/mouse"
+
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/entities"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
var (
@@ -35,7 +35,7 @@ func main() {
// 1. Make Player
newFlappy(ctx, 90, 140)
// 2. Make scrolling repeating pillars
- pillarFreq := floatrange.NewLinear(1, 5)
+ pillarFreq := span.NewLinear(1.0, 5.0)
var pillarLoop func()
pillarLoop = func() {
newPillarPair(ctx)
@@ -92,8 +92,8 @@ func newFlappy(ctx *scene.Context, x, y float64) {
}
var (
- gapPosition = floatrange.NewLinear(10, 370)
- gapSpan = floatrange.NewLinear(100, 250)
+ gapPosition = span.NewLinear(10.0, 370.0)
+ gapSpan = span.NewLinear(100.0, 250.0)
)
func newPillarPair(ctx *scene.Context) {
diff --git a/examples/joystick-viz/main.go b/examples/joystick-viz/main.go
index 6061b7e0..33edc802 100644
--- a/examples/joystick-viz/main.go
+++ b/examples/joystick-viz/main.go
@@ -4,15 +4,15 @@ import (
"fmt"
"time"
- "github.com/oakmound/oak/v3/debugtools/inputviz"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/debugtools/inputviz"
+ "github.com/oakmound/oak/v4/render"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/event"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/joystick"
- "github.com/oakmound/oak/v3/scene"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/joystick"
+ "github.com/oakmound/oak/v4/scene"
)
func main() {
@@ -36,8 +36,7 @@ func main() {
return 0
})
go func() {
- rWidth := float64(ctx.Window.Width()) / 2
- rHeight := float64(ctx.Window.Height()) / 2
+ rBounds := ctx.Window.Bounds().DivConst(2)
jCh, cancel := joystick.WaitForJoysticks(1 * time.Second)
defer cancel()
for joy := range jCh {
@@ -47,15 +46,15 @@ func main() {
case 0:
// 0,0
case 1:
- x = rWidth
+ x = float64(rBounds.X())
case 2:
- y = rHeight
+ y = float64(rBounds.Y())
case 3:
- x = rWidth
- y = rHeight
+ x = float64(rBounds.X())
+ y = float64(rBounds.Y())
}
jrend := inputviz.Joystick{
- Rect: floatgeom.NewRect2WH(x, y, rWidth, rHeight),
+ Rect: floatgeom.NewRect2WH(x, y, float64(rBounds.X()), float64(rBounds.Y())),
StickDeadzone: 4000,
BaseLayer: -1,
}
diff --git a/examples/keyboard-viz/main.go b/examples/keyboard-viz/main.go
index 4ba204f7..332b316b 100644
--- a/examples/keyboard-viz/main.go
+++ b/examples/keyboard-viz/main.go
@@ -5,12 +5,12 @@ import (
"image"
"image/color"
- "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/debugtools/inputviz"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/debugtools/inputviz"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
func main() {
@@ -22,8 +22,9 @@ func main() {
fg.Size = 13
return fg
})
+ bds := ctx.Window.Bounds()
m := inputviz.Keyboard{
- Rect: floatgeom.NewRect2(0, 0, float64(ctx.Window.Width()), float64(ctx.Window.Height())),
+ Rect: floatgeom.NewRect2(0, 0, float64(bds.X()), float64(bds.Y())),
BaseLayer: -1,
RenderCharacters: true,
Font: fnt,
diff --git a/examples/mouse-viz/main.go b/examples/mouse-viz/main.go
index 370f572f..6c0871b5 100644
--- a/examples/mouse-viz/main.go
+++ b/examples/mouse-viz/main.go
@@ -1,17 +1,18 @@
package main
import (
- "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/debugtools/inputviz"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/debugtools/inputviz"
+ "github.com/oakmound/oak/v4/scene"
)
func main() {
oak.AddScene("mouseviz", scene.Scene{
Start: func(ctx *scene.Context) {
+ bds := ctx.Window.Bounds()
m := inputviz.Mouse{
- Rect: floatgeom.NewRect2(0, 0, float64(ctx.Window.Width()), float64(ctx.Window.Height())),
+ Rect: floatgeom.NewRect2(0, 0, float64(bds.X()), float64(bds.Y())),
BaseLayer: -1,
}
m.RenderAndListen(ctx, 0)
diff --git a/examples/multi-window/main.go b/examples/multi-window/main.go
index 120e0840..c263786e 100644
--- a/examples/multi-window/main.go
+++ b/examples/multi-window/main.go
@@ -4,11 +4,11 @@ import (
"fmt"
"image/color"
- "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
func main() {
diff --git a/examples/particle-demo/main.go b/examples/particle-demo/main.go
index 238f9d87..d74096ad 100644
--- a/examples/particle-demo/main.go
+++ b/examples/particle-demo/main.go
@@ -6,19 +6,18 @@ import (
"log"
"strconv"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/alg"
- "github.com/oakmound/oak/v3/alg/range/floatrange"
- "github.com/oakmound/oak/v3/alg/range/intrange"
- "github.com/oakmound/oak/v3/debugstream"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/oakerr"
- "github.com/oakmound/oak/v3/physics"
- "github.com/oakmound/oak/v3/render"
- pt "github.com/oakmound/oak/v3/render/particle"
- "github.com/oakmound/oak/v3/scene"
- "github.com/oakmound/oak/v3/shape"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/alg"
+ "github.com/oakmound/oak/v4/alg/span"
+ "github.com/oakmound/oak/v4/debugstream"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/oakerr"
+ "github.com/oakmound/oak/v4/physics"
+ "github.com/oakmound/oak/v4/render"
+ pt "github.com/oakmound/oak/v4/render/particle"
+ "github.com/oakmound/oak/v4/scene"
+ "github.com/oakmound/oak/v4/shape"
)
var (
@@ -80,9 +79,9 @@ func main() {
return oakerr.UnsupportedFormat{Format: err.Error()}.Error()
}
if !two {
- src.Generator.(pt.Sizeable).SetSize(intrange.NewConstant(f1))
+ src.Generator.(pt.Sizeable).SetSize(span.NewConstant(f1))
} else {
- src.Generator.(pt.Sizeable).SetSize(intrange.NewLinear(f1, f2))
+ src.Generator.(pt.Sizeable).SetSize(span.NewLinear(f1, f2))
}
return ""
@@ -94,9 +93,9 @@ func main() {
return oakerr.UnsupportedFormat{Format: err.Error()}.Error()
}
if !two {
- src.Generator.(pt.Sizeable).SetEndSize(intrange.NewConstant(f1))
+ src.Generator.(pt.Sizeable).SetEndSize(span.NewConstant(f1))
} else {
- src.Generator.(pt.Sizeable).SetEndSize(intrange.NewLinear(f1, f2))
+ src.Generator.(pt.Sizeable).SetEndSize(span.NewLinear(f1, f2))
}
return ""
}})
@@ -107,9 +106,9 @@ func main() {
return oakerr.UnsupportedFormat{Format: err.Error()}.Error()
}
if !two {
- src.Generator.GetBaseGenerator().NewPerFrame = floatrange.NewConstant(npf)
+ src.Generator.GetBaseGenerator().NewPerFrame = span.NewConstant(npf)
} else {
- src.Generator.GetBaseGenerator().NewPerFrame = floatrange.NewLinear(npf, npf2)
+ src.Generator.GetBaseGenerator().NewPerFrame = span.NewLinear(npf, npf2)
}
return ""
}})
@@ -120,9 +119,9 @@ func main() {
return oakerr.UnsupportedFormat{Format: err.Error()}.Error()
}
if !two {
- src.Generator.GetBaseGenerator().LifeSpan = floatrange.NewConstant(npf)
+ src.Generator.GetBaseGenerator().LifeSpan = span.NewConstant(npf)
} else {
- src.Generator.GetBaseGenerator().LifeSpan = floatrange.NewLinear(npf, npf2)
+ src.Generator.GetBaseGenerator().LifeSpan = span.NewLinear(npf, npf2)
}
return ""
}})
@@ -133,9 +132,9 @@ func main() {
return oakerr.UnsupportedFormat{Format: err.Error()}.Error()
}
if !two {
- src.Generator.GetBaseGenerator().Rotation = floatrange.NewConstant(npf)
+ src.Generator.GetBaseGenerator().Rotation = span.NewConstant(npf)
} else {
- src.Generator.GetBaseGenerator().Rotation = floatrange.NewLinear(npf, npf2)
+ src.Generator.GetBaseGenerator().Rotation = span.NewLinear(npf, npf2)
}
return ""
}})
@@ -146,9 +145,9 @@ func main() {
return oakerr.UnsupportedFormat{Format: err.Error()}.Error()
}
if !two {
- src.Generator.GetBaseGenerator().Angle = floatrange.NewConstant(npf * alg.DegToRad)
+ src.Generator.GetBaseGenerator().Angle = span.NewConstant(npf * alg.DegToRad)
} else {
- src.Generator.GetBaseGenerator().Angle = floatrange.NewLinear(npf*alg.DegToRad, npf2*alg.DegToRad)
+ src.Generator.GetBaseGenerator().Angle = span.NewLinear(npf*alg.DegToRad, npf2*alg.DegToRad)
}
return ""
}})
@@ -159,9 +158,9 @@ func main() {
return oakerr.UnsupportedFormat{Format: err.Error()}.Error()
}
if !two {
- src.Generator.GetBaseGenerator().Speed = floatrange.NewConstant(npf)
+ src.Generator.GetBaseGenerator().Speed = span.NewConstant(npf)
} else {
- src.Generator.GetBaseGenerator().Speed = floatrange.NewLinear(npf, npf2)
+ src.Generator.GetBaseGenerator().Speed = span.NewLinear(npf, npf2)
}
return ""
}})
@@ -271,11 +270,11 @@ func main() {
render.Draw(render.NewDrawFPS(0, nil, 10, 10))
x := 320.0
y := 240.0
- newPf := floatrange.NewLinear(1, 2)
- life := floatrange.NewLinear(100, 120)
- angle := floatrange.NewLinear(0, 360)
- speed := floatrange.NewLinear(1, 5)
- size := intrange.NewConstant(1)
+ newPf := span.NewLinear(1.0, 2.0)
+ life := span.NewLinear(100.0, 120.0)
+ angle := span.NewLinear(0.0, 360.0)
+ speed := span.NewLinear(1.0, 5.0)
+ size := span.NewConstant(1)
layerFn := func(v physics.Vector) int {
return 1
}
diff --git a/examples/piano/main.go b/examples/piano/main.go
index f45d0c55..e746151f 100644
--- a/examples/piano/main.go
+++ b/examples/piano/main.go
@@ -11,18 +11,18 @@ import (
"sync"
"time"
- "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/audio"
- "github.com/oakmound/oak/v3/audio/pcm"
- "github.com/oakmound/oak/v3/audio/synth"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/entities"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/audio"
+ "github.com/oakmound/oak/v4/audio/pcm"
+ "github.com/oakmound/oak/v4/audio/synth"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/entities"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
const (
@@ -224,8 +224,8 @@ func main() {
render.Draw(monitor)
pitchDetector := synth.NewPitchDetector(r)
- pt.pitch = &pitchDetector.DetectedPitch
- ft.f64 = &pitchDetector.DetectedRawPitch
+ pt.pitch = &pitchDetector.DetectedPitches[0]
+ ft.f64 = &pitchDetector.DetectedRawPitches[0]
audio.Play(gctx, pitchDetector, func(po *audio.PlayOptions) {
po.Destination = monitor
@@ -246,7 +246,7 @@ func main() {
x := 20.0
y := 200.0
i := 0
- for i < len(keycharOrder) && x+kc.Width() < float64(ctx.Window.Width()-10) {
+ for i < len(keycharOrder) && x+kc.Width() < float64(ctx.Window.Bounds().X()-10) {
ky := newKey(ctx, pitch, kc, keycharOrder[i])
ky.SetPos(floatgeom.Point2{x, y})
layer := 0
diff --git a/examples/platformer/main.go b/examples/platformer/main.go
index 4d88a73d..ce5b056c 100644
--- a/examples/platformer/main.go
+++ b/examples/platformer/main.go
@@ -4,16 +4,16 @@ import (
"image/color"
"math"
- "github.com/oakmound/oak/v3/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
- "github.com/oakmound/oak/v3/collision"
+ "github.com/oakmound/oak/v4/collision"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/entities"
- "github.com/oakmound/oak/v3/scene"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/entities"
+ "github.com/oakmound/oak/v4/scene"
)
const (
diff --git a/examples/pong/main.go b/examples/pong/main.go
index c84af099..b67aa0fb 100644
--- a/examples/pong/main.go
+++ b/examples/pong/main.go
@@ -5,14 +5,14 @@ import (
"math"
"math/rand"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/entities"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/entities"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
var (
diff --git a/examples/radar-demo/main.go b/examples/radar-demo/main.go
index afec01d9..7d157255 100644
--- a/examples/radar-demo/main.go
+++ b/examples/radar-demo/main.go
@@ -5,15 +5,15 @@ import (
"math"
"math/rand"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/alg/intgeom"
-
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/entities"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/examples/radar-demo/radar"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/entities"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/examples/radar-demo/radar"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
const (
@@ -55,7 +55,7 @@ func main() {
w := 100
h := 100
r := radar.NewRadar(w, h, points, center, 10)
- r.SetPos(float64(ctx.Window.Width()-w), 0)
+ r.SetPos(float64(ctx.Window.Bounds().X()-w), 0)
for i := 0; i < 5; i++ {
x, y := rand.Float64()*400, rand.Float64()*400
diff --git a/examples/radar-demo/radar/radar.go b/examples/radar-demo/radar/radar.go
index f755369c..58ce0b44 100644
--- a/examples/radar-demo/radar/radar.go
+++ b/examples/radar-demo/radar/radar.go
@@ -5,7 +5,7 @@ import (
"image/color"
"image/draw"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/render"
)
// Point is a utility function for location
diff --git a/examples/rooms/main.go b/examples/rooms/main.go
index 7f489a7d..9fea3120 100644
--- a/examples/rooms/main.go
+++ b/examples/rooms/main.go
@@ -4,13 +4,13 @@ import (
"image/color"
"math/rand"
- "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/entities"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/entities"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
// Rooms exercises shifting the camera in a zelda-esque fashion,
@@ -20,10 +20,10 @@ import (
func isOffScreen(ctx *scene.Context, char *entities.Entity) (intgeom.Dir2, bool) {
x := int(char.X())
y := int(char.Y())
- if x > ctx.Window.Viewport().X()+ctx.Window.Width() {
+ if x > ctx.Window.Viewport().X()+ctx.Window.Bounds().X() {
return intgeom.Right, true
}
- if y > ctx.Window.Viewport().Y()+ctx.Window.Height() {
+ if y > ctx.Window.Viewport().Y()+ctx.Window.Bounds().Y() {
return intgeom.Down, true
}
if int(char.Right()) < ctx.Window.Viewport().X() {
@@ -55,14 +55,14 @@ func main() {
dir, ok := isOffScreen(ctx, char)
if !transitioning && ok {
transitioning = true
- totalTransitionDelta = intgeom.Point2{ctx.Window.Width(), ctx.Window.Height()}.Mul(intgeom.Point2{dir.X(), dir.Y()})
+ totalTransitionDelta = ctx.Window.Bounds().Mul(intgeom.Point2{dir.X(), dir.Y()})
transitionDelta = totalTransitionDelta.DivConst(transitionFrameCount)
}
if transitioning {
// disable movement
// move camera one size towards the player
if totalTransitionDelta.X() != 0 || totalTransitionDelta.Y() != 0 {
- oak.ShiftScreen(transitionDelta.X(), transitionDelta.Y())
+ oak.ShiftViewport(transitionDelta)
totalTransitionDelta = totalTransitionDelta.Sub(transitionDelta)
} else {
transitioning = false
diff --git a/examples/screenopts/main.go b/examples/screenopts/main.go
index 5ce3e691..5418277a 100644
--- a/examples/screenopts/main.go
+++ b/examples/screenopts/main.go
@@ -7,11 +7,11 @@ import (
"math/rand"
"strconv"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
func main() {
diff --git a/examples/slide/main.go b/examples/slide/main.go
index a41ce638..c273bef5 100644
--- a/examples/slide/main.go
+++ b/examples/slide/main.go
@@ -6,16 +6,15 @@ import (
"image/color"
"log"
- "github.com/oakmound/oak/v3/alg/range/floatrange"
- "github.com/oakmound/oak/v3/alg/range/intrange"
- "github.com/oakmound/oak/v3/render/mod"
- "github.com/oakmound/oak/v3/render/particle"
-
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/examples/slide/show"
- "github.com/oakmound/oak/v3/examples/slide/show/static"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/shape"
+ "github.com/oakmound/oak/v4/alg/span"
+ "github.com/oakmound/oak/v4/render/mod"
+ "github.com/oakmound/oak/v4/render/particle"
+
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/examples/slide/show"
+ "github.com/oakmound/oak/v4/examples/slide/show/static"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/shape"
"golang.org/x/image/colornames"
)
@@ -458,12 +457,12 @@ func addParticles(i int, sslides []*static.Slide) {
sslides[i].Append(show.Title("Particles"))
sslides[i].OnClick = func() {
go particle.NewColorGenerator(
- particle.Size(intrange.NewConstant(4)),
- particle.EndSize(intrange.NewConstant(7)),
- particle.Angle(floatrange.NewLinear(0, 359)),
+ particle.Size(span.NewConstant(4)),
+ particle.EndSize(span.NewConstant(7)),
+ particle.Angle(span.NewLinear(0.0, 359.0)),
particle.Pos(width/2, height/2),
- particle.Speed(floatrange.NewSpread(5, 2)),
- particle.NewPerFrame(floatrange.NewSpread(5, 5)),
+ particle.Speed(span.NewSpread(5.0, 2.0)),
+ particle.NewPerFrame(span.NewSpread(5.0, 5.0)),
particle.Color(
color.RGBA{0, 0, 0, 255}, color.RGBA{0, 0, 0, 0},
color.RGBA{255, 255, 255, 255}, color.RGBA{0, 0, 0, 0},
diff --git a/examples/slide/show/fonts.go b/examples/slide/show/fonts.go
index 53e95c5a..67d10308 100644
--- a/examples/slide/show/fonts.go
+++ b/examples/slide/show/fonts.go
@@ -5,7 +5,7 @@ import (
"image/color"
"path"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/render"
)
func InitFonts() (err error) {
diff --git a/examples/slide/show/helpers.go b/examples/slide/show/helpers.go
index e15c6201..33ebedb1 100644
--- a/examples/slide/show/helpers.go
+++ b/examples/slide/show/helpers.go
@@ -5,9 +5,9 @@ import (
"path/filepath"
"strings"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/render/mod"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/render"
)
var (
diff --git a/examples/slide/show/slide.go b/examples/slide/show/slide.go
index 2fdade72..e0258bf5 100644
--- a/examples/slide/show/slide.go
+++ b/examples/slide/show/slide.go
@@ -6,12 +6,12 @@ import (
"image/color"
"strconv"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/debugstream"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/debugstream"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
type Slide interface {
@@ -100,13 +100,14 @@ func Start(width, height int, slides ...Slide) {
oak.AddScene("slide"+strconv.Itoa(len(slides)),
scene.Scene{
Start: func(ctx *scene.Context) {
- oldBackground = oak.GetBackgroundImage()
+ oldBackground = ctx.Window.(*oak.Window).GetBackgroundImage()
oak.SetColorBackground(image.NewUniform(color.RGBA{0, 0, 0, 255}))
+ wbds := ctx.Window.Bounds()
render.Draw(
Express.NewText(
"Spacebar to restart show ...",
- float64(ctx.Window.Width()/2),
- float64(ctx.Window.Height()-50),
+ float64(wbds.X()/2),
+ float64(wbds.Y()-50),
),
)
event.GlobalBind(ctx, key.Down(key.Spacebar), func(key.Event) event.Response {
diff --git a/examples/slide/show/static/basicSlide.go b/examples/slide/show/static/basicSlide.go
index 3b2d01ce..81728f45 100644
--- a/examples/slide/show/static/basicSlide.go
+++ b/examples/slide/show/static/basicSlide.go
@@ -4,12 +4,12 @@ import (
"fmt"
"os"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
type Slide struct {
diff --git a/examples/sprite-demo/main.go b/examples/sprite-demo/main.go
index b607a412..cbd7bff2 100644
--- a/examples/sprite-demo/main.go
+++ b/examples/sprite-demo/main.go
@@ -7,12 +7,12 @@ import (
"math/rand"
"path/filepath"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/render/mod"
- "github.com/oakmound/oak/v3/scene"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/render/mod"
+ "github.com/oakmound/oak/v4/scene"
)
const (
diff --git a/examples/svg/go.mod b/examples/svg/go.mod
index 366c5323..530c3c91 100644
--- a/examples/svg/go.mod
+++ b/examples/svg/go.mod
@@ -3,7 +3,7 @@ module github.com/oakmound/oak/examples/svg
go 1.18
require (
- github.com/oakmound/oak/v3 v3.0.0-alpha.1
+ github.com/oakmound/oak/v4 v4.0.0-alpha.1
github.com/srwiley/oksvg v0.0.0-20210320200257-875f767ac39a
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9
)
@@ -13,17 +13,14 @@ require (
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 // indirect
github.com/disintegration/gift v1.2.1 // indirect
- github.com/eaburns/bit v0.0.0-20131029213740-7bd5cd37375d // indirect
- github.com/eaburns/flac v0.0.0-20171003200620-9a6fb92396d1 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220320163800-277f93cfa958 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
- github.com/hajimehoshi/go-mp3 v0.3.2 // indirect
github.com/jfreymuth/pulse v0.1.0 // indirect
github.com/oakmound/alsa v0.0.2 // indirect
github.com/oakmound/libudev v0.2.1 // indirect
github.com/oakmound/w32 v2.1.0+incompatible // indirect
github.com/oov/directsound-go v0.0.0-20141101201356-e53e59c700bf // indirect
- golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
+ golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd // indirect
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a // indirect
golang.org/x/mobile v0.0.0-20220325161704-447654d348e3 // indirect
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect
@@ -32,4 +29,4 @@ require (
golang.org/x/text v0.3.6 // indirect
)
-replace github.com/oakmound/oak/v3 => ../..
+replace github.com/oakmound/oak/v4 => ../..
diff --git a/examples/svg/go.sum b/examples/svg/go.sum
index 84a67c7d..5f8cb361 100644
--- a/examples/svg/go.sum
+++ b/examples/svg/go.sum
@@ -7,17 +7,10 @@ github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 h1:O/r2Sj+8QcMF
github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k=
github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc=
github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
-github.com/eaburns/bit v0.0.0-20131029213740-7bd5cd37375d h1:HB5J9+f1xpkYLgWQ/RqEcbp3SEufyOIMYLoyKNKiG7E=
-github.com/eaburns/bit v0.0.0-20131029213740-7bd5cd37375d/go.mod h1:CHkHWWZ4kbGY6jEy1+qlitDaCtRgNvCOQdakj/1Yl/Q=
-github.com/eaburns/flac v0.0.0-20171003200620-9a6fb92396d1 h1:wl/ggSfTHqAy46hyzw1IlrUYwjqmXYvbJyPdH3rT7YE=
-github.com/eaburns/flac v0.0.0-20171003200620-9a6fb92396d1/go.mod h1:frG94byMNy+1CgGrQ25dZ+17tf98EN+OYBQL4Zh612M=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220320163800-277f93cfa958 h1:TL70PMkdPCt9cRhKTqsm+giRpgrd0IGEj763nNr2VFY=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220320163800-277f93cfa958/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
-github.com/hajimehoshi/go-mp3 v0.3.2 h1:xSYNE2F3lxtOu9BRjCWHHceg7S91IHfXfXp5+LYQI7s=
-github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
-github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/jfreymuth/pulse v0.1.0 h1:KN38/9hoF9PJvP5DpEVhMRKNuwnJUonc8c9ARorRXUA=
github.com/jfreymuth/pulse v0.1.0/go.mod h1:cpYspI6YljhkUf1WLXLLDmeaaPFc3CnGLjDZf9dZ4no=
github.com/oakmound/alsa v0.0.2 h1:JbOUckkJqVvhABth7qy2JgAjqsWuBPggyoYOk1L6eK0=
@@ -36,15 +29,14 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
+golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd h1:zVFyTKZN/Q7mNRWSs1GOYnHM9NiFSJ54YVRsD0rNWT4=
+golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a h1:LnH9RNcpPv5Kzi15lXg42lYMPUf0x8CuPv1YnvBWZAg=
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20220325161704-447654d348e3 h1:ZDL7hDvJEQEcHVkoZawKmRUgbqn1pOIzb8EinBh5csU=
golang.org/x/mobile v0.0.0-20220325161704-447654d348e3/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
@@ -58,9 +50,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/examples/svg/main.go b/examples/svg/main.go
index ceae36bd..b55a284d 100644
--- a/examples/svg/main.go
+++ b/examples/svg/main.go
@@ -6,9 +6,9 @@ import (
"fmt"
"image"
- "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
"github.com/srwiley/oksvg"
"github.com/srwiley/rasterx"
)
diff --git a/examples/text-demos/color-changing-text-demo/main.go b/examples/text-demos/color-changing-text-demo/main.go
index 5f425632..84121b4e 100644
--- a/examples/text-demos/color-changing-text-demo/main.go
+++ b/examples/text-demos/color-changing-text-demo/main.go
@@ -6,20 +6,19 @@ import (
"path"
"strconv"
- "github.com/oakmound/oak/v3/alg/range/floatrange"
-
"image"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/alg/span"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
var (
font *render.Font
r, g, b float64
- diff = floatrange.NewSpread(0, 10)
- limit = floatrange.NewLinear(0, 255)
+ diff = span.NewSpread(0.0, 10.0)
+ limit = span.NewLinear(0.0, 255.0)
)
type floatStringer struct {
@@ -32,7 +31,7 @@ func (fs floatStringer) String() string {
func main() {
oak.AddScene("demo",
- scene.Scene{Start: func(*scene.Context) {
+ scene.Scene{Start: func(ctx *scene.Context) {
render.Draw(render.NewDrawFPS(0.25, nil, 10, 10))
fg := render.FontGenerator{
File: path.Join("assets", "font", "luxisbi.ttf"),
@@ -62,21 +61,19 @@ func main() {
render.Draw(font2.NewText("g", 280, 260), 0)
render.Draw(font2.NewText("b", 400, 260), 0)
- go func() {
- for {
- r = limit.EnforceRange(r + diff.Poll())
- g = limit.EnforceRange(g + diff.Poll())
- b = limit.EnforceRange(b + diff.Poll())
- font.Drawer.Src = image.NewUniform(
- color.RGBA{
- uint8(r),
- uint8(g),
- uint8(b),
- 255,
- },
- )
- }
- }()
+ ctx.DoEachFrame(func() {
+ r = limit.Clamp(r + diff.Poll())
+ g = limit.Clamp(g + diff.Poll())
+ b = limit.Clamp(b + diff.Poll())
+ font.Drawer.Src = image.NewUniform(
+ color.RGBA{
+ uint8(r),
+ uint8(g),
+ uint8(b),
+ 255,
+ },
+ )
+ })
},
})
oak.SetFS(assets)
diff --git a/examples/text-demos/continual-text-demo/main.go b/examples/text-demos/continual-text-demo/main.go
index afe8cf38..927f1bbf 100644
--- a/examples/text-demos/continual-text-demo/main.go
+++ b/examples/text-demos/continual-text-demo/main.go
@@ -4,14 +4,15 @@ import (
"image/color"
"math/rand"
- "github.com/oakmound/oak/v3/alg/range/floatrange"
- "github.com/oakmound/oak/v3/dlog"
+ "github.com/oakmound/oak/v4/alg/span"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/event"
"image"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
// ~60 fps draw rate with these examples in testing
@@ -24,8 +25,8 @@ const (
var (
font *render.Font
r, g, b float64
- diff = floatrange.NewSpread(0, 10)
- limit = floatrange.NewLinear(0, 255)
+ diff = span.NewSpread(0.0, 10.0)
+ limit = span.NewLinear(0.0, 255.0)
strs []*render.Text
)
@@ -40,7 +41,7 @@ func randomStr(chars int) string {
func main() {
oak.AddScene("demo",
- scene.Scene{Start: func(*scene.Context) {
+ scene.Scene{Start: func(ctx *scene.Context) {
render.Draw(render.NewDrawFPS(.25, nil, 10, 10))
r = 255
@@ -61,26 +62,23 @@ func main() {
render.Draw(strs[len(strs)-1], 0)
}
- go func() {
- for {
- r = limit.EnforceRange(r + diff.Poll())
- g = limit.EnforceRange(g + diff.Poll())
- b = limit.EnforceRange(b + diff.Poll())
- // This should be a function in oak to just set color source
- // (or texture source)
- font.Drawer.Src = image.NewUniform(
- color.RGBA{
- uint8(r),
- uint8(g),
- uint8(b),
- 255,
- },
- )
- for _, st := range strs {
- st.SetString(randomStr(strlen))
- }
+ event.GlobalBind(ctx, event.Enter, func(_ event.EnterPayload) event.Response {
+ r = limit.Clamp(r + diff.Poll())
+ g = limit.Clamp(g + diff.Poll())
+ b = limit.Clamp(b + diff.Poll())
+ font.Drawer.Src = image.NewUniform(
+ color.RGBA{
+ uint8(r),
+ uint8(g),
+ uint8(b),
+ 255,
+ },
+ )
+ for _, st := range strs {
+ st.SetString(randomStr(strlen))
}
- }()
+ return 0
+ })
},
})
render.SetDrawStack(
diff --git a/examples/titlescreen-demo/main.go b/examples/titlescreen-demo/main.go
index 3cec5149..f3d45228 100644
--- a/examples/titlescreen-demo/main.go
+++ b/examples/titlescreen-demo/main.go
@@ -3,13 +3,13 @@ package main
import (
"image/color"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/entities"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/entities"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
// Axes are the plural of axis
@@ -24,15 +24,15 @@ const (
func center(ctx *scene.Context, obj render.Renderable, ax Axes) {
objWidth, objHeight := obj.GetDims()
-
+ wbds := ctx.Window.Bounds()
switch ax {
case Both:
- obj.SetPos(float64(ctx.Window.Width()/2-objWidth/2),
- float64(ctx.Window.Height()-objHeight)/2) //distributive property
+ obj.SetPos(float64(wbds.X()/2-objWidth/2),
+ float64(wbds.Y()-objHeight)/2) //distributive property
case X:
- obj.SetPos(float64(ctx.Window.Width()-objWidth)/2, obj.Y())
+ obj.SetPos(float64(wbds.X()-objWidth)/2, obj.Y())
case Y:
- obj.SetPos(obj.X(), float64(ctx.Window.Height()-objHeight)/2)
+ obj.SetPos(obj.X(), float64(wbds.Y()-objHeight)/2)
}
}
@@ -49,8 +49,10 @@ func main() {
//tell the draw loop to draw titleText
render.Draw(titleText)
+ wbds := ctx.Window.Bounds()
+
//do the same for the text with button instructions, but this time Y position is not a placeholder (X still is)
- instructionText := render.NewText("press Enter to start, or press Q to quit", 0, float64(ctx.Window.Height()*3/4))
+ instructionText := render.NewText("press Enter to start, or press Q to quit", 0, float64(wbds.Y()*3/4))
//this time we only center the X axis, otherwise it would overlap titleText
center(ctx, instructionText, X)
render.Draw(instructionText)
diff --git a/examples/top-down-shooter/main.go b/examples/top-down-shooter/main.go
index eb4fe402..2f021cc2 100644
--- a/examples/top-down-shooter/main.go
+++ b/examples/top-down-shooter/main.go
@@ -6,20 +6,20 @@ import (
"math/rand"
"time"
- "github.com/oakmound/oak/v3/render/mod"
-
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/collision/ray"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/entities"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/render/mod"
+
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/collision/ray"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/entities"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
const (
@@ -73,10 +73,7 @@ func main() {
playerX = &char.Rect.Min[0]
playerY = &char.Rect.Min[1]
- screenCenter := floatgeom.Point2{
- float64(ctx.Window.Width()) / 2,
- float64(ctx.Window.Height()) / 2,
- }
+ screenCenter := ctx.Window.Bounds().DivConst(2)
event.Bind(ctx, event.Enter, char, func(char *entities.Entity, ev event.EnterPayload) event.Response {
if oak.IsDown(key.W) {
@@ -93,9 +90,10 @@ func main() {
}
ctx.Window.(*oak.Window).DoBetweenDraws(func() {
char.ShiftDelta()
- oak.SetScreen(
- int(char.X()-screenCenter.X()),
- int(char.Y()-screenCenter.Y()),
+ oak.SetViewport(
+ screenCenter.Sub(intgeom.Point2{
+ int(char.X()), int(char.Y()),
+ }),
)
char.Delta = floatgeom.Point2{}
})
diff --git a/examples/zooming/main.go b/examples/zooming/main.go
index b28f831d..19bee5d0 100644
--- a/examples/zooming/main.go
+++ b/examples/zooming/main.go
@@ -6,12 +6,12 @@ import (
"image/draw"
"path/filepath"
- oak "github.com/oakmound/oak/v3"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
+ oak "github.com/oakmound/oak/v4"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
)
var (
diff --git a/fileutil/open.go b/fileutil/open.go
index 02140f32..7a155271 100644
--- a/fileutil/open.go
+++ b/fileutil/open.go
@@ -1,7 +1,6 @@
package fileutil
import (
- "bytes"
"io"
"io/fs"
"os"
@@ -29,11 +28,11 @@ func Open(file string) (io.ReadCloser, error) {
fixedPath := fixWindowsPath(file)
f, readErr := FS.Open(fixedPath)
if readErr != nil && OSFallback {
- byt, err := os.ReadFile(file)
+ osFile, err := os.Open(file)
if err != nil {
return nil, err
}
- return io.NopCloser(bytes.NewReader(byt)), nil
+ return osFile, nil
}
return f, readErr
}
diff --git a/fileutil/open_test.go b/fileutil/open_test.go
index 5472a371..c1e48974 100644
--- a/fileutil/open_test.go
+++ b/fileutil/open_test.go
@@ -2,7 +2,9 @@ package fileutil
import (
"embed"
+ "errors"
"io"
+ "os"
"testing"
)
@@ -10,36 +12,86 @@ import (
var testfs embed.FS
func TestOpen(t *testing.T) {
- FS = testfs
- f, err := Open("testdata/test.txt")
- if err != nil {
- t.Fatalf("open failed: %v", err)
- }
- _, err = io.ReadAll(f)
- if err != nil {
- t.Fatalf("read all failed: %v", err)
- }
- err = f.Close()
- if err != nil {
- t.Fatalf("close failed: %v", err)
- }
+ t.Run("Basic", func(t *testing.T) {
+ FS = testfs
+ f, err := Open("testdata/test.txt")
+ if err != nil {
+ t.Fatalf("open failed: %v", err)
+ }
+ _, err = io.ReadAll(f)
+ if err != nil {
+ t.Fatalf("read all failed: %v", err)
+ }
+ err = f.Close()
+ if err != nil {
+ t.Fatalf("close failed: %v", err)
+ }
+ })
+ t.Run("NotFound", func(t *testing.T) {
+ FS = testfs
+ _, err := Open("testdata/notfound.txt")
+ perr := &os.PathError{}
+ if !errors.As(err, &perr) {
+ t.Fatalf("expected path error: %v", err)
+ }
+ })
+ t.Run("OSFallback", func(t *testing.T) {
+ FS = testfs
+ f, err := os.CreateTemp(".", "test")
+ if err != nil {
+ t.Fatalf("failed to create temp file: %v", err)
+ }
+ defer os.Remove(f.Name())
+ f.Close()
+ f2, err := Open(f.Name())
+ if err != nil {
+ t.Fatalf("open failed: %v", err)
+ }
+ f2.Close()
+ })
}
func TestReadFile(t *testing.T) {
- FS = testfs
- _, err := ReadFile("testdata/test.txt")
- if err != nil {
- t.Fatalf("read all failed: %v", err)
- }
+ t.Run("Basic", func(t *testing.T) {
+ FS = testfs
+ _, err := ReadFile("testdata/test.txt")
+ if err != nil {
+ t.Fatalf("read all failed: %v", err)
+ }
+ })
+ t.Run("NotFound", func(t *testing.T) {
+ FS = testfs
+ _, err := ReadFile("testdata/notfound.txt")
+ perr := &os.PathError{}
+ if !errors.As(err, &perr) {
+ t.Fatalf("expected path error: %v", err)
+ }
+ })
}
func TestReadDir(t *testing.T) {
- FS = testfs
- ds, err := ReadDir("testdata")
- if err != nil {
- t.Fatalf("read dir failed: %v", err)
- }
- if len(ds) != 1 {
- t.Fatalf("read dir had %v elements, expected 1", len(ds))
- }
+ t.Run("Basic", func(t *testing.T) {
+ FS = testfs
+ ds, err := ReadDir("testdata")
+ if err != nil {
+ t.Fatalf("read dir failed: %v", err)
+ }
+ if len(ds) != 1 {
+ t.Fatalf("read dir had %v elements, expected 1", len(ds))
+ }
+ })
+ t.Run("NoWindowsPaths", func(t *testing.T) {
+ FixWindowsPaths = false
+ defer func() {
+ FixWindowsPaths = true
+ }()
+ FS = testfs
+ ds, err := ReadDir("testdata")
+ if err != nil {
+ t.Fatalf("read dir failed: %v", err)
+ }
+ if len(ds) != 1 {
+ t.Fatalf("read dir had %v elements, expected 1", len(ds))
+ }
+ })
}
diff --git a/go.mod b/go.mod
index 2b6b03bd..4d60493d 100644
--- a/go.mod
+++ b/go.mod
@@ -1,4 +1,4 @@
-module github.com/oakmound/oak/v3
+module github.com/oakmound/oak/v4
go 1.18
@@ -16,6 +16,7 @@ require (
github.com/oakmound/libudev v0.2.1 // linux, joystick
github.com/oakmound/w32 v2.1.0+incompatible // windows, shiny
github.com/oov/directsound-go v0.0.0-20141101201356-e53e59c700bf // windows, audio
+ golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a
golang.org/x/mobile v0.0.0-20220325161704-447654d348e3
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
@@ -24,5 +25,4 @@ require (
require (
github.com/eaburns/bit v0.0.0-20131029213740-7bd5cd37375d // indirect
- golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
)
diff --git a/go.sum b/go.sum
index addc030d..4e184703 100644
--- a/go.sum
+++ b/go.sum
@@ -35,8 +35,9 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
+golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd h1:zVFyTKZN/Q7mNRWSs1GOYnHM9NiFSJ54YVRsD0rNWT4=
+golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a h1:LnH9RNcpPv5Kzi15lXg42lYMPUf0x8CuPv1YnvBWZAg=
diff --git a/init.go b/init.go
index d7aa2291..f9a09dd1 100644
--- a/init.go
+++ b/init.go
@@ -8,9 +8,10 @@ import (
"strings"
"time"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/oakerr"
- "github.com/oakmound/oak/v3/timing"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/oakerr"
+ "github.com/oakmound/oak/v4/scene"
+ "github.com/oakmound/oak/v4/timing"
)
var (
@@ -18,8 +19,11 @@ var (
)
// Init initializes the oak engine.
-// It spawns off an event loop of several goroutines
-// and loops through scenes after initialization.
+// After the configuration options have been parsed and validated, this will run concurrent
+// routines drawing to an OS window or app, forwarding OS inputs to this window's configured
+// event handler, and running scenes: first the predefined 'loading' scene, then firstScene
+// as provided here, then scenes following commands sent to the window or returned by ending
+// scenes.
func (w *Window) Init(firstScene string, configOptions ...ConfigOption) error {
var err error
@@ -28,13 +32,6 @@ func (w *Window) Init(firstScene string, configOptions ...ConfigOption) error {
return fmt.Errorf("failed to create config: %w", err)
}
- // if c.config.Screen.TargetWidth != 0 && c.config.Screen.TargetHeight != 0 {
- // w, h := driver.MonitorSize()
- // if w != 0 || h != 0 {
- // // Todo: Modify conf.Screen.Scale
- // }
- // }
-
lvl, err := dlog.ParseDebugLevel(w.config.Debug.Level)
if err != nil {
return fmt.Errorf("failed to parse debug config: %w", err)
@@ -42,10 +39,8 @@ func (w *Window) Init(firstScene string, configOptions ...ConfigOption) error {
dlog.SetFilter(func(msg string) bool {
return strings.Contains(msg, w.config.Debug.Filter)
})
- err = dlog.SetLogLevel(lvl)
- if err != nil {
- return err
- }
+ // This error cannot happen as it would surface in Parse above
+ _ = dlog.SetLogLevel(lvl)
err = oakerr.SetLanguageString(w.config.Language)
if err != nil {
return err
@@ -74,7 +69,27 @@ func (w *Window) Init(firstScene string, configOptions ...ConfigOption) error {
overrideInit(w)
- go w.sceneLoop(firstScene, w.config.TrackInputChanges, w.config.BatchLoad)
+ err = w.SceneMap.AddScene(oakLoadingScene, scene.Scene{
+ Start: func(ctx *scene.Context) {
+ if w.config.BatchLoad {
+ go func() {
+ w.loadAssets(w.config.Assets.ImagePath, w.config.Assets.AudioPath)
+ w.endLoad()
+ }()
+ } else {
+ go w.endLoad()
+ }
+ },
+ End: func() (string, *scene.Result) {
+ return w.firstScene, &scene.Result{
+ NextSceneInput: w.FirstSceneInput,
+ }
+ },
+ })
+ if err != nil {
+ return err
+ }
+ go w.sceneLoop(firstScene, w.config.TrackInputChanges)
if w.config.EnableDebugConsole {
go w.debugConsole(os.Stdin, os.Stdout)
}
diff --git a/init_override_js.go b/init_override_js.go
index 1df10f17..3bfae168 100644
--- a/init_override_js.go
+++ b/init_override_js.go
@@ -4,7 +4,7 @@
package oak
import (
- "github.com/oakmound/oak/v3/dlog"
+ "github.com/oakmound/oak/v4/dlog"
"syscall/js"
)
@@ -15,11 +15,11 @@ func overrideInit(w *Window) {
}
if w.config.EnableDebugConsole {
dlog.Info("Debug console is not supported in JS")
- w.config.EnableDebugConsole = false
+ w.config.EnableDebugConsole = false
}
if w.config.UnlimitedDrawFrameRate {
dlog.Info("Unlimited draw frame rate is not supported in JS")
- w.config.UnlimitedDrawFrameRate = false
+ w.config.UnlimitedDrawFrameRate = false
}
w.animationFrame = make(chan struct{})
js.Global().Call("requestAnimationFrame", js.FuncOf(w.requestFrame))
diff --git a/init_test.go b/init_test.go
new file mode 100644
index 00000000..48135ded
--- /dev/null
+++ b/init_test.go
@@ -0,0 +1,46 @@
+package oak
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestInitFailures(t *testing.T) {
+ t.Run("BadConfig", func(t *testing.T) {
+ c1 := NewWindow()
+ err := c1.Init("", func(c Config) (Config, error) {
+ return c, fmt.Errorf("whoops")
+ })
+ if err == nil {
+ t.Fatal("expected error to cascade down from init")
+ }
+ })
+ t.Run("ParseDebugLevel", func(t *testing.T) {
+ c1 := NewWindow()
+ err := c1.Init("", func(c Config) (Config, error) {
+ c.Debug.Level = "bogus"
+ return c, nil
+ })
+ if err == nil {
+ t.Fatal("expected error parsing debug level")
+ }
+ })
+ t.Run("SetLanguageString", func(t *testing.T) {
+ c1 := NewWindow()
+ err := c1.Init("", func(c Config) (Config, error) {
+ c.Language = "bogus"
+ return c, nil
+ })
+ if err == nil {
+ t.Fatal("expected error parsing language string")
+ }
+ })
+}
+
+func TestInitDebugConsole(t *testing.T) {
+ c1 := NewWindow()
+ c1.Init("bad", func(c Config) (Config, error) {
+ c.EnableDebugConsole = true
+ return c, nil
+ })
+}
diff --git a/inputLoop.go b/inputLoop.go
index d3654b96..6137c188 100644
--- a/inputLoop.go
+++ b/inputLoop.go
@@ -1,28 +1,33 @@
package oak
import (
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/timing"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/timing"
- "github.com/oakmound/oak/v3/dlog"
- okey "github.com/oakmound/oak/v3/key"
- omouse "github.com/oakmound/oak/v3/mouse"
+ "github.com/oakmound/oak/v4/dlog"
+ okey "github.com/oakmound/oak/v4/key"
+ omouse "github.com/oakmound/oak/v4/mouse"
"golang.org/x/mobile/event/key"
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/mouse"
"golang.org/x/mobile/event/size"
)
+// The following block defines events generated by oak during scene execution
var (
- // ViewportUpdate: Triggered when the position of of the viewport changes
+ // ViewportUpdate is triggered when the position of of the viewport changes
ViewportUpdate = event.RegisterEvent[intgeom.Point2]()
- // OnStop: Triggered when the engine is stopped.
+ // OnStop is triggered when the engine is stopped, e.g. when a window's close
+ // button is clicked.
OnStop = event.RegisterEvent[struct{}]()
- // FocusGain: Triggered when the window gains focus
+ // FocusGain is triggered when a window gains focus
FocusGain = event.RegisterEvent[struct{}]()
- // FocusLoss: Triggered when the window loses focus
+ // FocusLoss is triggered when a window loses focus
FocusLoss = event.RegisterEvent[struct{}]()
+ // InputChange is triggered when the most recent input device changes (e.g. keyboard to joystick or vice versa). It
+ // is only sent if Config.TrackInputChanges is true when Init is called.
+ InputChange = event.RegisterEvent[InputType]()
)
func (w *Window) inputLoop() {
diff --git a/inputLoop_test.go b/inputLoop_test.go
index 8ef6703b..2ac6d92b 100644
--- a/inputLoop_test.go
+++ b/inputLoop_test.go
@@ -4,25 +4,26 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/event"
- okey "github.com/oakmound/oak/v3/key"
+ "github.com/oakmound/oak/v4/event"
"golang.org/x/mobile/event/key"
+ "golang.org/x/mobile/event/mouse"
)
func TestInputLoop(t *testing.T) {
c1 := blankScene(t)
c1.SetLogicHandler(event.NewBus(nil))
- c1.Window.Send(okey.Event{
+ c1.Window.Send(key.Event{
Direction: key.DirPress,
Code: key.Code0,
})
- c1.Window.Send(okey.Event{
+ c1.Window.Send(key.Event{
Direction: key.DirNone,
Code: key.Code0,
})
- c1.Window.Send(okey.Event{
+ c1.Window.Send(key.Event{
Direction: key.DirRelease,
Code: key.Code0,
})
+ c1.Window.Send(mouse.Event{})
time.Sleep(2 * time.Second)
}
diff --git a/inputTracker.go b/inputTracker.go
index d5080ded..5ee79c1c 100644
--- a/inputTracker.go
+++ b/inputTracker.go
@@ -4,24 +4,22 @@ import (
"sync/atomic"
"time"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/joystick"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/mouse"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/joystick"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/mouse"
)
// InputType expresses some form of input to the engine to represent a player
type InputType int32
-// InputChange is triggered when the most recent input device changes (e.g. keyboard to joystick or vice versa)
-var InputChange = event.RegisterEvent[InputType]()
-
var trackingJoystickChange = event.RegisterEvent[struct{}]()
-// Supported Input Types
+// The following constants define valid types of input sent via the InputChange event.
const (
- InputKeyboard InputType = iota
+ InputNone InputType = iota
+ InputKeyboard
InputMouse
InputJoystick
)
diff --git a/inputTracker_test.go b/inputTracker_test.go
index 322374e9..a3fd31b7 100644
--- a/inputTracker_test.go
+++ b/inputTracker_test.go
@@ -4,13 +4,15 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/scene"
)
func TestTrackInputChanges(t *testing.T) {
+ inputChangeFailed := make(chan bool)
+
c1 := NewWindow()
c1.SetLogicHandler(event.NewBus(event.NewCallerMap()))
c1.AddScene("1", scene.Scene{})
@@ -21,35 +23,32 @@ func TestTrackInputChanges(t *testing.T) {
time.Sleep(2 * time.Second)
expectedType := new(InputType)
*expectedType = InputKeyboard
- failed := false
event.GlobalBind(c1.eventHandler, InputChange, func(mode InputType) event.Response {
- if mode != *expectedType {
- failed = true
- }
+ inputChangeFailed <- mode != *expectedType
return 0
})
c1.TriggerKeyDown(key.Event{})
- time.Sleep(2 * time.Second)
- if failed {
+ if <-inputChangeFailed {
t.Fatalf("keyboard change failed")
}
*expectedType = InputJoystick
event.TriggerOn(c1.eventHandler, trackingJoystickChange, struct{}{})
- time.Sleep(2 * time.Second)
- if failed {
+ if <-inputChangeFailed {
t.Fatalf("joystick change failed")
}
+ c1.mostRecentInput = int32(InputJoystick)
*expectedType = InputMouse
c1.TriggerMouseEvent(mouse.Event{EventType: mouse.Press})
- time.Sleep(2 * time.Second)
- if failed {
+ if <-inputChangeFailed {
t.Fatalf("mouse change failed")
}
*expectedType = InputKeyboard
c1.mostRecentInput = int32(InputJoystick)
c1.TriggerKeyDown(key.Event{})
- time.Sleep(2 * time.Second)
- if failed {
+ if <-inputChangeFailed {
t.Fatalf("keyboard change failed")
}
+ if c1.MostRecentInput() != InputKeyboard {
+ t.Fatalf("most recent input getter failed")
+ }
}
diff --git a/joystick/driver_darwin.go b/joystick/driver_darwin.go
index 396d1ceb..5bcb09ad 100644
--- a/joystick/driver_darwin.go
+++ b/joystick/driver_darwin.go
@@ -1,6 +1,6 @@
package joystick
-import "github.com/oakmound/oak/v3/oakerr"
+import "github.com/oakmound/oak/v4/oakerr"
func osinit() error {
return nil
@@ -14,19 +14,19 @@ type osJoystick struct {
}
func (j *Joystick) prepare() error {
- return oakerr.UnsupportedPlatform{Operation:"joystick"}
+ return oakerr.UnsupportedPlatform{Operation: "joystick"}
}
func (j *Joystick) getState() (*State, error) {
- return nil, oakerr.UnsupportedPlatform{Operation:"joystick"}
+ return nil, oakerr.UnsupportedPlatform{Operation: "joystick"}
}
func (j *Joystick) vibrate(left, right uint16) error {
- return oakerr.UnsupportedPlatform{Operation:"joystick"}
+ return oakerr.UnsupportedPlatform{Operation: "joystick"}
}
func (j *Joystick) close() error {
- return oakerr.UnsupportedPlatform{Operation:"joystick"}
+ return oakerr.UnsupportedPlatform{Operation: "joystick"}
}
func getJoysticks() []*Joystick {
diff --git a/joystick/driver_js.go b/joystick/driver_js.go
index eb36b4e0..063c8ec5 100644
--- a/joystick/driver_js.go
+++ b/joystick/driver_js.go
@@ -1,17 +1,17 @@
package joystick
import (
+ "errors"
"reflect"
"syscall/js"
- "errors"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/oakerr"
- "github.com/oakmound/oak/v3/timing"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/oakerr"
+ "github.com/oakmound/oak/v4/timing"
)
-func osinit() error {
- // TODO: listen to joystick connected and joystick disconnected? We'd still need to
+func osinit() error {
+ // TODO: listen to joystick connected and joystick disconnected? We'd still need to
// list from getGamepads every frame, it seems, to get new button presses.
return nil
}
@@ -63,19 +63,19 @@ type jsGamepadState struct {
connected bool
// osID string
// index int
- mapping string
+ mapping string
}
type jsButton struct {
- value float64
+ value float64
//touched bool
pressed bool
}
type osJoystick struct {
- cache State
- jsState jsGamepadState
- newJSState jsGamepadState
+ cache State
+ jsState jsGamepadState
+ newJSState jsGamepadState
newButtons map[string]bool
}
diff --git a/joystick/driver_linux.go b/joystick/driver_linux.go
index 4aed3e51..67e11c41 100644
--- a/joystick/driver_linux.go
+++ b/joystick/driver_linux.go
@@ -7,10 +7,10 @@ import (
"strconv"
"sync"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/oakerr"
- "github.com/oakmound/oak/v3/timing"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/oakerr"
+ "github.com/oakmound/oak/v4/timing"
"encoding/binary"
"path"
diff --git a/joystick/driver_other.go b/joystick/driver_other.go
index 34d4f06d..4f2abe8f 100644
--- a/joystick/driver_other.go
+++ b/joystick/driver_other.go
@@ -3,7 +3,7 @@
package joystick
-import "github.com/oakmound/oak/v3/oakerr"
+import "github.com/oakmound/oak/v4/oakerr"
func newOsJoystick() osJoystick {
return osJoystick{}
diff --git a/joystick/driver_windows.go b/joystick/driver_windows.go
index 7d64b1c6..4485699c 100644
--- a/joystick/driver_windows.go
+++ b/joystick/driver_windows.go
@@ -3,8 +3,8 @@ package joystick
import (
"sync"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/timing"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/timing"
"github.com/oakmound/w32"
)
diff --git a/joystick/joystick.go b/joystick/joystick.go
index e2f12513..856381b7 100644
--- a/joystick/joystick.go
+++ b/joystick/joystick.go
@@ -7,8 +7,8 @@ import (
"sync"
"time"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/event"
)
type Input string
diff --git a/key/events.go b/key/events.go
index cfbd06f6..143261f2 100644
--- a/key/events.go
+++ b/key/events.go
@@ -3,7 +3,7 @@ package key
import (
"sync"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/event"
"golang.org/x/mobile/event/key"
)
diff --git a/lifecycle.go b/lifecycle.go
index 7df9106a..b202e8d8 100644
--- a/lifecycle.go
+++ b/lifecycle.go
@@ -4,11 +4,11 @@ import (
"image"
"image/draw"
- "github.com/oakmound/oak/v3/alg"
- "github.com/oakmound/oak/v3/debugstream"
+ "github.com/oakmound/oak/v4/alg"
+ "github.com/oakmound/oak/v4/debugstream"
"golang.org/x/mobile/event/lifecycle"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
func (w *Window) lifecycleLoop(s screen.Screen) {
@@ -102,10 +102,15 @@ func (w *Window) ChangeWindow(width, height int) error {
return nil
}
-// UpdateViewSize updates the size of this window's viewport.
+// UpdateViewSize updates the size of this window's viewport. If the window has yet
+// to be initialized, it will update ScreenWidth and ScreenHeight, and then exit.
func (w *Window) UpdateViewSize(width, height int) error {
w.ScreenWidth = width
w.ScreenHeight = height
+ // this is being called before Init
+ if w.screenControl == nil {
+ return nil
+ }
for i := 0; i < bufferCount; i++ {
newBuffer, err := w.screenControl.NewImage(image.Point{width, height})
if err != nil {
diff --git a/loading.go b/loading.go
index 70c68e8c..e0d75271 100644
--- a/loading.go
+++ b/loading.go
@@ -3,10 +3,10 @@ package oak
import (
"io/fs"
- "github.com/oakmound/oak/v3/audio"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/fileutil"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/audio"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/fileutil"
+ "github.com/oakmound/oak/v4/render"
"golang.org/x/sync/errgroup"
)
@@ -27,11 +27,8 @@ func (w *Window) loadAssets(imageDir, audioDir string) {
} else {
err = audio.BatchLoad(audioDir)
}
- if err != nil {
- return err
- }
dlog.Verb("Done Loading Audio")
- return nil
+ return err
})
dlog.ErrorCheck(eg.Wait())
}
diff --git a/loading_test.go b/loading_test.go
index 3d03e82b..ba242a69 100644
--- a/loading_test.go
+++ b/loading_test.go
@@ -4,7 +4,7 @@ import (
"os"
"testing"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/scene"
)
func TestBatchLoad_HappyPath(t *testing.T) {
diff --git a/mouse/default.go b/mouse/default.go
index 0200513f..afda470e 100644
--- a/mouse/default.go
+++ b/mouse/default.go
@@ -1,6 +1,6 @@
package mouse
-import "github.com/oakmound/oak/v3/collision"
+import "github.com/oakmound/oak/v4/collision"
// DefaultTree is a collision tree intended to be used by default if no other
// is instantiated. Methods on a collision tree are duplicated as functions
diff --git a/mouse/default_test.go b/mouse/default_test.go
index d44fa0d9..64684588 100644
--- a/mouse/default_test.go
+++ b/mouse/default_test.go
@@ -3,7 +3,7 @@ package mouse
import (
"testing"
- "github.com/oakmound/oak/v3/collision"
+ "github.com/oakmound/oak/v4/collision"
)
func TestDefaultFunctions(t *testing.T) {
diff --git a/mouse/event.go b/mouse/event.go
index b17cbf06..7ffd5309 100644
--- a/mouse/event.go
+++ b/mouse/event.go
@@ -1,9 +1,9 @@
package mouse
import (
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/event"
)
var (
diff --git a/mouse/event_test.go b/mouse/event_test.go
index e77c2f37..97df2d0c 100644
--- a/mouse/event_test.go
+++ b/mouse/event_test.go
@@ -3,7 +3,7 @@ package mouse
import (
"testing"
- "github.com/oakmound/oak/v3/collision"
+ "github.com/oakmound/oak/v4/collision"
)
func TestEventConversions(t *testing.T) {
diff --git a/mouse/events.go b/mouse/events.go
index 8ed4832f..628e4926 100644
--- a/mouse/events.go
+++ b/mouse/events.go
@@ -1,6 +1,6 @@
package mouse
-import "github.com/oakmound/oak/v3/event"
+import "github.com/oakmound/oak/v4/event"
var (
// Press is triggered when a mouse key is pressed down
diff --git a/mouse/events_test.go b/mouse/events_test.go
new file mode 100644
index 00000000..112b328b
--- /dev/null
+++ b/mouse/events_test.go
@@ -0,0 +1,67 @@
+package mouse
+
+import (
+ "testing"
+
+ "github.com/oakmound/oak/v4/event"
+)
+
+func TestEventOn(t *testing.T) {
+ t.Run("AllEvents", func(t *testing.T) {
+ if ev2, ok := EventOn(Press); !ok || ev2 != PressOn {
+ t.Error("Press was not matched to PressOn")
+ }
+ if ev2, ok := EventOn(Release); !ok || ev2 != ReleaseOn {
+ t.Error("Release was not matched to ReleaseOn")
+ }
+ if ev2, ok := EventOn(ScrollDown); !ok || ev2 != ScrollDownOn {
+ t.Error("ScrollDown was not matched to ScrollDownOn")
+ }
+ if ev2, ok := EventOn(ScrollUp); !ok || ev2 != ScrollUpOn {
+ t.Error("ScrollUp was not matched to ScrollUpOn")
+ }
+ if ev2, ok := EventOn(Click); !ok || ev2 != ClickOn {
+ t.Error("Click was not matched to ClickOn")
+ }
+ if ev2, ok := EventOn(Drag); !ok || ev2 != DragOn {
+ t.Error("Drag was not matched to DragOn")
+ }
+ })
+ t.Run("Unknown", func(t *testing.T) {
+ ev := event.RegisterEvent[*Event]()
+ _, ok := EventOn(ev)
+ if ok {
+ t.Error("EventOn should have returned false for an unknown event")
+ }
+ })
+}
+
+func TestEventRelative(t *testing.T) {
+ t.Run("AllEvents", func(t *testing.T) {
+ if ev2, ok := EventRelative(PressOn); !ok || ev2 != RelativePressOn {
+ t.Error("PressOn was not matched to RelativePressOn")
+ }
+ if ev2, ok := EventRelative(ReleaseOn); !ok || ev2 != RelativeReleaseOn {
+ t.Error("ReleaseOn was not matched to RelativeReleaseOn")
+ }
+ if ev2, ok := EventRelative(ScrollDownOn); !ok || ev2 != RelativeScrollDownOn {
+ t.Error("ScrollDownOn was not matched to RelativeScrollDownOn")
+ }
+ if ev2, ok := EventRelative(ScrollUpOn); !ok || ev2 != RelativeScrollUpOn {
+ t.Error("ScrollUpOn was not matched to RelativeScrollUpOn")
+ }
+ if ev2, ok := EventRelative(ClickOn); !ok || ev2 != RelativeClickOn {
+ t.Error("ClickOn was not matched to RelativeClickOn")
+ }
+ if ev2, ok := EventRelative(DragOn); !ok || ev2 != RelativeDragOn {
+ t.Error("DragOn was not matched to RelativeDragOn")
+ }
+ })
+ t.Run("Unknown", func(t *testing.T) {
+ ev := event.RegisterEvent[*Event]()
+ _, ok := EventRelative(ev)
+ if ok {
+ t.Error("EventRelative should have returned false for an unknown event")
+ }
+ })
+}
diff --git a/mouse/mouse.go b/mouse/mouse.go
index 5caa8498..a6f93cb3 100644
--- a/mouse/mouse.go
+++ b/mouse/mouse.go
@@ -1,7 +1,7 @@
package mouse
import (
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/event"
"golang.org/x/mobile/event/mouse"
)
diff --git a/mouse/onCollision.go b/mouse/onCollision.go
index 7c77abaf..36fbb469 100644
--- a/mouse/onCollision.go
+++ b/mouse/onCollision.go
@@ -3,8 +3,8 @@ package mouse
import (
"errors"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/event"
)
// CollisionPhase is a component that can be placed into another struct to
diff --git a/mouse/onCollision_test.go b/mouse/onCollision_test.go
index 12fe61a5..2c4fdd89 100644
--- a/mouse/onCollision_test.go
+++ b/mouse/onCollision_test.go
@@ -4,9 +4,9 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/event"
)
type cphase struct {
diff --git a/physics/force.go b/physics/force.go
index a0b8f2f7..de316caf 100644
--- a/physics/force.go
+++ b/physics/force.go
@@ -1,7 +1,7 @@
package physics
import (
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/oakerr"
)
const frozen = -64
diff --git a/physics/vector.go b/physics/vector.go
index a19a55e7..b36a225e 100644
--- a/physics/vector.go
+++ b/physics/vector.go
@@ -3,7 +3,7 @@ package physics
import (
"math"
- "github.com/oakmound/oak/v3/alg"
+ "github.com/oakmound/oak/v4/alg"
)
// A Vector is a two-dimensional point or vector used throughout oak
diff --git a/physics/vector_test.go b/physics/vector_test.go
index 2ad81e81..cb727dbb 100644
--- a/physics/vector_test.go
+++ b/physics/vector_test.go
@@ -3,7 +3,7 @@ package physics
import (
"testing"
- "github.com/oakmound/oak/v3/alg"
+ "github.com/oakmound/oak/v4/alg"
)
func TestVectorFuncs(t *testing.T) {
diff --git a/render/bachload_test.go b/render/bachload_test.go
index 09e4697c..2d67bf2e 100644
--- a/render/bachload_test.go
+++ b/render/bachload_test.go
@@ -4,7 +4,7 @@ import (
"errors"
"testing"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/oakerr"
)
func TestBlankBatchLoad_BadBaseFolder(t *testing.T) {
diff --git a/render/batchload.go b/render/batchload.go
index 27f8165b..8400de4c 100644
--- a/render/batchload.go
+++ b/render/batchload.go
@@ -8,10 +8,10 @@ import (
"strconv"
"sync"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/fileutil"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/fileutil"
+ "github.com/oakmound/oak/v4/oakerr"
)
// BatchLoad loads subdirectories from the given base folder and imports all files,
diff --git a/render/bezier.go b/render/bezier.go
index 1e0c3974..49ce88ec 100644
--- a/render/bezier.go
+++ b/render/bezier.go
@@ -4,9 +4,9 @@ import (
"image"
"image/color"
- "github.com/oakmound/oak/v3/alg"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/shape"
+ "github.com/oakmound/oak/v4/alg"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/shape"
)
// BezierLine converts a bezier into a line sprite.
diff --git a/render/bezier_test.go b/render/bezier_test.go
index 8172c12a..4daa242b 100644
--- a/render/bezier_test.go
+++ b/render/bezier_test.go
@@ -4,7 +4,7 @@ import (
"image/color"
"testing"
- "github.com/oakmound/oak/v3/shape"
+ "github.com/oakmound/oak/v4/shape"
)
func TestSimpleBezierLine(t *testing.T) {
diff --git a/render/cache.go b/render/cache.go
index d3bca480..7a1a2e32 100644
--- a/render/cache.go
+++ b/render/cache.go
@@ -5,7 +5,7 @@ import (
"sync"
"github.com/golang/freetype/truetype"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
// DefaultCache is the receiver for package level sprites, sheets, and font loading operations.
diff --git a/render/cache_test.go b/render/cache_test.go
index 2782c4a3..c194eed0 100644
--- a/render/cache_test.go
+++ b/render/cache_test.go
@@ -17,4 +17,14 @@ func TestCache_Clear(t *testing.T) {
if err == nil {
t.Fatal("get jeremy should have failed post-Clear")
}
+ file = "testdata/assets/fonts/luxisr.ttf"
+ _, err = LoadFont(file)
+ if err != nil {
+ t.Fatalf("load luxisr should have succeeded: %v", err)
+ }
+ DefaultCache.Clear(file)
+ _, err = GetFont(file)
+ if err == nil {
+ t.Fatal("get luxisr should have failed post-Clear")
+ }
}
diff --git a/render/colorbox.go b/render/colorbox.go
index 4eac32a5..b496b5b3 100644
--- a/render/colorbox.go
+++ b/render/colorbox.go
@@ -5,7 +5,7 @@ import (
"image/color"
"image/draw"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
// NewColorBox returns a Sprite full of a given color with the given dimensions
diff --git a/render/compositeM.go b/render/compositeM.go
index 0438f006..17ec5c9d 100644
--- a/render/compositeM.go
+++ b/render/compositeM.go
@@ -4,8 +4,8 @@ import (
"image"
"image/draw"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/render/mod"
)
// CompositeM Types display all of their parts at the same time,
diff --git a/render/compositeM_test.go b/render/compositeM_test.go
index ee8c827d..a8001dc5 100644
--- a/render/compositeM_test.go
+++ b/render/compositeM_test.go
@@ -6,8 +6,8 @@ import (
"reflect"
"testing"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/render/mod"
)
func TestComposite(t *testing.T) {
diff --git a/render/compositeR.go b/render/compositeR.go
index 3c63cf77..ba9220c1 100644
--- a/render/compositeR.go
+++ b/render/compositeR.go
@@ -5,8 +5,8 @@ import (
"image/draw"
"sync"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
// A CompositeR is equivalent to a CompositeM for Renderables instead of
diff --git a/render/compositeR_test.go b/render/compositeR_test.go
index 3beb08fd..20cdf0b0 100644
--- a/render/compositeR_test.go
+++ b/render/compositeR_test.go
@@ -6,8 +6,8 @@ import (
"reflect"
"testing"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
func TestCompositeR(t *testing.T) {
diff --git a/render/curve.go b/render/curve.go
index 8406113c..8fe72c8b 100644
--- a/render/curve.go
+++ b/render/curve.go
@@ -5,8 +5,8 @@ import (
"image/color"
"math"
- "github.com/oakmound/oak/v3/alg"
- "github.com/oakmound/oak/v3/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
)
// NewCircle creates a sprite and draws a circle onto it
diff --git a/render/decoder.go b/render/decoder.go
index 81a17755..b93ce20f 100644
--- a/render/decoder.go
+++ b/render/decoder.go
@@ -2,14 +2,9 @@ package render
import (
"image"
- "image/gif"
- "image/jpeg"
- "image/png"
"io"
- "github.com/oakmound/oak/v3/oakerr"
-
- "golang.org/x/image/bmp"
+ "github.com/oakmound/oak/v4/oakerr"
)
// Decoder functions convert arbitrary readers to images.
@@ -22,20 +17,8 @@ type Decoder func(io.Reader) (image.Image, error)
type CfgDecoder func(io.Reader) (image.Config, error)
var (
- fileDecoders = map[string]Decoder{
- ".jpeg": jpeg.Decode,
- ".jpg": jpeg.Decode,
- ".gif": gif.Decode,
- ".png": png.Decode,
- ".bmp": bmp.Decode,
- }
- cfgDecoders = map[string]CfgDecoder{
- ".jpeg": jpeg.DecodeConfig,
- ".jpg": jpeg.DecodeConfig,
- ".gif": gif.DecodeConfig,
- ".png": png.DecodeConfig,
- ".bmp": bmp.DecodeConfig,
- }
+ fileDecoders = map[string]Decoder{}
+ cfgDecoders = map[string]CfgDecoder{}
)
// RegisterDecoder adds a decoder to the set of image decoders
diff --git a/render/default_decoders.go b/render/default_decoders.go
new file mode 100644
index 00000000..ea0c1ca3
--- /dev/null
+++ b/render/default_decoders.go
@@ -0,0 +1,26 @@
+//go:build !noimages
+// +build !noimages
+
+package render
+
+import (
+ "image/gif"
+ "image/jpeg"
+ "image/png"
+
+ "golang.org/x/image/bmp"
+)
+
+func init() {
+ // Register standard image decoders. If provided with the build tag 'noimages', this is skipped.
+ RegisterDecoder(".jpeg", jpeg.Decode)
+ RegisterDecoder(".jpg", jpeg.Decode)
+ RegisterDecoder(".gif", gif.Decode)
+ RegisterDecoder(".png", png.Decode)
+ RegisterDecoder(".bmp", bmp.Decode)
+ RegisterCfgDecoder(".jpeg", jpeg.DecodeConfig)
+ RegisterCfgDecoder(".jpg", jpeg.DecodeConfig)
+ RegisterCfgDecoder(".gif", gif.DecodeConfig)
+ RegisterCfgDecoder(".png", png.DecodeConfig)
+ RegisterCfgDecoder(".bmp", bmp.DecodeConfig)
+}
diff --git a/render/draw.go b/render/draw.go
index 81287f14..17648e53 100644
--- a/render/draw.go
+++ b/render/draw.go
@@ -10,8 +10,7 @@ var (
emptyRenderable = NewColorBox(1, 1, color.RGBA{0, 0, 0, 0})
)
-// EmptyRenderable returns a minimal, 1-width and height pseudo-nil
-// Renderable (and Modifiable)
+// EmptyRenderable returns a minimal, 1-width and height pseudo-nil Renderable
func EmptyRenderable() Modifiable {
return emptyRenderable.Copy()
}
diff --git a/render/drawHeap.go b/render/drawHeap.go
index 23cf4ec5..da56eb61 100644
--- a/render/drawHeap.go
+++ b/render/drawHeap.go
@@ -4,7 +4,7 @@ import (
"image/draw"
"sync"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
// A RenderableHeap manages a set of renderables to be drawn in explicit layered
diff --git a/render/drawHeap_test.go b/render/drawHeap_test.go
index 6012e5fb..dd981726 100644
--- a/render/drawHeap_test.go
+++ b/render/drawHeap_test.go
@@ -5,7 +5,7 @@ import (
"image/color"
"testing"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
const heapLoops = 2000
diff --git a/render/drawStack.go b/render/drawStack.go
index dd268c89..2b3188c5 100644
--- a/render/drawStack.go
+++ b/render/drawStack.go
@@ -3,8 +3,8 @@ package render
import (
"image/draw"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/oakerr"
)
var (
diff --git a/render/drawStack_test.go b/render/drawStack_test.go
index 8ff14adc..9da7c7fb 100644
--- a/render/drawStack_test.go
+++ b/render/drawStack_test.go
@@ -6,7 +6,7 @@ import (
"reflect"
"testing"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
func TestDrawStack(t *testing.T) {
@@ -26,6 +26,10 @@ func TestDrawStack(t *testing.T) {
if len(GlobalDrawStack.as) != 1 {
t.Fatalf("global draw stack did not have one length after pop")
}
+ cp := GlobalDrawStack.Copy()
+ if len(cp.toPush) != len(GlobalDrawStack.toPush) {
+ t.Fatalf("copy failed to copy push length")
+ }
}
func TestDrawStack_Draw(t *testing.T) {
diff --git a/render/font.go b/render/font.go
index cbaf84f1..a95dd7cf 100644
--- a/render/font.go
+++ b/render/font.go
@@ -12,9 +12,9 @@ import (
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/fileutil"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/fileutil"
+ "github.com/oakmound/oak/v4/oakerr"
)
var (
diff --git a/render/font_test.go b/render/font_test.go
index 625c3117..12f86913 100644
--- a/render/font_test.go
+++ b/render/font_test.go
@@ -7,7 +7,7 @@ import (
"math/rand"
"testing"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/oakerr"
"golang.org/x/image/colornames"
)
@@ -57,6 +57,37 @@ func TestFontGenerator_validate(t *testing.T) {
}
}
+func TestFontGenerator_Generate_Failure(t *testing.T) {
+ t.Run("BadRawFile", func(t *testing.T) {
+ fg := FontGenerator{
+ RawFile: []byte("notafontfile"),
+ Color: image.NewUniform(color.RGBA{255, 0, 0, 255}),
+ FontOptions: FontOptions{
+ Size: 13.0,
+ DPI: 44.0,
+ },
+ }
+ _, err := fg.Generate()
+ if err == nil {
+ t.Fatalf("generate should have failed")
+ }
+ })
+ t.Run("BadLoadFont", func(t *testing.T) {
+ fg := FontGenerator{
+ File: "file that does not exist",
+ Color: image.NewUniform(color.RGBA{255, 0, 0, 255}),
+ FontOptions: FontOptions{
+ Size: 13.0,
+ DPI: 44.0,
+ },
+ }
+ _, err := fg.Generate()
+ if err == nil {
+ t.Fatalf("generate should have failed")
+ }
+ })
+}
+
func TestFontGenerator_Generate_Success(t *testing.T) {
fg := FontGenerator{
File: "testdata/assets/fonts/luxisr.ttf",
@@ -71,3 +102,109 @@ func TestFontGenerator_Generate_Success(t *testing.T) {
t.Fatalf("generate failed: %v", err)
}
}
+
+func TestFont_Height(t *testing.T) {
+ ht := rand.Float64() * 10
+ fg := FontGenerator{
+ File: "testdata/assets/fonts/luxisr.ttf",
+ Color: image.NewUniform(color.RGBA{255, 0, 0, 255}),
+ FontOptions: FontOptions{
+ Size: ht,
+ DPI: 44.0,
+ },
+ }
+ f, err := fg.Generate()
+ if err != nil {
+ t.Fatalf("generate failed: %v", err)
+ }
+ if f.Height() != ht {
+ t.Fatalf("size did not match height: got %v expected %v", f.Height(), ht)
+ }
+}
+
+func TestFont_RegenerateWith(t *testing.T) {
+ fg := FontGenerator{
+ File: "testdata/assets/fonts/luxisr.ttf",
+ Color: image.NewUniform(color.RGBA{255, 0, 0, 255}),
+ FontOptions: FontOptions{
+ Size: 13.0,
+ DPI: 44.0,
+ },
+ }
+ f, err := fg.Generate()
+ if err != nil {
+ t.Fatalf("generate failed: %v", err)
+ }
+ f2, err := f.RegenerateWith(func(fg FontGenerator) FontGenerator {
+ fg.Size = 100
+ return fg
+ })
+ if err != nil {
+ t.Fatalf("regenerate failed: %v", err)
+ }
+ if f2.Height() != 100 {
+ t.Fatalf("size did not match height: got %v expected %v", f.Height(), 100)
+ }
+}
+
+func TestCache_LoadFont(t *testing.T) {
+ t.Run("NotExists", func(t *testing.T) {
+ c := NewCache()
+ _, err := c.LoadFont("bogusfilepath")
+ if err == nil {
+ t.Fatal("expected error loading bad file")
+ }
+ })
+ t.Run("NotFontFile", func(t *testing.T) {
+ c := NewCache()
+ _, err := c.LoadFont("testdata/assets/images/devfile.pdn")
+ if err == nil {
+ t.Fatal("expected error loading non-font")
+ }
+ })
+ t.Run("GetCached", func(t *testing.T) {
+ c := NewCache()
+ _, err := c.LoadFont("testdata/assets/fonts/luxisr.ttf")
+ if err != nil {
+ t.Fatal("failed to load font into cache")
+ }
+ _, err = c.GetFont("luxisr.ttf")
+ if err != nil {
+ t.Fatalf("failed to get cached font: %v", err)
+ }
+ })
+ t.Run("GetUncached", func(t *testing.T) {
+ c := NewCache()
+ _, err := c.GetFont("luxisr.ttf")
+ if err == nil {
+ t.Fatalf("expected error getting uncached font")
+ }
+ })
+}
+
+func TestFont_Fallback(t *testing.T) {
+ fg := FontGenerator{
+ File: "testdata/assets/fonts/luxisr.ttf",
+ Color: image.NewUniform(color.RGBA{255, 0, 0, 255}),
+ FontOptions: FontOptions{
+ Size: 13.0,
+ DPI: 44.0,
+ },
+ }
+ f, err := fg.Generate()
+ if err != nil {
+ t.Fatalf("generate failed: %v", err)
+ }
+
+ fg.File = "testdata/assets/fonts/seguiemj.ttf"
+ emjfont, err := fg.Generate()
+ if err != nil {
+ t.Fatalf("generate failed: %v", err)
+ }
+
+ f.Fallbacks = append(f.Fallbacks, emjfont)
+
+ f.MeasureString("a😀b😃c😄d😁e本")
+ txt := f.NewText("a😀b😃c😄d😁e本", 0, 0)
+ txt.Draw(image.NewRGBA(image.Rect(0, 0, 200, 200)), 0, 0)
+}
diff --git a/render/fps.go b/render/fps.go
index 9b10a0a4..d7992b25 100644
--- a/render/fps.go
+++ b/render/fps.go
@@ -4,7 +4,7 @@ import (
"image/draw"
"time"
- "github.com/oakmound/oak/v3/timing"
+ "github.com/oakmound/oak/v4/timing"
)
const (
diff --git a/render/interfaceFeatures.go b/render/interfaceFeatures.go
index 1b8a8dfc..803a88d4 100644
--- a/render/interfaceFeatures.go
+++ b/render/interfaceFeatures.go
@@ -1,6 +1,6 @@
package render
-import "github.com/oakmound/oak/v3/event"
+import "github.com/oakmound/oak/v4/event"
// NonStatic types are not always static. If something is not NonStatic,
// it is equivalent to having IsStatic always return true.
diff --git a/render/layered.go b/render/layered.go
index 97031f8a..2c2024db 100644
--- a/render/layered.go
+++ b/render/layered.go
@@ -1,7 +1,7 @@
package render
import (
- "github.com/oakmound/oak/v3/physics"
+ "github.com/oakmound/oak/v4/physics"
)
const (
diff --git a/render/line.go b/render/line.go
index 09c8e217..f37ba9d8 100644
--- a/render/line.go
+++ b/render/line.go
@@ -5,7 +5,7 @@ import (
"image/color"
"math"
- "github.com/oakmound/oak/v3/alg/range/colorrange"
+ "github.com/oakmound/oak/v4/alg/span"
)
// Todo:
@@ -30,7 +30,7 @@ func NewThickLine(x1, y1, x2, y2 float64, c color.Color, thickness int) *Sprite
// NewGradientLine returns a Line that has some value of thickness along with a start and end color
func NewGradientLine(x1, y1, x2, y2 float64, c1, c2 color.Color, thickness int) *Sprite {
- colorer := colorrange.NewLinear(c1, c2).Percentile
+ colorer := span.NewLinearColor(c1, c2).Percentile
return NewColoredLine(x1, y1, x2, y2, colorer, thickness)
}
@@ -57,7 +57,7 @@ func DrawThickLine(rgba *image.RGBA, x1, y1, x2, y2 int, c color.Color, thicknes
//DrawGradientLine acts like DrawThickLine but also applies a gradient to the line
func DrawGradientLine(rgba *image.RGBA, x1, y1, x2, y2 int, c1, c2 color.Color, thickness int) {
- colorer := colorrange.NewLinear(c1, c2).Percentile
+ colorer := span.NewLinearColor(c1, c2).Percentile
DrawLineColored(rgba, x1, y1, x2, y2, thickness, colorer)
}
diff --git a/render/loadsheet.go b/render/loadsheet.go
index cd513dc4..1db88a37 100644
--- a/render/loadsheet.go
+++ b/render/loadsheet.go
@@ -4,8 +4,8 @@ import (
"image"
"path/filepath"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/oakerr"
)
// LoadSheet loads a file in some directory with sheets of (w,h) sized sprites.
diff --git a/render/loadsheet_test.go b/render/loadsheet_test.go
index 8d9e05f2..1a079522 100644
--- a/render/loadsheet_test.go
+++ b/render/loadsheet_test.go
@@ -6,8 +6,8 @@ import (
"path/filepath"
"testing"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/oakerr"
)
var (
diff --git a/render/loadsprite.go b/render/loadsprite.go
index 21c38be7..34172bc8 100644
--- a/render/loadsprite.go
+++ b/render/loadsprite.go
@@ -8,8 +8,8 @@ import (
"path/filepath"
"strings"
- "github.com/oakmound/oak/v3/fileutil"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/fileutil"
+ "github.com/oakmound/oak/v4/oakerr"
)
func loadSpriteNoCache(file string, maxFileSize int64) (*image.RGBA, error) {
diff --git a/render/logicfps.go b/render/logicfps.go
index edce75c1..f6dd71ac 100644
--- a/render/logicfps.go
+++ b/render/logicfps.go
@@ -3,8 +3,8 @@ package render
import (
"time"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/timing"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/timing"
)
// LogicFPS is a Stackable that will draw the logical fps onto the screen when a part
diff --git a/render/logicfps_test.go b/render/logicfps_test.go
index 62e908fa..981c2c71 100644
--- a/render/logicfps_test.go
+++ b/render/logicfps_test.go
@@ -4,7 +4,7 @@ import (
"image"
"testing"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/event"
)
func TestLogicFPS(t *testing.T) {
diff --git a/render/mod/cut.go b/render/mod/cut.go
index 8d1f01fc..4363fc78 100644
--- a/render/mod/cut.go
+++ b/render/mod/cut.go
@@ -4,9 +4,9 @@ import (
"image"
"image/color"
- "github.com/oakmound/oak/v3/alg"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/shape"
+ "github.com/oakmound/oak/v4/alg"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/shape"
)
// CutRound rounds the edges of the Modifiable with Bezier curves.
diff --git a/render/mod/filter.go b/render/mod/filter.go
index f578cfbd..eba448d4 100644
--- a/render/mod/filter.go
+++ b/render/mod/filter.go
@@ -20,9 +20,9 @@ func AndFilter(fs ...Filter) Filter {
}
}
-// ConformToPallete is not a modification, but acts like ConformToPallete
+// ConformToPalette( is not a modification, but acts like ConformToPalette(
// without allocating a new *image.RGBA
-func ConformToPallete(p color.Model) Filter {
+func ConformToPalette(p color.Model) Filter {
return func(rgba *image.RGBA) {
bounds := rgba.Bounds()
w := bounds.Max.X
diff --git a/render/mod/gift.go b/render/mod/gift.go
index 36a20815..6423bb0b 100644
--- a/render/mod/gift.go
+++ b/render/mod/gift.go
@@ -1,8 +1,12 @@
+//go:build !nogift
+// +build !nogift
+
package mod
import (
"image"
"image/color"
+ "math"
"github.com/disintegration/gift"
)
@@ -124,3 +128,17 @@ var Transpose = GiftTransform(gift.Transpose())
// Transverse flips vertically and rotates 90 degrees counter clockwise.
var Transverse = GiftTransform(gift.Transverse())
+
+// Scale returns a scaled rgba.
+func Scale(xRatio, yRatio float64) Mod {
+ return func(rgba image.Image) *image.RGBA {
+ bounds := rgba.Bounds()
+ w := int(math.Floor(float64(bounds.Max.X) * xRatio))
+ h := int(math.Floor(float64(bounds.Max.Y) * yRatio))
+ filter := gift.New(
+ gift.Resize(w, h, gift.CubicResampling))
+ dst := image.NewRGBA(filter.Bounds(rgba.Bounds()))
+ filter.Draw(dst, rgba)
+ return dst
+ }
+}
diff --git a/render/mod/highlight.go b/render/mod/highlight.go
index 15ea5f30..d02939f6 100644
--- a/render/mod/highlight.go
+++ b/render/mod/highlight.go
@@ -4,7 +4,7 @@ import (
"image"
"image/color"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
func HighlightOff(c color.Color, thickness, xOff, yOff int) Mod {
diff --git a/render/mod/mod.go b/render/mod/mod.go
index 1ff484a6..295f3d4f 100644
--- a/render/mod/mod.go
+++ b/render/mod/mod.go
@@ -3,9 +3,6 @@ package mod
import (
"image"
"image/color"
- "math"
-
- "github.com/disintegration/gift"
)
// A Mod takes an image and returns that image transformed in some way.
@@ -44,20 +41,6 @@ func SafeAnd(ms ...Mod) Mod {
return And(ms...)
}
-// Scale returns a scaled rgba.
-func Scale(xRatio, yRatio float64) Mod {
- return func(rgba image.Image) *image.RGBA {
- bounds := rgba.Bounds()
- w := int(math.Floor(float64(bounds.Max.X) * xRatio))
- h := int(math.Floor(float64(bounds.Max.Y) * yRatio))
- filter := gift.New(
- gift.Resize(w, h, gift.CubicResampling))
- dst := image.NewRGBA(filter.Bounds(rgba.Bounds()))
- filter.Draw(dst, rgba)
- return dst
- }
-}
-
// TrimColor will trim inputs so that any rows or columns where each pixel is
// less than or equal to the input color are removed. This will change the dimensions
// of the image.
diff --git a/render/mod/mod_test.go b/render/mod/mod_test.go
index a32f13bc..ec2795df 100644
--- a/render/mod/mod_test.go
+++ b/render/mod/mod_test.go
@@ -7,7 +7,7 @@ import (
"testing"
"github.com/disintegration/gift"
- "github.com/oakmound/oak/v3/shape"
+ "github.com/oakmound/oak/v4/shape"
)
func TestComposedModifications(t *testing.T) {
@@ -70,7 +70,7 @@ func TestAllModifications(t *testing.T) {
*image.RGBA
}
filterList := []filterCase{{
- ConformToPallete(color.Palette{color.RGBA{64, 0, 0, 128}}),
+ ConformToPalette(color.Palette{color.RGBA{64, 0, 0, 128}}),
setAll(newrgba(3, 3), color.RGBA{64, 0, 0, 128}),
}, {
Fade(10),
diff --git a/render/modifiable.go b/render/modifiable.go
index 4446fd65..46ab9684 100644
--- a/render/modifiable.go
+++ b/render/modifiable.go
@@ -3,7 +3,7 @@ package render
import (
"image"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/render/mod"
)
// A Modifiable is a Renderable that has functions to change its
diff --git a/render/noopStackable.go b/render/noopStackable.go
index ad695ccb..de665c50 100644
--- a/render/noopStackable.go
+++ b/render/noopStackable.go
@@ -3,7 +3,7 @@ package render
import (
"image/draw"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
// NoopStackable is a Stackable element where all methods are no-ops.
diff --git a/render/noopStackable_test.go b/render/noopStackable_test.go
index 5357c611..d986adcb 100644
--- a/render/noopStackable_test.go
+++ b/render/noopStackable_test.go
@@ -3,7 +3,7 @@ package render
import (
"testing"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
func TestNoopStackable(t *testing.T) {
@@ -20,4 +20,5 @@ func TestNoopStackable(t *testing.T) {
if noop2 != noop {
t.Fatalf("expected equal noop stackables")
}
+ noop.Clear()
}
diff --git a/render/particle/allocator.go b/render/particle/allocator.go
index 83706c5d..13a7f9aa 100644
--- a/render/particle/allocator.go
+++ b/render/particle/allocator.go
@@ -1,7 +1,7 @@
package particle
import (
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/event"
)
const (
diff --git a/render/particle/allocator_test.go b/render/particle/allocator_test.go
index bbf04497..546d1567 100644
--- a/render/particle/allocator_test.go
+++ b/render/particle/allocator_test.go
@@ -3,7 +3,7 @@ package particle
import (
"testing"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/event"
)
func TestAllocate(t *testing.T) {
diff --git a/render/particle/collisionParticle.go b/render/particle/collisionParticle.go
index c88371ed..a8d534ba 100644
--- a/render/particle/collisionParticle.go
+++ b/render/particle/collisionParticle.go
@@ -3,7 +3,7 @@ package particle
import (
"image/draw"
- "github.com/oakmound/oak/v3/collision"
+ "github.com/oakmound/oak/v4/collision"
)
// A CollisionParticle is a wrapper around other particles that also
diff --git a/render/particle/collision_test.go b/render/particle/collision_test.go
index 40d2dba9..a1b3a71a 100644
--- a/render/particle/collision_test.go
+++ b/render/particle/collision_test.go
@@ -4,7 +4,7 @@ import (
"image"
"testing"
- "github.com/oakmound/oak/v3/collision"
+ "github.com/oakmound/oak/v4/collision"
)
func TestCollisionParticle(t *testing.T) {
diff --git a/render/particle/collisonGenerator.go b/render/particle/collisonGenerator.go
index f4d269f5..4e2d4460 100644
--- a/render/particle/collisonGenerator.go
+++ b/render/particle/collisonGenerator.go
@@ -1,8 +1,8 @@
package particle
import (
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/event"
)
// A CollisionGenerator generates collision particles
diff --git a/render/particle/colorGenerator.go b/render/particle/colorGenerator.go
index 4b22ceeb..a7478ba4 100644
--- a/render/particle/colorGenerator.go
+++ b/render/particle/colorGenerator.go
@@ -3,10 +3,10 @@ package particle
import (
"image/color"
- "github.com/oakmound/oak/v3/alg"
- "github.com/oakmound/oak/v3/shape"
+ "github.com/oakmound/oak/v4/alg"
+ "github.com/oakmound/oak/v4/shape"
- "github.com/oakmound/oak/v3/alg/range/intrange"
+ "github.com/oakmound/oak/v4/alg/span"
)
// A ColorGenerator generates ColorParticles
@@ -15,8 +15,8 @@ type ColorGenerator struct {
StartColor, StartColorRand color.Color
EndColor, EndColorRand color.Color
// The size, in pixel radius, of spawned particles
- Size intrange.Range
- EndSize intrange.Range
+ Size span.Span[int]
+ EndSize span.Span[int]
//
// Some sort of particle type, for rendering triangles or squares or circles...
Shape shape.Shape
@@ -40,8 +40,8 @@ func (cg *ColorGenerator) setDefaults() {
cg.StartColorRand = color.RGBA{0, 0, 0, 0}
cg.EndColor = color.RGBA{0, 0, 0, 0}
cg.EndColorRand = color.RGBA{0, 0, 0, 0}
- cg.Size = intrange.NewConstant(1)
- cg.EndSize = intrange.NewConstant(1)
+ cg.Size = span.NewConstant(1)
+ cg.EndSize = span.NewConstant(1)
cg.Shape = shape.Square
}
@@ -49,7 +49,7 @@ func (cg *ColorGenerator) setDefaults() {
func (cg *ColorGenerator) Generate(layer int) *Source {
// Convert rotation from degrees to radians
if cg.Rotation != nil {
- cg.Rotation = cg.Rotation.Mult(alg.DegToRad)
+ cg.Rotation = cg.Rotation.MulSpan(alg.DegToRad)
}
return NewDefaultSource(cg, layer)
}
@@ -66,7 +66,7 @@ func (cg *ColorGenerator) GenerateParticle(bp *baseParticle) Particle {
}
// GetParticleSize on a color generator returns that the particles
-// are per-particle specificially sized
+// are per-particle specifically sized
func (cg *ColorGenerator) GetParticleSize() (w float64, h float64, perParticle bool) {
return 0, 0, true
}
@@ -92,12 +92,12 @@ func (cg *ColorGenerator) SetEndColor(ec, ecr color.Color) {
// A Sizeable is a generator that can have some size set to it
type Sizeable interface {
- SetSize(i intrange.Range)
- SetEndSize(i intrange.Range)
+ SetSize(i span.Span[int])
+ SetEndSize(i span.Span[int])
}
// Size is an option to set a Sizeable size
-func Size(i intrange.Range) func(Generator) {
+func Size(i span.Span[int]) func(Generator) {
return func(g Generator) {
if g2, ok := g.(Sizeable); ok {
g2.SetSize(i)
@@ -106,7 +106,7 @@ func Size(i intrange.Range) func(Generator) {
}
// EndSize sets the end size of a Sizeable
-func EndSize(i intrange.Range) func(Generator) {
+func EndSize(i span.Span[int]) func(Generator) {
return func(g Generator) {
if g2, ok := g.(Sizeable); ok {
g2.SetEndSize(i)
@@ -115,12 +115,12 @@ func EndSize(i intrange.Range) func(Generator) {
}
// SetSize satisfies Sizeable
-func (cg *ColorGenerator) SetSize(i intrange.Range) {
+func (cg *ColorGenerator) SetSize(i span.Span[int]) {
cg.Size = i
}
// SetEndSize stasfies Sizeable
-func (cg *ColorGenerator) SetEndSize(i intrange.Range) {
+func (cg *ColorGenerator) SetEndSize(i span.Span[int]) {
cg.EndSize = i
}
diff --git a/render/particle/colorParticle.go b/render/particle/colorParticle.go
index 2c6e7a68..6fcb7220 100644
--- a/render/particle/colorParticle.go
+++ b/render/particle/colorParticle.go
@@ -4,8 +4,8 @@ import (
"image/color"
"image/draw"
- "github.com/oakmound/oak/v3/physics"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/physics"
+ "github.com/oakmound/oak/v4/render"
)
// A ColorParticle is a particle with a defined color and size
diff --git a/render/particle/color_test.go b/render/particle/color_test.go
index 0ad2dcde..9c3275f5 100644
--- a/render/particle/color_test.go
+++ b/render/particle/color_test.go
@@ -5,18 +5,17 @@ import (
"image/color"
"testing"
- "github.com/oakmound/oak/v3/alg/range/floatrange"
- "github.com/oakmound/oak/v3/alg/range/intrange"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/shape"
+ "github.com/oakmound/oak/v4/alg/span"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/shape"
)
func TestColorParticle(t *testing.T) {
g := NewColorGenerator(
- Rotation(floatrange.NewConstant(1)),
+ Rotation(span.NewConstant(1.0)),
Color(color.RGBA{255, 0, 0, 255}, color.RGBA{255, 0, 0, 255}, color.RGBA{255, 0, 0, 255}, color.RGBA{255, 0, 0, 255}),
- Size(intrange.NewConstant(5)),
- EndSize(intrange.NewConstant(10)),
+ Size(span.NewConstant(5)),
+ EndSize(span.NewConstant(10)),
Shape(shape.Heart),
)
src := g.Generate(0)
diff --git a/render/particle/generator.go b/render/particle/generator.go
index 53299897..ffb84c57 100644
--- a/render/particle/generator.go
+++ b/render/particle/generator.go
@@ -1,14 +1,15 @@
package particle
import (
- "github.com/oakmound/oak/v3/alg/range/floatrange"
- "github.com/oakmound/oak/v3/alg/range/intrange"
- "github.com/oakmound/oak/v3/physics"
+ "math"
+
+ "github.com/oakmound/oak/v4/alg/span"
+ "github.com/oakmound/oak/v4/physics"
)
var (
// Inf represents Infinite duration
- Inf = intrange.NewInfinite()
+ Inf = span.NewConstant(math.MaxInt32)
)
// A Generator holds settings for generating particles
@@ -33,14 +34,14 @@ type BaseGenerator struct {
// to something along the lines of 'new per 30 frames',
// or allow low fractional values to be meaningful,
// so that more fine-tuned particle generation speeds are possible.
- NewPerFrame floatrange.Range
+ NewPerFrame span.Span[float64]
// The number of frames each particle should persist
// before being removed.
- LifeSpan floatrange.Range
+ LifeSpan span.Span[float64]
// 0 - between quadrant 1 and 4
// 90 - between quadrant 2 and 1
- Angle floatrange.Range
- Speed floatrange.Range
+ Angle span.Span[float64]
+ Speed span.Span[float64]
Spread physics.Vector
// Duration in milliseconds for the particle source.
// After this many milliseconds have passed, it will
@@ -48,9 +49,9 @@ type BaseGenerator struct {
// not be removed until their individual lifespans run
// out.
// A duration of -1 represents never stopping.
- Duration intrange.Range
+ Duration span.Span[int]
// Rotational acceleration, to change angle over time
- Rotation floatrange.Range
+ Rotation span.Span[float64]
// Gravity X() and Gravity Y() represent particle acceleration per frame.
Gravity physics.Vector
SpeedDecay physics.Vector
@@ -67,10 +68,10 @@ func (bg *BaseGenerator) GetBaseGenerator() *BaseGenerator {
func (bg *BaseGenerator) setDefaults() {
*bg = BaseGenerator{
Vector: physics.NewVector(0, 0),
- NewPerFrame: floatrange.NewConstant(1),
- LifeSpan: floatrange.NewConstant(60),
- Angle: floatrange.NewConstant(0),
- Speed: floatrange.NewConstant(1),
+ NewPerFrame: span.NewConstant(1.0),
+ LifeSpan: span.NewConstant(60.0),
+ Angle: span.NewConstant(0.0),
+ Speed: span.NewConstant(1.0),
Spread: physics.NewVector(0, 0),
Duration: Inf,
Rotation: nil,
diff --git a/render/particle/gradientGenerator.go b/render/particle/gradientGenerator.go
index 405e3cac..eb221518 100644
--- a/render/particle/gradientGenerator.go
+++ b/render/particle/gradientGenerator.go
@@ -3,8 +3,8 @@ package particle
import (
"image/color"
- "github.com/oakmound/oak/v3/alg"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/alg"
+ "github.com/oakmound/oak/v4/render"
)
// A GradientGenerator is a ColorGenerator with a patterned gradient
@@ -43,7 +43,7 @@ func (gg *GradientGenerator) setDefaults() {
func (gg *GradientGenerator) Generate(layer int) *Source {
// Convert rotation from degrees to radians
if gg.Rotation != nil {
- gg.Rotation = gg.Rotation.Mult(alg.DegToRad)
+ gg.Rotation = gg.Rotation.MulSpan(alg.DegToRad)
}
return NewDefaultSource(gg, layer)
}
diff --git a/render/particle/gradientParticle.go b/render/particle/gradientParticle.go
index 47b1fd5f..a1e51a25 100644
--- a/render/particle/gradientParticle.go
+++ b/render/particle/gradientParticle.go
@@ -4,7 +4,7 @@ import (
"image/color"
"image/draw"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/render"
)
// A GradientParticle has a gradient from one color to another
diff --git a/render/particle/gradient_test.go b/render/particle/gradient_test.go
index 244d9aa3..1fd70431 100644
--- a/render/particle/gradient_test.go
+++ b/render/particle/gradient_test.go
@@ -5,31 +5,30 @@ import (
"image/color"
"testing"
- "github.com/oakmound/oak/v3/alg/range/floatrange"
- "github.com/oakmound/oak/v3/alg/range/intrange"
- "github.com/oakmound/oak/v3/physics"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/shape"
+ "github.com/oakmound/oak/v4/alg/span"
+ "github.com/oakmound/oak/v4/physics"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/shape"
)
func TestGradientParticle(t *testing.T) {
g := NewGradientGenerator(
- Rotation(floatrange.NewConstant(1)),
+ Rotation(span.NewConstant(1.0)),
Color(color.RGBA{255, 0, 0, 255}, color.RGBA{255, 0, 0, 255}, color.RGBA{255, 0, 0, 255}, color.RGBA{255, 0, 0, 255}),
Color2(color.RGBA{255, 0, 0, 255}, color.RGBA{255, 0, 0, 255}, color.RGBA{255, 0, 0, 255}, color.RGBA{255, 0, 0, 255}),
- Size(intrange.NewConstant(5)),
- EndSize(intrange.NewConstant(10)),
+ Size(span.NewConstant(5)),
+ EndSize(span.NewConstant(10)),
Shape(shape.Heart),
Progress(render.HorizontalProgress),
And(
- NewPerFrame(floatrange.NewConstant(20)),
+ NewPerFrame(span.NewConstant(20.0)),
),
Pos(20, 20),
- LifeSpan(floatrange.NewConstant(10)),
- Angle(floatrange.NewConstant(0)),
- Speed(floatrange.NewConstant(0)),
+ LifeSpan(span.NewConstant(10.0)),
+ Angle(span.NewConstant(0.0)),
+ Speed(span.NewConstant(0.0)),
Spread(10, 10),
- Duration(intrange.NewConstant(10)),
+ Duration(span.NewConstant(10)),
Gravity(10, 10),
SpeedDecay(1, 1),
End(func(_ Particle) {}),
diff --git a/render/particle/math.go b/render/particle/math.go
index 80fc2be6..d890368d 100644
--- a/render/particle/math.go
+++ b/render/particle/math.go
@@ -5,7 +5,7 @@ import (
"math"
"math/rand"
- "github.com/oakmound/oak/v3/alg"
+ "github.com/oakmound/oak/v4/alg"
)
// floatFromSpread returns a random value between
diff --git a/render/particle/options.go b/render/particle/options.go
index 3b53f59e..d822f134 100644
--- a/render/particle/options.go
+++ b/render/particle/options.go
@@ -1,10 +1,11 @@
package particle
import (
- "github.com/oakmound/oak/v3/alg"
- "github.com/oakmound/oak/v3/alg/range/floatrange"
- "github.com/oakmound/oak/v3/alg/range/intrange"
- "github.com/oakmound/oak/v3/physics"
+ "math"
+
+ "github.com/oakmound/oak/v4/alg"
+ "github.com/oakmound/oak/v4/alg/span"
+ "github.com/oakmound/oak/v4/physics"
)
// And chains together particle options into a single option
@@ -18,7 +19,7 @@ func And(as ...func(Generator)) func(Generator) {
}
// NewPerFrame sets how many particles should be produced per frame
-func NewPerFrame(npf floatrange.Range) func(Generator) {
+func NewPerFrame(npf span.Span[float64]) func(Generator) {
return func(g Generator) {
g.GetBaseGenerator().NewPerFrame = npf
}
@@ -32,7 +33,7 @@ func Pos(x, y float64) func(Generator) {
}
// LifeSpan sets how long a particle should last before dying
-func LifeSpan(ls floatrange.Range) func(Generator) {
+func LifeSpan(ls span.Span[float64]) func(Generator) {
return func(g Generator) {
g.GetBaseGenerator().LifeSpan = ls
}
@@ -41,19 +42,19 @@ func LifeSpan(ls floatrange.Range) func(Generator) {
// InfiniteLifeSpan will set particles to never die over time.
func InfiniteLifeSpan() func(Generator) {
return func(g Generator) {
- g.GetBaseGenerator().LifeSpan = floatrange.NewInfinite()
+ g.GetBaseGenerator().LifeSpan = span.NewConstant(math.MaxFloat64)
}
}
// Angle sets the initial angle of a particle in degrees
-func Angle(a floatrange.Range) func(Generator) {
+func Angle(a span.Span[float64]) func(Generator) {
return func(g Generator) {
- g.GetBaseGenerator().Angle = a.Mult(alg.DegToRad)
+ g.GetBaseGenerator().Angle = a.MulSpan(alg.DegToRad)
}
}
// Speed sets the initial speed of a particle
-func Speed(s floatrange.Range) func(Generator) {
+func Speed(s span.Span[float64]) func(Generator) {
return func(g Generator) {
g.GetBaseGenerator().Speed = s
}
@@ -67,14 +68,14 @@ func Spread(x, y float64) func(Generator) {
}
// Duration sets how long a generator should produce particles for
-func Duration(i intrange.Range) func(Generator) {
+func Duration(i span.Span[int]) func(Generator) {
return func(g Generator) {
g.GetBaseGenerator().Duration = i
}
}
// Rotation rotates particles by a variable amount per frame
-func Rotation(a floatrange.Range) func(Generator) {
+func Rotation(a span.Span[float64]) func(Generator) {
return func(g Generator) {
g.GetBaseGenerator().Rotation = a
}
diff --git a/render/particle/particle.go b/render/particle/particle.go
index 91f6c571..a089026c 100644
--- a/render/particle/particle.go
+++ b/render/particle/particle.go
@@ -5,8 +5,8 @@ package particle
import (
"image/draw"
- "github.com/oakmound/oak/v3/physics"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/physics"
+ "github.com/oakmound/oak/v4/render"
)
// A Particle is a renderable that is spawned by a generator, usually very fast,
diff --git a/render/particle/particle_test.go b/render/particle/particle_test.go
index 748f9fbc..578b2033 100644
--- a/render/particle/particle_test.go
+++ b/render/particle/particle_test.go
@@ -3,7 +3,7 @@ package particle
import (
"testing"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/render"
)
func TestParticle(t *testing.T) {
diff --git a/render/particle/shape.go b/render/particle/shape.go
index 3121d705..e1317275 100644
--- a/render/particle/shape.go
+++ b/render/particle/shape.go
@@ -1,6 +1,6 @@
package particle
-import "github.com/oakmound/oak/v3/shape"
+import "github.com/oakmound/oak/v4/shape"
// Shapeable generators can have the Shape option called on them
type Shapeable interface {
diff --git a/render/particle/source.go b/render/particle/source.go
index 9a3c089e..601e6cd7 100644
--- a/render/particle/source.go
+++ b/render/particle/source.go
@@ -4,9 +4,9 @@ import (
"math"
"time"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/physics"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/physics"
+ "github.com/oakmound/oak/v4/render"
)
const (
diff --git a/render/particle/source_test.go b/render/particle/source_test.go
index 728d68d5..01f1fc13 100644
--- a/render/particle/source_test.go
+++ b/render/particle/source_test.go
@@ -4,35 +4,34 @@ import (
"image/color"
"testing"
- "github.com/oakmound/oak/v3/alg/range/floatrange"
- "github.com/oakmound/oak/v3/alg/range/intrange"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/physics"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/shape"
+ "github.com/oakmound/oak/v4/alg/span"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/physics"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/shape"
)
func TestSource(t *testing.T) {
g := NewGradientGenerator(
- Rotation(floatrange.NewConstant(1)),
+ Rotation(span.NewConstant(1.0)),
Color(color.RGBA{255, 0, 0, 255}, color.RGBA{255, 0, 0, 255},
color.RGBA{255, 0, 0, 255}, color.RGBA{255, 0, 0, 255}),
Color2(color.RGBA{255, 0, 0, 255}, color.RGBA{255, 0, 0, 255},
color.RGBA{255, 0, 0, 255}, color.RGBA{255, 0, 0, 255}),
- Size(intrange.NewConstant(5)),
- EndSize(intrange.NewConstant(10)),
+ Size(span.NewConstant(5)),
+ EndSize(span.NewConstant(10)),
Shape(shape.Heart),
Progress(render.HorizontalProgress),
And(
- NewPerFrame(floatrange.NewConstant(200)),
+ NewPerFrame(span.NewConstant(200.0)),
),
Pos(20, 20),
- LifeSpan(floatrange.NewConstant(10)),
+ LifeSpan(span.NewConstant(10.0)),
Limit(2047),
- Angle(floatrange.NewConstant(0)),
- Speed(floatrange.NewConstant(0)),
+ Angle(span.NewConstant(0.0)),
+ Speed(span.NewConstant(0.0)),
Spread(10, 10),
- Duration(intrange.NewConstant(10)),
+ Duration(span.NewConstant(10)),
Gravity(10, 10),
SpeedDecay(1, 1),
End(func(_ Particle) {}),
diff --git a/render/particle/spriteGenerator.go b/render/particle/spriteGenerator.go
index 111b6102..b91856b9 100644
--- a/render/particle/spriteGenerator.go
+++ b/render/particle/spriteGenerator.go
@@ -1,16 +1,16 @@
package particle
import (
- "github.com/oakmound/oak/v3/alg/range/floatrange"
+ "github.com/oakmound/oak/v4/alg/span"
- "github.com/oakmound/oak/v3/alg"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/alg"
+ "github.com/oakmound/oak/v4/render"
)
// A SpriteGenerator generate SpriteParticles
type SpriteGenerator struct {
BaseGenerator
- SpriteRotation floatrange.Range
+ SpriteRotation span.Span[float64]
Base *render.Sprite
}
@@ -28,14 +28,14 @@ func NewSpriteGenerator(options ...func(Generator)) Generator {
func (sg *SpriteGenerator) setDefaults() {
sg.BaseGenerator.setDefaults()
- sg.SpriteRotation = floatrange.NewConstant(0)
+ sg.SpriteRotation = span.NewConstant(0.0)
}
// Generate creates a source using this generator
func (sg *SpriteGenerator) Generate(layer int) *Source {
// Convert rotation from degrees to radians
if sg.Rotation != nil {
- sg.Rotation = sg.Rotation.Mult(alg.DegToRad)
+ sg.Rotation = sg.Rotation.MulSpan(alg.DegToRad)
}
return NewDefaultSource(sg, layer)
}
@@ -51,7 +51,7 @@ func (sg *SpriteGenerator) GenerateParticle(bp *baseParticle) Particle {
// A Sprited can have a sprite set to it
type Sprited interface {
SetSprite(*render.Sprite)
- SetSpriteRotation(f floatrange.Range)
+ SetSpriteRotation(f span.Span[float64])
}
// Sprite sets a Sprited's sprite
@@ -68,14 +68,14 @@ func (sg *SpriteGenerator) SetSprite(s *render.Sprite) {
}
// SpriteRotation sets a Sprited's rotation
-func SpriteRotation(f floatrange.Range) func(Generator) {
+func SpriteRotation(f span.Span[float64]) func(Generator) {
return func(g Generator) {
g.(Sprited).SetSpriteRotation(f)
}
}
// SetSpriteRotation satisfied Sprited for SpriteGenerators
-func (sg *SpriteGenerator) SetSpriteRotation(f floatrange.Range) {
+func (sg *SpriteGenerator) SetSpriteRotation(f span.Span[float64]) {
sg.SpriteRotation = f
}
diff --git a/render/particle/spriteParticle.go b/render/particle/spriteParticle.go
index 317ba791..84682f53 100644
--- a/render/particle/spriteParticle.go
+++ b/render/particle/spriteParticle.go
@@ -3,8 +3,8 @@ package particle
import (
"image/draw"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/render/mod"
)
// A SpriteParticle is a particle that has an amount of sprite rotation
diff --git a/render/particle/sprite_test.go b/render/particle/sprite_test.go
index e03940d5..bf6e67ed 100644
--- a/render/particle/sprite_test.go
+++ b/render/particle/sprite_test.go
@@ -5,17 +5,16 @@ import (
"image/color"
"testing"
- "github.com/oakmound/oak/v3/alg/range/floatrange"
-
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/alg/span"
+ "github.com/oakmound/oak/v4/render"
)
func TestSpriteParticle(t *testing.T) {
s := render.NewColorBox(10, 10, color.RGBA{255, 0, 0, 255})
g := NewSpriteGenerator(
Sprite(s),
- Rotation(floatrange.NewConstant(1)),
- SpriteRotation(floatrange.NewConstant(1)),
+ Rotation(span.NewConstant(1.0)),
+ SpriteRotation(span.NewConstant(1.0)),
)
src := g.Generate(0)
src.addParticles()
diff --git a/render/polygon.go b/render/polygon.go
index c2086a18..e1908da4 100644
--- a/render/polygon.go
+++ b/render/polygon.go
@@ -5,8 +5,8 @@ import (
"image/color"
"math"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/alg/range/colorrange"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/span"
)
// A Polygon is a renderable that is represented by a set of in order points
@@ -45,7 +45,7 @@ func (pg *Polygon) GetThickOutline(c color.Color, thickness int) *CompositeM {
// GetGradientOutline returns a set of lines of the given color along this polygon's outline,
// at the given thickness, ranging from c1 to c2 in color
func (pg *Polygon) GetGradientOutline(c1, c2 color.Color, thickness int) *CompositeM {
- return pg.GetColoredOutline(colorrange.NewLinear(c1, c2).Percentile, thickness)
+ return pg.GetColoredOutline(span.NewLinearColor(c1, c2).Percentile, thickness)
}
// GetColoredOutline returns a set of lines of the given color along this polygon's outline
diff --git a/render/polygon_test.go b/render/polygon_test.go
index 9a705abc..7ae4709f 100644
--- a/render/polygon_test.go
+++ b/render/polygon_test.go
@@ -4,7 +4,7 @@ import (
"image/color"
"testing"
- "github.com/oakmound/oak/v3/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
)
func TestContains(t *testing.T) {
diff --git a/render/renderable.go b/render/renderable.go
index 5a56e19f..ce2c3fee 100644
--- a/render/renderable.go
+++ b/render/renderable.go
@@ -3,7 +3,7 @@ package render
import (
"image/draw"
- "github.com/oakmound/oak/v3/physics"
+ "github.com/oakmound/oak/v4/physics"
)
// A Renderable is anything which can
diff --git a/render/reverting.go b/render/reverting.go
index bafa6d68..3b38162d 100644
--- a/render/reverting.go
+++ b/render/reverting.go
@@ -1,8 +1,8 @@
package render
import (
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/render/mod"
)
// The Reverting structure lets modifications be made to a Modifiable and then
diff --git a/render/reverting_test.go b/render/reverting_test.go
index 49cae688..e8a64497 100644
--- a/render/reverting_test.go
+++ b/render/reverting_test.go
@@ -6,7 +6,7 @@ import (
"reflect"
"testing"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/render/mod"
)
var (
diff --git a/render/sequence.go b/render/sequence.go
index 0ce1afad..9fd3c5c5 100644
--- a/render/sequence.go
+++ b/render/sequence.go
@@ -5,9 +5,9 @@ import (
"image/draw"
"time"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/render/mod"
- "github.com/oakmound/oak/v3/timing"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/render/mod"
+ "github.com/oakmound/oak/v4/timing"
)
// A Sequence is a series of modifiables drawn as an animation. It is more
diff --git a/render/sequence_test.go b/render/sequence_test.go
index 45b56f29..5fe92ebf 100644
--- a/render/sequence_test.go
+++ b/render/sequence_test.go
@@ -8,8 +8,8 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/render/mod"
)
type Dummy struct {
diff --git a/render/sheet.go b/render/sheet.go
index 7818c075..c537eaad 100644
--- a/render/sheet.go
+++ b/render/sheet.go
@@ -3,7 +3,7 @@ package render
import (
"image"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/oakerr"
)
// Sheet is a 2D array of image rgbas
diff --git a/render/sheet_test.go b/render/sheet_test.go
index 5b279b21..8f9f8dcf 100644
--- a/render/sheet_test.go
+++ b/render/sheet_test.go
@@ -5,8 +5,8 @@ import (
"os"
"testing"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/fileutil"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/fileutil"
)
//go:embed testdata/assets/*
diff --git a/render/sprite.go b/render/sprite.go
index 91409abf..df041be7 100644
--- a/render/sprite.go
+++ b/render/sprite.go
@@ -5,7 +5,7 @@ import (
"image/color"
"image/draw"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/render/mod"
)
// A Sprite is a basic wrapper around image data and a point. The most basic Renderable.
diff --git a/render/sprite_test.go b/render/sprite_test.go
index d6abed87..2bb1dbfe 100644
--- a/render/sprite_test.go
+++ b/render/sprite_test.go
@@ -6,18 +6,17 @@ import (
"reflect"
"testing"
- "github.com/oakmound/oak/v3/alg/range/colorrange"
- "github.com/oakmound/oak/v3/alg/range/intrange"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/alg/span"
+ "github.com/oakmound/oak/v4/render/mod"
)
var (
// this is excessive for a lot of tests
// but it takes away some decision making
// and could reveal problems that probably aren't there
- widths = intrange.NewLinear(1, 10)
- heights = intrange.NewLinear(1, 10)
- colors = colorrange.NewLinear(color.RGBA{0, 0, 0, 0}, color.RGBA{255, 255, 255, 255})
+ widths = span.NewLinear(1, 10)
+ heights = span.NewLinear(1, 10)
+ colors = span.NewLinearColor(color.RGBA{0, 0, 0, 0}, color.RGBA{255, 255, 255, 255})
)
const (
diff --git a/render/switch.go b/render/switch.go
index e493bc8a..6a38aa53 100644
--- a/render/switch.go
+++ b/render/switch.go
@@ -5,10 +5,10 @@ import (
"image/draw"
"sync"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/oakerr"
- "github.com/oakmound/oak/v3/physics"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/oakerr"
+ "github.com/oakmound/oak/v4/physics"
+ "github.com/oakmound/oak/v4/render/mod"
)
// The Switch type is intended for use to easily swap between multiple
diff --git a/render/switch_test.go b/render/switch_test.go
index e9ff6585..ca286ab8 100644
--- a/render/switch_test.go
+++ b/render/switch_test.go
@@ -6,8 +6,8 @@ import (
"reflect"
"testing"
- "github.com/oakmound/oak/v3/physics"
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/physics"
+ "github.com/oakmound/oak/v4/render/mod"
)
func TestCompoundFuncs(t *testing.T) {
diff --git a/render/testdata/assets/fonts/seguiemj.ttf b/render/testdata/assets/fonts/seguiemj.ttf
new file mode 100644
index 00000000..77497bf7
Binary files /dev/null and b/render/testdata/assets/fonts/seguiemj.ttf differ
diff --git a/render/text.go b/render/text.go
index 9b3a00d8..88131260 100644
--- a/render/text.go
+++ b/render/text.go
@@ -5,7 +5,7 @@ import (
"image/draw"
"strconv"
- "github.com/oakmound/oak/v3/alg"
+ "github.com/oakmound/oak/v4/alg"
"golang.org/x/image/math/fixed"
)
diff --git a/scene.go b/scene.go
index a59310cf..a6ce44e8 100644
--- a/scene.go
+++ b/scene.go
@@ -3,11 +3,11 @@ package oak
import (
"time"
- "github.com/oakmound/oak/v3/scene"
- "github.com/oakmound/oak/v3/timing"
+ "github.com/oakmound/oak/v4/scene"
+ "github.com/oakmound/oak/v4/timing"
)
-// AddScene is shorthand for c.SceneMap.AddScene
+// AddScene is shorthand for w.SceneMap.AddScene
func (w *Window) AddScene(name string, s scene.Scene) error {
return w.SceneMap.AddScene(name, s)
}
diff --git a/scene/context.go b/scene/context.go
index 07c394d7..ef337127 100644
--- a/scene/context.go
+++ b/scene/context.go
@@ -3,31 +3,37 @@ package scene
import (
"context"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/render"
)
// A Context contains all transient engine components used in a scene, including
// the draw stack, event bus, known event callers, collision trees, keyboard state,
// and a reference to the OS window itself. When a scene ends, modifications made
// to these structures will be reset, excluding window modifications.
-// TODO oak v4: consider embedding these system objects on the context to change
-// ctx.DrawStack.Draw to ctx.Draw and ctx.Handler.Bind to ctx.Bind
type Context struct {
// This context will be canceled when the scene ends
context.Context
- *event.CallerMap
- event.Handler
PreviousScene string
SceneInput interface{}
Window Window
+ *event.CallerMap
+ event.Handler
*render.DrawStack
+ *key.State
MouseTree *collision.Tree
CollisionTree *collision.Tree
- KeyState *key.State
+}
+
+// DoEachFrame is a helper method to call a function on each frame for the duration of this scene.
+func (ctx *Context) DoEachFrame(f func()) {
+ event.GlobalBind(ctx, event.Enter, func(_ event.EnterPayload) event.Response {
+ f()
+ return 0
+ })
}
diff --git a/scene/context_desktop.go b/scene/context_desktop.go
index 6f624c35..7777bdb6 100644
--- a/scene/context_desktop.go
+++ b/scene/context_desktop.go
@@ -3,6 +3,6 @@
package scene
-import "github.com/oakmound/oak/v3/window"
+import "github.com/oakmound/oak/v4/window"
type Window = window.Window
diff --git a/scene/context_other.go b/scene/context_other.go
index 2572c27a..153661f3 100644
--- a/scene/context_other.go
+++ b/scene/context_other.go
@@ -3,6 +3,6 @@
package scene
-import "github.com/oakmound/oak/v3/window"
+import "github.com/oakmound/oak/v4/window"
type Window = window.App
diff --git a/scene/delay.go b/scene/delay.go
index 524418c8..fd6682ea 100644
--- a/scene/delay.go
+++ b/scene/delay.go
@@ -4,7 +4,7 @@ import (
"context"
"time"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/render"
)
// DoAfter will execute the given function after some duration. When the scene
diff --git a/scene/delay_test.go b/scene/delay_test.go
index 96b48aed..518e9326 100644
--- a/scene/delay_test.go
+++ b/scene/delay_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/render"
+ "github.com/oakmound/oak/v4/render"
)
func TestDoAfterCancels(t *testing.T) {
diff --git a/scene/example_test.go b/scene/example_test.go
index a75894fc..dbeeb830 100644
--- a/scene/example_test.go
+++ b/scene/example_test.go
@@ -3,7 +3,7 @@ package scene_test
import (
"fmt"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/scene"
)
func ExampleMap_GetCurrent() {
diff --git a/scene/map.go b/scene/map.go
index deaa77e8..346f6463 100644
--- a/scene/map.go
+++ b/scene/map.go
@@ -3,7 +3,7 @@ package scene
import (
"sync"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/oakerr"
)
// A Map lets scenes be accessed via associated names.
@@ -41,7 +41,6 @@ func (m *Map) GetCurrent() (Scene, bool) {
// conflict with an existing name in the map, and then adds it to the map.
// If a conflict occurs, the scene will not be overwritten.
// Checks if the Scene's start is nil, sets to noop if so.
-// Checks if the Scene's loop is nil, sets to infinite if so.
// Checks if the Scene's end is nil, sets to loop to this scene if so.
func (m *Map) AddScene(name string, s Scene) error {
diff --git a/scene/map_test.go b/scene/map_test.go
index 3678c1b0..bf029400 100644
--- a/scene/map_test.go
+++ b/scene/map_test.go
@@ -5,7 +5,7 @@ import (
"image"
"testing"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/oakerr"
)
func TestMap(t *testing.T) {
diff --git a/scene/scene.go b/scene/scene.go
index 5f5cf641..93554e3f 100644
--- a/scene/scene.go
+++ b/scene/scene.go
@@ -1,12 +1,12 @@
package scene
import (
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/oakerr"
)
// A Scene is a set of functions defining what needs to happen when a scene
-// starts, loops, and ends.
+// starts and ends.
type Scene struct {
// Start is called when a scene begins, including contextual information like
// what scene came before this one and a direct reference to clean data structures
diff --git a/scene/transition.go b/scene/transition.go
index 94818c1a..03bf73a3 100644
--- a/scene/transition.go
+++ b/scene/transition.go
@@ -4,39 +4,21 @@ import (
"image"
"image/draw"
- "github.com/oakmound/oak/v3/render/mod"
-)
-
-var (
- zeroPoint = image.Point{X: 0, Y: 0}
+ "github.com/oakmound/oak/v4/render/mod"
)
// Transition functions can be set to occur at the end of a scene.
type Transition func(*image.RGBA, int) bool
-// Fade is a scene transition that fades to black at a given rate for
-// a total of 'frames' frames
-func Fade(rate float32, frames int) func(*image.RGBA, int) bool {
- rate *= -1
- return func(buf *image.RGBA, frame int) bool {
- if frame > frames {
- return false
- }
- i := float32(frame)
- mod.Brighten(rate * i)(buf)
- return true
- }
-}
-
// Zoom transitions by performing a simplistic zoom each frame towards some
-// percentange-based part of the screen.
-func Zoom(xPerc, yPerc float64, frames int, zoomRate float64) func(*image.RGBA, int) bool {
+// percentage-based part of the screen.
+func Zoom(xPerc, yPerc float64, frames int, zoomRate float64) Transition {
return func(buf *image.RGBA, frame int) bool {
if frame > frames {
return false
}
z := mod.Zoom(xPerc, yPerc, 1+zoomRate*float64(frame))
- draw.Draw(buf, buf.Bounds(), z(buf), zeroPoint, draw.Src)
+ draw.Draw(buf, buf.Bounds(), z(buf), image.ZP, draw.Src)
return true
}
}
diff --git a/scene/transition_gift.go b/scene/transition_gift.go
new file mode 100644
index 00000000..3c58631d
--- /dev/null
+++ b/scene/transition_gift.go
@@ -0,0 +1,24 @@
+//go:build !nogift
+// +build !nogift
+
+package scene
+
+import (
+ "image"
+
+ "github.com/oakmound/oak/v4/render/mod"
+)
+
+// Fade is a scene transition that fades to black at a given rate for
+// a total of 'frames' frames
+func Fade(rate float32, frames int) Transition {
+ rate *= -1
+ return func(buf *image.RGBA, frame int) bool {
+ if frame > frames {
+ return false
+ }
+ i := float32(frame)
+ mod.Brighten(rate * i)(buf)
+ return true
+ }
+}
diff --git a/sceneLoop.go b/sceneLoop.go
index 2d5e4bb1..3fc9fc74 100644
--- a/sceneLoop.go
+++ b/sceneLoop.go
@@ -3,41 +3,24 @@ package oak
import (
"context"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/dlog"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/oakerr"
- "github.com/oakmound/oak/v3/scene"
- "github.com/oakmound/oak/v3/timing"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/dlog"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/oakerr"
+ "github.com/oakmound/oak/v4/scene"
+ "github.com/oakmound/oak/v4/timing"
)
// the oak loading scene is a reserved scene
// for preloading assets
const oakLoadingScene = "oak:loading"
-func (w *Window) sceneLoop(first string, trackingInputs, batchLoad bool) {
- w.SceneMap.AddScene(oakLoadingScene, scene.Scene{
- Start: func(ctx *scene.Context) {
- if batchLoad {
- go func() {
- w.loadAssets(w.config.Assets.ImagePath, w.config.Assets.AudioPath)
- w.endLoad()
- }()
- } else {
- go w.endLoad()
- }
- },
- End: func() (string, *scene.Result) {
- return w.firstScene, &scene.Result{
- NextSceneInput: w.FirstSceneInput,
- }
- },
- })
-
+func (w *Window) sceneLoop(first string, trackingInputs bool) {
var prevScene string
result := new(scene.Result)
+ // kick start the draw loop
w.drawCh <- struct{}{}
w.drawCh <- struct{}{}
@@ -46,7 +29,7 @@ func (w *Window) sceneLoop(first string, trackingInputs, batchLoad bool) {
w.SceneMap.CurrentScene = oakLoadingScene
for {
- w.setViewport(intgeom.Point2{0, 0})
+ w.SetViewport(intgeom.Point2{0, 0})
w.RemoveViewportBounds()
dlog.Info(dlog.SceneStarting, w.SceneMap.CurrentScene)
@@ -80,13 +63,12 @@ func (w *Window) sceneLoop(first string, trackingInputs, batchLoad bool) {
MouseTree: w.MouseTree,
CollisionTree: w.CollisionTree,
Window: w,
- KeyState: &w.State,
+ State: &w.State,
})
w.transitionCh <- struct{}{}
}()
w.sceneTransition(result)
-
// Post transition, begin loading animation
w.drawCh <- struct{}{}
<-w.transitionCh
@@ -94,21 +76,19 @@ func (w *Window) sceneLoop(first string, trackingInputs, batchLoad bool) {
w.drawCh <- struct{}{}
dlog.Info(dlog.SceneLooping)
- cont := true
enterCancel := event.EnterLoop(w.eventHandler, timing.FPSToFrameDelay(w.FrameRate))
-
nextSceneOverride := ""
- for cont {
- select {
- case <-w.ParentContext.Done():
- case <-w.quitCh:
- cancel()
- return
- case nextSceneOverride = <-w.skipSceneCh:
- cont = false
- }
+ select {
+ case <-w.ParentContext.Done():
+ w.Quit()
+ cancel()
+ return
+ case <-w.quitCh:
+ cancel()
+ return
+ case nextSceneOverride = <-w.skipSceneCh:
}
cancel()
dlog.Info(dlog.SceneEnding, w.SceneMap.CurrentScene)
diff --git a/sceneLoop_test.go b/sceneLoop_test.go
index fb48c25b..89d6e8b0 100644
--- a/sceneLoop_test.go
+++ b/sceneLoop_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/scene"
)
func TestSceneLoopUnknownScene(t *testing.T) {
diff --git a/scene_test.go b/scene_test.go
index c92b7431..5b6f205d 100644
--- a/scene_test.go
+++ b/scene_test.go
@@ -1,9 +1,12 @@
package oak
import (
+ "context"
+ "errors"
"testing"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/oakerr"
+ "github.com/oakmound/oak/v4/scene"
)
func TestSceneTransition(t *testing.T) {
@@ -25,3 +28,35 @@ func TestSceneTransition(t *testing.T) {
})
c1.Init("1")
}
+
+func TestLoadingSceneClaimed(t *testing.T) {
+ c1 := NewWindow()
+ c1.AddScene(oakLoadingScene, scene.Scene{})
+ err := c1.Init("1")
+ var wantErr oakerr.ExistingElement
+ if !errors.As(err, &wantErr) {
+ t.Fatalf("expected existing element error, got %v", err)
+ }
+}
+
+func TestSceneGoTo(t *testing.T) {
+ c1 := NewWindow()
+ var cancel func()
+ c1.ParentContext, cancel = context.WithCancel(c1.ParentContext)
+ c1.AddScene("1", scene.Scene{
+ Start: func(context *scene.Context) {
+ context.Window.GoToScene("good")
+ },
+ End: func() (nextScene string, result *scene.Result) {
+ return "bad", &scene.Result{
+ Transition: scene.Fade(1, 10),
+ }
+ },
+ })
+ c1.AddScene("good", scene.Scene{
+ Start: func(ctx *scene.Context) {
+ cancel()
+ },
+ })
+ c1.Init("1")
+}
diff --git a/screenFilter.go b/screenFilter.go
index 1e56aee3..b9789432 100644
--- a/screenFilter.go
+++ b/screenFilter.go
@@ -1,28 +1,27 @@
package oak
import (
+ "image"
"image/color"
- "github.com/oakmound/oak/v3/shiny/screen"
-
- "github.com/oakmound/oak/v3/render/mod"
+ "github.com/oakmound/oak/v4/render/mod"
)
// SetPalette tells oak to conform the screen to the input color palette before drawing.
func (w *Window) SetPalette(palette color.Palette) {
- w.SetScreenFilter(mod.ConformToPallete(palette))
+ w.SetDrawFilter(mod.ConformToPalette(palette))
}
-// SetScreenFilter will filter the screen by the given modification function prior
+// SetDrawFilter will filter the screen by the given modification function prior
// to publishing the screen's rgba to be displayed.
-func (w *Window) SetScreenFilter(screenFilter mod.Filter) {
- w.prePublish = func(w *Window, tx screen.Texture) {
- screenFilter(w.winBuffers[w.bufferIdx].RGBA())
+func (w *Window) SetDrawFilter(screenFilter mod.Filter) {
+ w.prePublish = func(buf *image.RGBA) {
+ screenFilter(buf)
}
}
// ClearScreenFilter resets the draw function to no longer filter the screen before
// publishing it to the window.
func (w *Window) ClearScreenFilter() {
- w.prePublish = func(*Window, screen.Texture) {}
+ w.prePublish = func(buf *image.RGBA) {}
}
diff --git a/screenFilter_test.go b/screenFilter_test.go
new file mode 100644
index 00000000..d8d96992
--- /dev/null
+++ b/screenFilter_test.go
@@ -0,0 +1,18 @@
+package oak
+
+import (
+ "image"
+ "image/color"
+ "testing"
+)
+
+func TestScreenFilter(t *testing.T) {
+ c1 := NewWindow()
+ blackAndWhite := color.Palette{
+ color.RGBA{0, 0, 0, 255},
+ color.RGBA{255, 255, 255, 255},
+ }
+ c1.SetPalette(blackAndWhite)
+ buf := image.NewRGBA(image.Rect(0, 0, 1, 1))
+ c1.prePublish(buf)
+}
diff --git a/screenshot.go b/screenshot.go
index 32105885..f0ca1b55 100644
--- a/screenshot.go
+++ b/screenshot.go
@@ -6,8 +6,6 @@ import (
"image/draw"
"image/gif"
"time"
-
- "github.com/oakmound/oak/v3/shiny/screen"
)
// ScreenShot takes a snap shot of the window's image content.
@@ -17,9 +15,8 @@ func (w *Window) ScreenShot() *image.RGBA {
shotCh := make(chan *image.RGBA)
// We need to take the shot when the screen is not being redrawn
// We know the screen has everything drawn on it when it is published
- w.prePublish = func(w *Window, tx screen.Texture) {
+ w.prePublish = func(rgba *image.RGBA) {
// Copy the buffer
- rgba := w.winBuffers[w.bufferIdx].RGBA()
bds := rgba.Bounds()
copy := image.NewRGBA(bds)
for x := bds.Min.X; x < bds.Max.X; x++ {
@@ -39,9 +36,8 @@ func (w *Window) gifShot() *image.Paletted {
shotCh := make(chan *image.Paletted)
// We need to take the shot when the screen is not being redrawn
// We know the screen has everything drawn on it when it is published
- w.prePublish = func(w *Window, tx screen.Texture) {
+ w.prePublish = func(rgba *image.RGBA) {
// Copy the buffer
- rgba := w.winBuffers[w.bufferIdx].RGBA()
bds := rgba.Bounds()
copy := image.NewPaletted(bds, palette.Plan9)
draw.Draw(copy, bds, rgba, zeroPoint, draw.Src)
diff --git a/screenshot_test.go b/screenshot_test.go
index 7c38ae9d..412c6b08 100644
--- a/screenshot_test.go
+++ b/screenshot_test.go
@@ -7,7 +7,7 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/scene"
)
func blankScene(t *testing.T) *Window {
diff --git a/shake/shake.go b/shake/shake.go
index 9dad016c..b03b89e0 100644
--- a/shake/shake.go
+++ b/shake/shake.go
@@ -6,9 +6,10 @@ import (
"math/rand"
"time"
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/scene"
- "github.com/oakmound/oak/v3/window"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/scene"
+ "github.com/oakmound/oak/v4/window"
)
// A Shaker knows how to shake something by a (or up to a) given magnitude.
@@ -25,7 +26,7 @@ type Shaker struct {
}
var (
- // DefaultShaker is the global default shaker, used when ShakeScreen is called.
+ // DefaultShaker is the global default shaker, used when shake.Screen or shake.Shake are called.
DefaultShaker = &Shaker{
Random: false,
Magnitude: floatgeom.Point2{3.0, 3.0},
@@ -111,7 +112,7 @@ type screenToPoser struct {
}
func (stp screenToPoser) ShiftPos(x, y float64) {
- stp.ShiftScreen(int(x), int(y))
+ stp.ShiftViewport(intgeom.Point2{int(x), int(y)})
}
// Screen shakes the screen that the context controls for the given duration.
diff --git a/shape/bezier.go b/shape/bezier.go
index c7cce062..30b44c8a 100644
--- a/shape/bezier.go
+++ b/shape/bezier.go
@@ -1,8 +1,8 @@
package shape
import (
- "github.com/oakmound/oak/v3/alg/floatgeom"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/oakerr"
)
// BezierCurve will form a Bezier on the given coordinates, expected in (x,y)
@@ -64,13 +64,3 @@ type BezierPoint floatgeom.Point2
func (bp BezierPoint) Pos(float64) (x, y float64) {
return bp[0], bp[1]
}
-
-// X returns bp's value on the X axis.
-func (bp BezierPoint) X() float64 {
- return bp[0]
-}
-
-// Y returns bp's value on the Y axis.
-func (bp BezierPoint) Y() float64 {
- return bp[1]
-}
diff --git a/shape/bezier_test.go b/shape/bezier_test.go
index 6fb9cec3..d5b16a73 100644
--- a/shape/bezier_test.go
+++ b/shape/bezier_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/oakerr"
+ "github.com/oakmound/oak/v4/oakerr"
)
const randTestCt = 100
diff --git a/shape/condense.go b/shape/condense.go
index 1d55a782..f9fd8aaf 100644
--- a/shape/condense.go
+++ b/shape/condense.go
@@ -1,6 +1,6 @@
package shape
-import "github.com/oakmound/oak/v3/alg/intgeom"
+import "github.com/oakmound/oak/v4/alg/intgeom"
// Condense finds a set of rectangles that covers the shape.
// Used to return a minimal set of rectangles in an appropriate time.
diff --git a/shape/condense_test.go b/shape/condense_test.go
index 0564b2f3..25bf2101 100644
--- a/shape/condense_test.go
+++ b/shape/condense_test.go
@@ -5,7 +5,7 @@ import (
"sort"
"testing"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
func TestCondense(t *testing.T) {
diff --git a/shape/holes.go b/shape/holes.go
index 33629a28..805b5741 100644
--- a/shape/holes.go
+++ b/shape/holes.go
@@ -1,7 +1,7 @@
package shape
import (
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
// GetHoles finds sets of points which are not In this shape that
diff --git a/shape/holes_test.go b/shape/holes_test.go
index 8d36c804..9c6d6cb5 100644
--- a/shape/holes_test.go
+++ b/shape/holes_test.go
@@ -3,7 +3,7 @@ package shape
import (
"testing"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
func TestHoles(t *testing.T) {
diff --git a/shape/in.go b/shape/in.go
index caee4df8..f5658f53 100644
--- a/shape/in.go
+++ b/shape/in.go
@@ -3,7 +3,7 @@ package shape
import (
"math"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
// In functions return whether the given coordinate lies
diff --git a/shape/outline.go b/shape/outline.go
index 4bbde964..c884b6f0 100644
--- a/shape/outline.go
+++ b/shape/outline.go
@@ -4,7 +4,7 @@ import (
"errors"
"math"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
const (
diff --git a/shape/points.go b/shape/points.go
index bccea8e8..1e31a377 100644
--- a/shape/points.go
+++ b/shape/points.go
@@ -1,7 +1,7 @@
package shape
import (
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
// Points is a shape defined by a set of points.
diff --git a/shape/points_test.go b/shape/points_test.go
index c9afc875..b72e50d2 100644
--- a/shape/points_test.go
+++ b/shape/points_test.go
@@ -3,7 +3,7 @@ package shape
import (
"testing"
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
var (
diff --git a/shape/rect.go b/shape/rect.go
index f2110fa7..17b56e40 100644
--- a/shape/rect.go
+++ b/shape/rect.go
@@ -1,7 +1,7 @@
package shape
import (
- "github.com/oakmound/oak/v3/alg/intgeom"
+ "github.com/oakmound/oak/v4/alg/intgeom"
)
// A Rect is a function that returns a 2d boolean array
diff --git a/shape/shape.go b/shape/shape.go
index 706d5403..4c9de64b 100644
--- a/shape/shape.go
+++ b/shape/shape.go
@@ -1,6 +1,6 @@
package shape
-import "github.com/oakmound/oak/v3/alg/intgeom"
+import "github.com/oakmound/oak/v4/alg/intgeom"
// A Shape represents a rectangle of width/height size
// where for each x,y coordinate, either that value lies
diff --git a/shiny/driver/androiddriver/main.go b/shiny/driver/androiddriver/main.go
index 20b9a5d9..597ffa93 100644
--- a/shiny/driver/androiddriver/main.go
+++ b/shiny/driver/androiddriver/main.go
@@ -6,7 +6,7 @@ package androiddriver
import (
"sync"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/mobile/app"
"golang.org/x/mobile/event/lifecycle"
diff --git a/shiny/driver/androiddriver/screen.go b/shiny/driver/androiddriver/screen.go
index c14175a0..fe1bfb56 100644
--- a/shiny/driver/androiddriver/screen.go
+++ b/shiny/driver/androiddriver/screen.go
@@ -7,8 +7,8 @@ package androiddriver
import (
"image"
- "github.com/oakmound/oak/v3/shiny/driver/internal/event"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/event"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/image/draw"
"golang.org/x/mobile/app"
"golang.org/x/mobile/event/size"
diff --git a/shiny/driver/androiddriver/texture.go b/shiny/driver/androiddriver/texture.go
index 10ecc4d9..6056eae0 100644
--- a/shiny/driver/androiddriver/texture.go
+++ b/shiny/driver/androiddriver/texture.go
@@ -8,7 +8,7 @@ import (
"image/color"
"image/draw"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
type textureImpl struct {
diff --git a/shiny/driver/driver.go b/shiny/driver/driver.go
index f4ac6122..7b74b4a7 100644
--- a/shiny/driver/driver.go
+++ b/shiny/driver/driver.go
@@ -11,7 +11,7 @@ package driver
// or OpenGL library.
import (
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
// Main is called by the program's main function to run the graphical
@@ -23,8 +23,3 @@ import (
func Main(f func(screen.Screen)) {
main(f)
}
-
-// MonitorSize reports the size in pixels of the primary monitor.
-func MonitorSize() (width int, height int) {
- return monitorSize()
-}
diff --git a/shiny/driver/driver_android.go b/shiny/driver/driver_android.go
index 706e0f88..2431fe9d 100644
--- a/shiny/driver/driver_android.go
+++ b/shiny/driver/driver_android.go
@@ -10,17 +10,12 @@
package driver
import (
- "github.com/oakmound/oak/v3/shiny/driver/androiddriver"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/androiddriver"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
func main(f func(screen.Screen)) {
androiddriver.Main(f)
}
-func monitorSize() (int, int) {
- // GetSystemMetrics syscall
- return 0, 0
-}
-
type Window = androiddriver.Screen
diff --git a/shiny/driver/driver_fallback.go b/shiny/driver/driver_fallback.go
index d44ed868..c9462f59 100644
--- a/shiny/driver/driver_fallback.go
+++ b/shiny/driver/driver_fallback.go
@@ -10,16 +10,12 @@ package driver
import (
"errors"
- "github.com/oakmound/oak/v3/shiny/driver/internal/errscreen"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/errscreen"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
func main(f func(screen.Screen)) {
f(errscreen.Stub(errors.New("no driver for accessing a screen")))
}
-func monitorSize() (int, int) {
- return 0, 0
-}
-
-type Window = struct{}
\ No newline at end of file
+type Window = struct{}
diff --git a/shiny/driver/driver_js.go b/shiny/driver/driver_js.go
index 2e661e61..1f4808a4 100644
--- a/shiny/driver/driver_js.go
+++ b/shiny/driver/driver_js.go
@@ -4,16 +4,12 @@
package driver
import (
- "github.com/oakmound/oak/v3/shiny/driver/jsdriver"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/jsdriver"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
func main(f func(screen.Screen)) {
jsdriver.Main(f)
}
-func monitorSize() (int, int) {
- return 0, 0
-}
-
type Window = jsdriver.Window
diff --git a/shiny/driver/driver_noop.go b/shiny/driver/driver_noop.go
index 1d5d1e4a..1a5cf8c1 100644
--- a/shiny/driver/driver_noop.go
+++ b/shiny/driver/driver_noop.go
@@ -4,16 +4,12 @@
package driver
import (
- "github.com/oakmound/oak/v3/shiny/driver/noop"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/noop"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
func main(f func(screen.Screen)) {
noop.Main(f)
}
-func monitorSize() (int, int) {
- return 0, 0
-}
-
type Window = noop.Window
diff --git a/shiny/driver/driver_windows.go b/shiny/driver/driver_windows.go
index 19d1ad27..6c855053 100644
--- a/shiny/driver/driver_windows.go
+++ b/shiny/driver/driver_windows.go
@@ -8,17 +8,12 @@
package driver
import (
- "github.com/oakmound/oak/v3/shiny/driver/windriver"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/windriver"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
func main(f func(screen.Screen)) {
windriver.Main(f)
}
-func monitorSize() (int, int) {
- // GetSystemMetrics syscall
- return 0, 0
-}
-
type Window = windriver.Window
diff --git a/shiny/driver/driver_x11.go b/shiny/driver/driver_x11.go
index 87320635..cfdad0b2 100644
--- a/shiny/driver/driver_x11.go
+++ b/shiny/driver/driver_x11.go
@@ -2,22 +2,19 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ((linux && !android) || dragonfly || openbsd) && !nooswindow
// +build linux,!android dragonfly openbsd
// +build !nooswindow
package driver
import (
- "github.com/oakmound/oak/v3/shiny/driver/x11driver"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/x11driver"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
func main(f func(screen.Screen)) {
x11driver.Main(f)
}
-func monitorSize() (int, int) {
- return 0, 0
-}
-
type Window = x11driver.Window
diff --git a/shiny/driver/internal/drawer/drawer.go b/shiny/driver/internal/drawer/drawer.go
index c9bd4d21..75094b5d 100644
--- a/shiny/driver/internal/drawer/drawer.go
+++ b/shiny/driver/internal/drawer/drawer.go
@@ -9,7 +9,7 @@ import (
"image"
"image/draw"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/image/math/f64"
)
diff --git a/shiny/driver/internal/errscreen/errscreen.go b/shiny/driver/internal/errscreen/errscreen.go
index 333a4835..612de6ea 100644
--- a/shiny/driver/internal/errscreen/errscreen.go
+++ b/shiny/driver/internal/errscreen/errscreen.go
@@ -8,7 +8,7 @@ package errscreen
import (
"image"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
// Stub returns a Screen whose methods all return the given error.
diff --git a/shiny/driver/internal/win32/win32.go b/shiny/driver/internal/win32/win32.go
index 8cd3dc7a..ebf1ffb3 100644
--- a/shiny/driver/internal/win32/win32.go
+++ b/shiny/driver/internal/win32/win32.go
@@ -20,7 +20,7 @@ import (
"syscall"
"unsafe"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/mobile/event/key"
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/mouse"
diff --git a/shiny/driver/jsdriver/screen.go b/shiny/driver/jsdriver/screen.go
index ba082f56..70472f30 100644
--- a/shiny/driver/jsdriver/screen.go
+++ b/shiny/driver/jsdriver/screen.go
@@ -9,7 +9,7 @@ import (
"image"
"syscall/js"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/mobile/event/key"
"golang.org/x/mobile/event/mouse"
)
diff --git a/shiny/driver/jsdriver/texture.go b/shiny/driver/jsdriver/texture.go
index 2dc94061..c9e8f801 100644
--- a/shiny/driver/jsdriver/texture.go
+++ b/shiny/driver/jsdriver/texture.go
@@ -8,7 +8,7 @@ import (
"image/color"
"image/draw"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
type textureImpl struct {
diff --git a/shiny/driver/jsdriver/window.go b/shiny/driver/jsdriver/window.go
index 78389d8a..c6af3cd0 100644
--- a/shiny/driver/jsdriver/window.go
+++ b/shiny/driver/jsdriver/window.go
@@ -8,8 +8,8 @@ import (
"image/draw"
"syscall/js"
- "github.com/oakmound/oak/v3/shiny/driver/internal/event"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/event"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/mobile/event/key"
"golang.org/x/mobile/event/mouse"
)
diff --git a/shiny/driver/mtldriver/internal/appkit/appkit.go b/shiny/driver/mtldriver/internal/appkit/appkit.go
index 4a408bab..93515791 100644
--- a/shiny/driver/mtldriver/internal/appkit/appkit.go
+++ b/shiny/driver/mtldriver/internal/appkit/appkit.go
@@ -18,7 +18,7 @@ package appkit
import (
"unsafe"
- "github.com/oakmound/oak/v3/shiny/driver/mtldriver/internal/coreanim"
+ "github.com/oakmound/oak/v4/shiny/driver/mtldriver/internal/coreanim"
)
/*
diff --git a/shiny/driver/mtldriver/mtldriver.go b/shiny/driver/mtldriver/mtldriver.go
index 93cd724e..b866eb2a 100644
--- a/shiny/driver/mtldriver/mtldriver.go
+++ b/shiny/driver/mtldriver/mtldriver.go
@@ -21,10 +21,10 @@ import (
"dmitri.shuralyov.com/gpu/mtl"
"github.com/go-gl/glfw/v3.3/glfw"
- "github.com/oakmound/oak/v3/shiny/driver/internal/errscreen"
- "github.com/oakmound/oak/v3/shiny/driver/mtldriver/internal/appkit"
- "github.com/oakmound/oak/v3/shiny/driver/mtldriver/internal/coreanim"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/errscreen"
+ "github.com/oakmound/oak/v4/shiny/driver/mtldriver/internal/appkit"
+ "github.com/oakmound/oak/v4/shiny/driver/mtldriver/internal/coreanim"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/mobile/event/key"
"golang.org/x/mobile/event/mouse"
"golang.org/x/mobile/event/paint"
diff --git a/shiny/driver/mtldriver/screen.go b/shiny/driver/mtldriver/screen.go
index f269f3a3..86b6101b 100644
--- a/shiny/driver/mtldriver/screen.go
+++ b/shiny/driver/mtldriver/screen.go
@@ -11,7 +11,7 @@ import (
"image"
"github.com/go-gl/glfw/v3.3/glfw"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
// screenImpl implements screen.Screen.
diff --git a/shiny/driver/mtldriver/texture.go b/shiny/driver/mtldriver/texture.go
index 7f6179ce..32f94225 100644
--- a/shiny/driver/mtldriver/texture.go
+++ b/shiny/driver/mtldriver/texture.go
@@ -11,7 +11,7 @@ import (
"image"
"image/color"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/image/draw"
)
diff --git a/shiny/driver/mtldriver/window.go b/shiny/driver/mtldriver/window.go
index 11ecfc39..2f26dde5 100644
--- a/shiny/driver/mtldriver/window.go
+++ b/shiny/driver/mtldriver/window.go
@@ -13,9 +13,9 @@ import (
"dmitri.shuralyov.com/gpu/mtl"
"github.com/go-gl/glfw/v3.3/glfw"
- "github.com/oakmound/oak/v3/shiny/driver/internal/event"
- "github.com/oakmound/oak/v3/shiny/driver/internal/lifecycler"
- "github.com/oakmound/oak/v3/shiny/driver/mtldriver/internal/coreanim"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/event"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/lifecycler"
+ "github.com/oakmound/oak/v4/shiny/driver/mtldriver/internal/coreanim"
"golang.org/x/mobile/event/size"
)
diff --git a/shiny/driver/mtldriver/window_amd64.go b/shiny/driver/mtldriver/window_amd64.go
index ba06d764..9e6e0f9d 100644
--- a/shiny/driver/mtldriver/window_amd64.go
+++ b/shiny/driver/mtldriver/window_amd64.go
@@ -6,8 +6,8 @@ package mtldriver
import (
"image"
- "github.com/oakmound/oak/v3/shiny/driver/internal/drawer"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/drawer"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/image/draw"
"golang.org/x/image/math/f64"
)
diff --git a/shiny/driver/mtldriver/window_arm64.go b/shiny/driver/mtldriver/window_arm64.go
index cf64640f..56c242c3 100644
--- a/shiny/driver/mtldriver/window_arm64.go
+++ b/shiny/driver/mtldriver/window_arm64.go
@@ -6,8 +6,8 @@ package mtldriver
import (
"image"
- "github.com/oakmound/oak/v3/shiny/driver/internal/drawer"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/drawer"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/image/draw"
"golang.org/x/image/math/f64"
)
diff --git a/shiny/driver/mtldriver_darwin.go b/shiny/driver/mtldriver_darwin.go
index 9035c9f1..46a12f20 100644
--- a/shiny/driver/mtldriver_darwin.go
+++ b/shiny/driver/mtldriver_darwin.go
@@ -14,45 +14,12 @@ import (
"regexp"
"strconv"
- "github.com/oakmound/oak/v3/shiny/driver/mtldriver"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/mtldriver"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
func main(f func(screen.Screen)) {
mtldriver.Main(f)
}
-var (
- sysProfRegex = regexp.MustCompile(`Resolution: (\d)* x (\d)*`)
-)
-
-func monitorSize() (int, int) {
- out, err := exec.Command("system_profiler", "SPDisplaysDataType").CombinedOutput()
- if err != nil {
- return 0, 0
- }
- found := sysProfRegex.FindAll(out, -1)
- if len(found) == 0 {
- return 0, 0
- }
- if len(found) != 1 {
- fmt.Println("Found multiple screens", len(found))
- }
- first := found[0]
- first = bytes.TrimPrefix(first, []byte("Resolution: "))
- dims := bytes.Split(first, []byte(" x "))
- if len(dims) != 2 {
- return 0, 0
- }
- w, err := strconv.Atoi(string(dims[0]))
- if err != nil {
- return 0, 0
- }
- h, err := strconv.Atoi(string(dims[1]))
- if err != nil {
- return 0, 0
- }
- return w, h
-}
-
type Window = mtldriver.Window
diff --git a/shiny/driver/noop/noop.go b/shiny/driver/noop/noop.go
index 9f0f127a..521de1b3 100644
--- a/shiny/driver/noop/noop.go
+++ b/shiny/driver/noop/noop.go
@@ -6,8 +6,8 @@ import (
"image/color"
"image/draw"
- "github.com/oakmound/oak/v3/shiny/driver/internal/event"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/event"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
func Main(f func(screen.Screen)) {
diff --git a/shiny/driver/windriver/buffer.go b/shiny/driver/windriver/buffer.go
index a5cea54e..4fc6aa23 100644
--- a/shiny/driver/windriver/buffer.go
+++ b/shiny/driver/windriver/buffer.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build windows
// +build windows
package windriver
@@ -12,8 +13,8 @@ import (
"sync"
"syscall"
- "github.com/oakmound/oak/v3/shiny/driver/internal/swizzle"
- "github.com/oakmound/oak/v3/shiny/driver/internal/win32"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/swizzle"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/win32"
)
type bufferImpl struct {
diff --git a/shiny/driver/windriver/other.go b/shiny/driver/windriver/other.go
index 72e8c2e2..2197033d 100644
--- a/shiny/driver/windriver/other.go
+++ b/shiny/driver/windriver/other.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !windows
// +build !windows
package windriver
@@ -10,8 +11,8 @@ import (
"fmt"
"runtime"
- "github.com/oakmound/oak/v3/shiny/driver/internal/errscreen"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/errscreen"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
// Main is called by the program's main function to run the graphical
diff --git a/shiny/driver/windriver/screen.go b/shiny/driver/windriver/screen.go
index 4f9ca956..0e916055 100644
--- a/shiny/driver/windriver/screen.go
+++ b/shiny/driver/windriver/screen.go
@@ -12,8 +12,8 @@ import (
"image"
"unsafe"
- "github.com/oakmound/oak/v3/shiny/driver/internal/win32"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/win32"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
type screenImpl struct {
diff --git a/shiny/driver/windriver/texture.go b/shiny/driver/windriver/texture.go
index 727e17d4..36edce5b 100644
--- a/shiny/driver/windriver/texture.go
+++ b/shiny/driver/windriver/texture.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build windows
// +build windows
package windriver
@@ -15,8 +16,8 @@ import (
"syscall"
"unsafe"
- "github.com/oakmound/oak/v3/shiny/driver/internal/win32"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/win32"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
type textureImpl struct {
diff --git a/shiny/driver/windriver/window.go b/shiny/driver/windriver/window.go
index 8297d1fb..a864882f 100644
--- a/shiny/driver/windriver/window.go
+++ b/shiny/driver/windriver/window.go
@@ -24,10 +24,10 @@ import (
"syscall"
"unsafe"
- "github.com/oakmound/oak/v3/shiny/driver/internal/drawer"
- "github.com/oakmound/oak/v3/shiny/driver/internal/event"
- "github.com/oakmound/oak/v3/shiny/driver/internal/win32"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/drawer"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/event"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/win32"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/image/math/f64"
"golang.org/x/mobile/event/key"
"golang.org/x/mobile/event/lifecycle"
diff --git a/shiny/driver/windriver/windraw.go b/shiny/driver/windriver/windraw.go
index a412bf7b..8d7f1ca2 100644
--- a/shiny/driver/windriver/windraw.go
+++ b/shiny/driver/windriver/windraw.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build windows
// +build windows
package windriver
@@ -14,7 +15,7 @@ import (
"syscall"
"unsafe"
- "github.com/oakmound/oak/v3/shiny/driver/internal/win32"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/win32"
)
func mkbitmap(size image.Point) (syscall.Handle, *byte, error) {
diff --git a/shiny/driver/windriver/windriver.go b/shiny/driver/windriver/windriver.go
index 5e29e862..cf339cd9 100644
--- a/shiny/driver/windriver/windriver.go
+++ b/shiny/driver/windriver/windriver.go
@@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build windows
// +build windows
package windriver
import (
- "github.com/oakmound/oak/v3/shiny/driver/internal/errscreen"
- "github.com/oakmound/oak/v3/shiny/driver/internal/win32"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/errscreen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/win32"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
// Main is called by the program's main function to run the graphical
diff --git a/shiny/driver/x11driver/buffer.go b/shiny/driver/x11driver/buffer.go
index 603d0003..4cb09e37 100644
--- a/shiny/driver/x11driver/buffer.go
+++ b/shiny/driver/x11driver/buffer.go
@@ -17,7 +17,7 @@ import (
"github.com/BurntSushi/xgb/shm"
"github.com/BurntSushi/xgb/xproto"
- "github.com/oakmound/oak/v3/shiny/driver/internal/swizzle"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/swizzle"
)
type bufferImpl struct {
diff --git a/shiny/driver/x11driver/screen.go b/shiny/driver/x11driver/screen.go
index ab55b5d6..56d5c448 100644
--- a/shiny/driver/x11driver/screen.go
+++ b/shiny/driver/x11driver/screen.go
@@ -21,8 +21,8 @@ import (
"github.com/BurntSushi/xgb/shm"
"github.com/BurntSushi/xgb/xproto"
- "github.com/oakmound/oak/v3/shiny/driver/internal/x11key"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/x11key"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/image/math/f64"
"golang.org/x/mobile/event/key"
"golang.org/x/mobile/event/mouse"
diff --git a/shiny/driver/x11driver/texture.go b/shiny/driver/x11driver/texture.go
index 31d52391..5c753a6f 100644
--- a/shiny/driver/x11driver/texture.go
+++ b/shiny/driver/x11driver/texture.go
@@ -14,7 +14,7 @@ import (
"github.com/BurntSushi/xgb/render"
"github.com/BurntSushi/xgb/xproto"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/image/math/f64"
)
diff --git a/shiny/driver/x11driver/window.go b/shiny/driver/x11driver/window.go
index cede7306..4756e917 100644
--- a/shiny/driver/x11driver/window.go
+++ b/shiny/driver/x11driver/window.go
@@ -16,12 +16,12 @@ import (
"github.com/BurntSushi/xgb/render"
"github.com/BurntSushi/xgb/xproto"
- "github.com/oakmound/oak/v3/shiny/driver/internal/drawer"
- "github.com/oakmound/oak/v3/shiny/driver/internal/event"
- "github.com/oakmound/oak/v3/shiny/driver/internal/lifecycler"
- "github.com/oakmound/oak/v3/shiny/driver/internal/x11"
- "github.com/oakmound/oak/v3/shiny/driver/internal/x11key"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/drawer"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/event"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/lifecycler"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/x11"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/x11key"
+ "github.com/oakmound/oak/v4/shiny/screen"
"golang.org/x/image/math/f64"
"golang.org/x/mobile/event/key"
"golang.org/x/mobile/event/mouse"
diff --git a/shiny/driver/x11driver/x11driver.go b/shiny/driver/x11driver/x11driver.go
index fa1f63db..ac7614f9 100644
--- a/shiny/driver/x11driver/x11driver.go
+++ b/shiny/driver/x11driver/x11driver.go
@@ -19,8 +19,8 @@ import (
"github.com/BurntSushi/xgbutil"
"github.com/BurntSushi/xgbutil/xevent"
- "github.com/oakmound/oak/v3/shiny/driver/internal/errscreen"
- "github.com/oakmound/oak/v3/shiny/screen"
+ "github.com/oakmound/oak/v4/shiny/driver/internal/errscreen"
+ "github.com/oakmound/oak/v4/shiny/screen"
)
// Main is called by the program's main function to run the graphical
diff --git a/shiny/screen/screen.go b/shiny/screen/screen.go
index 3eca3e4a..417bfe47 100644
--- a/shiny/screen/screen.go
+++ b/shiny/screen/screen.go
@@ -15,8 +15,8 @@
// package main
//
// import (
-// "github.com/oakmound/oak/v3/shiny/driver"
-// "github.com/oakmound/oak/v3/shiny/screen"
+// "github.com/oakmound/oak/v4/shiny/driver"
+// "github.com/oakmound/oak/v4/shiny/screen"
// "golang.org/x/mobile/event/lifecycle"
// )
//
@@ -68,8 +68,6 @@ type Screen interface {
NewTexture(size image.Point) (Texture, error)
// NewWindow returns a new Window for this screen.
- //
- // A nil opts is valid and means to use the default option values.
NewWindow(opts WindowGenerator) (Window, error)
}
diff --git a/test_coverage.sh b/test_coverage.sh
index 06aa1ff3..a0b230ca 100755
--- a/test_coverage.sh
+++ b/test_coverage.sh
@@ -18,6 +18,11 @@ if [ -f profile.out ]; then
cat profile.out >> coverage.txt
rm profile.out
fi
+go test -coverprofile=profile.out -covermode=atomic ./alg/span
+if [ -f profile.out ]; then
+ cat profile.out >> coverage.txt
+ rm profile.out
+fi
go test -coverprofile=profile.out -covermode=atomic ./collision
if [ -f profile.out ]; then
cat profile.out >> coverage.txt
diff --git a/timing/doc.go b/timing/doc.go
deleted file mode 100644
index 3d18ba1d..00000000
--- a/timing/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package timing provides utilities for time.
-package timing
diff --git a/timing/fps.go b/timing/fps.go
index 6cc03eb6..8f924f17 100644
--- a/timing/fps.go
+++ b/timing/fps.go
@@ -1,3 +1,4 @@
+// Package timing provides utilities for time.
package timing
import (
@@ -16,7 +17,7 @@ const (
func FPS(lastTime, now time.Time) float64 {
fps := 1 / now.Sub(lastTime).Seconds()
// This indicates that we recorded two times within
- // the innacuracy of the OS's system clock, so the values
+ // the inaccuracy of the OS's system clock, so the values
// were the same. 1200 is chosen because on windows,
// fps will be 1200 instead of a negative value.
if int(fps) < 0 {
diff --git a/viewport.go b/viewport.go
index 8ad51ea4..b405f474 100644
--- a/viewport.go
+++ b/viewport.go
@@ -1,21 +1,23 @@
package oak
import (
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/event"
)
-// SetScreen positions the viewport to be at x,y
-func (w *Window) SetScreen(x, y int) {
- w.setViewport(intgeom.Point2{x, y})
+type Viewport struct {
+ Position intgeom.Point2
+ Bounds intgeom.Rect2
+ BoundsEnforced bool
}
-// ShiftScreen shifts the viewport by x,y
-func (w *Window) ShiftScreen(x, y int) {
- w.setViewport(w.viewPos.Add(intgeom.Point2{x, y}))
+// ShiftViewport shifts the viewport by x,y
+func (w *Window) ShiftViewport(delta intgeom.Point2) {
+ w.SetViewport(w.viewPos.Add(delta))
}
-func (w *Window) setViewport(pt intgeom.Point2) {
+// SetViewport positions the viewport to be at x,y
+func (w *Window) SetViewport(pt intgeom.Point2) {
if w.useViewBounds {
if w.viewBounds.Min.X() <= pt.X() && w.viewBounds.Max.X() >= pt.X()+w.ScreenWidth {
w.viewPos[0] = pt.X()
@@ -37,8 +39,11 @@ func (w *Window) setViewport(pt intgeom.Point2) {
event.TriggerOn(w.eventHandler, ViewportUpdate, w.viewPos)
}
-// GetViewportBounds reports what bounds the viewport has been set to, if any.
-func (w *Window) GetViewportBounds() (rect intgeom.Rect2, ok bool) {
+// ViewportBounds returns the boundary of this window's viewport, or the rectangle
+// that the viewport is not allowed to exit as it moves around. It often represents
+// the total size of the world within a given scene. If bounds are not enforced, ok will
+// be false.
+func (w *Window) ViewportBounds() (rect intgeom.Rect2, ok bool) {
return w.viewBounds, w.useViewBounds
}
@@ -60,20 +65,14 @@ func (w *Window) SetViewportBounds(rect intgeom.Rect2) {
w.useViewBounds = true
w.viewBounds = rect
- newViewX := w.viewPos.X()
- newViewY := w.viewPos.Y()
- if newViewX < rect.Min[0] {
- newViewX = rect.Min[0]
- } else if newViewX > rect.Max[0] {
- newViewX = rect.Max[0]
- }
- if newViewY < rect.Min[1] {
- newViewY = rect.Min[1]
- } else if newViewY > rect.Max[1] {
- newViewY = rect.Max[1]
+ newView := rect.Clamp(w.viewPos)
+ if newView != w.viewPos {
+ w.SetViewport(newView)
}
+}
- if newViewX != w.viewPos.X() || newViewY != w.viewPos.Y() {
- w.setViewport(intgeom.Point2{newViewX, newViewY})
- }
+// Viewport returns the viewport's position. Its width and height are the window's
+// width and height. This position plus width/height cannot exceed ViewportBounds.
+func (w *Window) Viewport() intgeom.Point2 {
+ return w.viewPos
}
diff --git a/viewport_test.go b/viewport_test.go
index 96a3d113..7f44f1ed 100644
--- a/viewport_test.go
+++ b/viewport_test.go
@@ -4,8 +4,8 @@ import (
"testing"
"time"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/scene"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/scene"
)
func sleep() {
@@ -21,57 +21,58 @@ func TestViewport(t *testing.T) {
}
go c1.Init("blank")
time.Sleep(2 * time.Second)
- if (c1.viewPos) != (intgeom.Point2{0, 0}) {
- t.Fatalf("expected %v got %v", c1.viewPos, intgeom.Point2{0, 0})
+ if (c1.Viewport()) != (intgeom.Point2{0, 0}) {
+ t.Fatalf("expected %v got %v", c1.Viewport(), intgeom.Point2{0, 0})
}
- c1.SetScreen(5, 5)
- if (c1.viewPos) != (intgeom.Point2{5, 5}) {
- t.Fatalf("expected %v got %v", c1.viewPos, intgeom.Point2{5, 5})
+ c1.SetViewport(intgeom.Point2{5, 5})
+ if (c1.Viewport()) != (intgeom.Point2{5, 5}) {
+ t.Fatalf("expected %v got %v", c1.Viewport(), intgeom.Point2{5, 5})
}
- _, ok := c1.GetViewportBounds()
+ _, ok := c1.ViewportBounds()
if ok {
t.Fatalf("viewport bounds should not be set on scene start")
}
c1.SetViewportBounds(intgeom.NewRect2(0, 0, 4, 4))
- if (c1.viewPos) != (intgeom.Point2{5, 5}) {
- t.Fatalf("expected %v got %v", c1.viewPos, intgeom.Point2{5, 5})
+ if (c1.Viewport()) != (intgeom.Point2{5, 5}) {
+ t.Fatalf("expected %v got %v", c1.Viewport(), intgeom.Point2{5, 5})
}
- c1.SetScreen(-1, -1)
- if (c1.viewPos) != (intgeom.Point2{0, 0}) {
- t.Fatalf("expected %v got %v", c1.viewPos, intgeom.Point2{0, 0})
+ c1.SetViewport(intgeom.Point2{-1, -1})
+ if (c1.Viewport()) != (intgeom.Point2{0, 0}) {
+ t.Fatalf("expected %v got %v", c1.Viewport(), intgeom.Point2{0, 0})
}
- c1.SetScreen(6, 6)
- if (c1.viewPos) != (intgeom.Point2{0, 0}) {
- t.Fatalf("expected %v got %v", c1.viewPos, intgeom.Point2{0, 0})
+ c1.SetViewport(intgeom.Point2{6, 6})
+ if (c1.Viewport()) != (intgeom.Point2{0, 0}) {
+ t.Fatalf("expected %v got %v", c1.Viewport(), intgeom.Point2{0, 0})
}
c1.SetViewportBounds(intgeom.NewRect2(0, 0, 1000, 1000))
- c1.SetScreen(20, 20)
- if (c1.viewPos) != (intgeom.Point2{20, 20}) {
- t.Fatalf("expected %v got %v", c1.viewPos, intgeom.Point2{20, 20})
+ c1.SetViewport(intgeom.Point2{20, 20})
+ if (c1.Viewport()) != (intgeom.Point2{20, 20}) {
+ t.Fatalf("expected %v got %v", c1.Viewport(), intgeom.Point2{20, 20})
}
- c1.ShiftScreen(-1, -1)
- if (c1.viewPos) != (intgeom.Point2{19, 19}) {
- t.Fatalf("expected %v got %v", c1.viewPos, intgeom.Point2{19, 19})
+ c1.ShiftViewport(intgeom.Point2{-1, -1})
+ if (c1.Viewport()) != (intgeom.Point2{19, 19}) {
+ t.Fatalf("expected %v got %v", c1.Viewport(), intgeom.Point2{19, 19})
}
c1.SetViewportBounds(intgeom.NewRect2(21, 21, 2000, 2000))
- if (c1.viewPos) != (intgeom.Point2{21, 21}) {
- t.Fatalf("expected %v got %v", c1.viewPos, intgeom.Point2{21, 21})
+ if (c1.Viewport()) != (intgeom.Point2{21, 21}) {
+ t.Fatalf("expected %v got %v", c1.Viewport(), intgeom.Point2{21, 21})
}
- c1.SetScreen(1000, 1000)
+ c1.SetViewport(intgeom.Point2{1000, 1000})
c1.SetViewportBounds(intgeom.NewRect2(0, 0, 900, 900))
- bds, ok := c1.GetViewportBounds()
+ bds, ok := c1.ViewportBounds()
if !ok {
t.Fatalf("viewport bounds were not enabled")
}
if bds != intgeom.NewRect2(0, 0, 900, 900) {
t.Fatalf("viewport bounds were not set: expected %v got %v", intgeom.NewRect2(0, 0, 900, 900), bds)
}
- if (c1.viewPos) != (intgeom.Point2{900 - c1.Width(), 900 - c1.Height()}) {
- t.Fatalf("expected %v got %v", c1.viewPos, intgeom.Point2{900 - c1.Width(), 900 - c1.Height()})
+ mx := intgeom.Point2{900, 900}
+ if (c1.Viewport()) != mx.Sub(c1.Bounds()) {
+ t.Fatalf("expected %v got %v", c1.Viewport(), mx.Sub(c1.Bounds()))
}
c1.RemoveViewportBounds()
- _, ok = c1.GetViewportBounds()
+ _, ok = c1.ViewportBounds()
if ok {
t.Fatalf("viewport bounds were enabled after clear")
}
@@ -81,11 +82,11 @@ func TestViewport(t *testing.T) {
sleep()
- if (c1.viewPos) != (intgeom.Point2{0, 0}) {
- t.Fatalf("expected %v got %v", c1.viewPos, intgeom.Point2{0, 0})
+ if (c1.Viewport()) != (intgeom.Point2{0, 0}) {
+ t.Fatalf("expected %v got %v", c1.Viewport(), intgeom.Point2{0, 0})
}
- _, ok = c1.GetViewportBounds()
+ _, ok = c1.ViewportBounds()
if ok {
t.Fatalf("viewport bounds should not be set on scene start")
}
diff --git a/window.go b/window.go
index 9a9fa480..57e1d4b7 100644
--- a/window.go
+++ b/window.go
@@ -1,3 +1,15 @@
+// Package oak is a game engine. It provides scene control, control over windows
+// and what is drawn to them, propagates regular events to evaluate game logic,
+// and so on.
+//
+// A minimal oak app follows:
+//
+// func main() {
+// oak.AddScene("myApp", scene.Scene{Start: func(ctx *scene.Context) {
+// // ... ctx.Draw(...), event.Bind(ctx, ...)
+// }})
+// oak.Init("myApp")
+// }
package oak
import (
@@ -8,23 +20,22 @@ import (
"sync/atomic"
"time"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/debugstream"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/key"
- "github.com/oakmound/oak/v3/mouse"
- "github.com/oakmound/oak/v3/render"
- "github.com/oakmound/oak/v3/scene"
- "github.com/oakmound/oak/v3/shiny/driver"
- "github.com/oakmound/oak/v3/shiny/screen"
- "github.com/oakmound/oak/v3/window"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/debugstream"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/key"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/render"
+ "github.com/oakmound/oak/v4/scene"
+ "github.com/oakmound/oak/v4/shiny/driver"
+ "github.com/oakmound/oak/v4/shiny/screen"
+ "github.com/oakmound/oak/v4/window"
)
var _ window.App = &Window{}
func (w *Window) windowController(s screen.Screen, x, y, width, height int) (*driver.Window, error) {
- // TODO v4: can we update this interface to return our concrete driver.Window?
dwin, err := s.NewWindow(screen.NewWindowGenerator(
screen.Dimensions(width, height),
screen.Title(w.config.Title),
@@ -40,6 +51,7 @@ func (w *Window) windowController(s screen.Screen, x, y, width, height int) (*dr
const bufferCount = 2
type Window struct {
+ // The keyboard state this window is aware of.
key.State
// the driver.Window embedded in this window exposes at compile time the OS level
@@ -62,6 +74,8 @@ type Window struct {
// drawing should cease (or resume)
drawCh chan struct{}
+ // The between draw channel receives a signal when
+ // a function is provided to Window.DoBetweenDraws.
betweenDrawCh chan func()
// ScreenWidth is the width of the screen
@@ -115,8 +129,8 @@ type Window struct {
// Driver is the driver oak will call during initialization
Driver Driver
- // prePublish is a function called each draw frame prior to
- prePublish func(w *Window, tx screen.Texture)
+ // prePublish is a function called each draw frame prior to publishing frames to the OS
+ prePublish func(*image.RGBA)
// LoadingR is a renderable that is displayed during loading screens.
LoadingR render.Renderable
@@ -147,8 +161,6 @@ type Window struct {
FirstSceneInput interface{}
- commands map[string]func([]string)
-
ControllerID int32
config Config
@@ -172,30 +184,27 @@ var (
// NewWindow creates a window with default settings.
func NewWindow() *Window {
- c := &Window{
+ return &Window{
State: key.NewState(),
transitionCh: make(chan struct{}),
skipSceneCh: make(chan string),
quitCh: make(chan struct{}),
drawCh: make(chan struct{}),
betweenDrawCh: make(chan func()),
+ SceneMap: scene.NewMap(),
+ Driver: driver.Main,
+ prePublish: func(*image.RGBA) {},
+ bkgFn: func() image.Image {
+ return image.Black
+ },
+ eventHandler: event.DefaultBus,
+ MouseTree: mouse.DefaultTree,
+ CollisionTree: collision.DefaultTree,
+ CallerMap: event.DefaultCallerMap,
+ DrawStack: render.GlobalDrawStack,
+ ControllerID: atomic.AddInt32(nextControllerID, 1),
+ ParentContext: context.Background(),
}
-
- c.SceneMap = scene.NewMap()
- c.Driver = driver.Main
- c.prePublish = func(*Window, screen.Texture) {}
- c.bkgFn = func() image.Image {
- return image.Black
- }
- c.eventHandler = event.DefaultBus
- c.MouseTree = mouse.DefaultTree
- c.CollisionTree = collision.DefaultTree
- c.CallerMap = event.DefaultCallerMap
- c.DrawStack = render.GlobalDrawStack
- c.commands = make(map[string]func([]string))
- c.ControllerID = atomic.AddInt32(nextControllerID, 1)
- c.ParentContext = context.Background()
- return c
}
// Propagate triggers direct mouse events on entities which are clicked
@@ -255,27 +264,10 @@ func (w *Window) Propagate(ev event.EventID[*mouse.Event], me mouse.Event) {
}
}
-// Width returns the absolute width of the window in pixels.
-func (w *Window) Width() int {
- return w.ScreenWidth
-}
-
-// Height returns the absolute height of the window in pixels.
-func (w *Window) Height() int {
- return w.ScreenHeight
-}
-
-// Viewport returns the viewport's position. Its width and height are the window's
-// width and height. This position plus width/height cannot exceed ViewportBounds.
-func (w *Window) Viewport() intgeom.Point2 {
- return w.viewPos
-}
-
-// ViewportBounds returns the boundary of this window's viewport, or the rectangle
-// that the viewport is not allowed to exit as it moves around. It often represents
-// the total size of the world within a given scene.
-func (w *Window) ViewportBounds() intgeom.Rect2 {
- return w.viewBounds
+// Width returns the absolute bounds of a window in pixels. It does not include window elements outside
+// of the client area (OS provided title bars).
+func (w *Window) Bounds() intgeom.Point2 {
+ return intgeom.Point2{w.ScreenWidth, w.ScreenHeight}
}
// SetLoadingRenderable sets what renderable should display between scenes
@@ -291,7 +283,7 @@ func (w *Window) SetBackground(b Background) {
}
}
-// SetColorBackground sets this window's background to be a standar image.Image,
+// SetColorBackground sets this window's background to be a standard image.Image,
// commonly a uniform color.
func (w *Window) SetColorBackground(img image.Image) {
w.bkgFn = func() image.Image {
@@ -312,9 +304,7 @@ func (w *Window) SetLogicHandler(h event.Handler) {
// NextScene causes this window to immediately end the current scene.
func (w *Window) NextScene() {
- go func() {
- w.skipSceneCh <- ""
- }()
+ w.GoToScene("")
}
// GoToScene causes this window to skip directly to the given scene.
@@ -329,12 +319,6 @@ func (w *Window) InFocus() bool {
return w.inFocus
}
-// CollisionTrees helps access the mouse and collision trees from the controller.
-// These trees together detail how a controller can drive mouse and entity interactions.
-func (w *Window) CollisionTrees() (mouseTree, collisionTree *collision.Tree) {
- return w.MouseTree, w.CollisionTree
-}
-
// EventHandler returns this window's event handler.
func (w *Window) EventHandler() event.Handler {
return w.eventHandler
@@ -356,7 +340,3 @@ func (w *Window) debugConsole(input io.Reader, output io.Writer) {
debugstream.AttachToStream(w.ParentContext, input, output)
debugstream.AddDefaultsForScope(w.ControllerID, w)
}
-
-func (w *Window) GetCallerMap() *event.CallerMap {
- return w.CallerMap
-}
diff --git a/window/window.go b/window/window.go
index a27fe188..6b512db5 100644
--- a/window/window.go
+++ b/window/window.go
@@ -4,8 +4,8 @@ package window
import (
"image"
- "github.com/oakmound/oak/v3/alg/intgeom"
- "github.com/oakmound/oak/v3/event"
+ "github.com/oakmound/oak/v4/alg/intgeom"
+ "github.com/oakmound/oak/v4/event"
)
// Window is an interface of methods on an oak.Window available on platforms which have distinct app windows
@@ -13,31 +13,58 @@ import (
type Window interface {
App
+ // SetFullscreen causes a window to expand and fill a display.
SetFullScreen(bool) error
+ // SetBorderless causes a window to lose its OS-provided border definitions, e.g. window title, close button.
SetBorderless(bool) error
+ // SetTopMost causes a window to remain above other windows even when it is clicked out of.
SetTopMost(bool) error
+ // SetTitle changes the title of this window, usually displayed in the top left of the window next to the icon.
SetTitle(string) error
+ // SetIcon changes the icon of this window, usually displayed both in the top left of the window and in a taskbar
+ // component.
SetIcon(image.Image) error
+ // MoveWindow moves a window to the given x,y coordinates with the given dimensions.
+ // TODO v4: intgeom.Rect2?
MoveWindow(x, y, w, h int) error
+ // HideCursor will cause the mouse cursor to not display when it lies within this window.
HideCursor() error
}
// App is an interface of methods available to all oak programs.
type App interface {
- Width() int
- Height() int
+ // Bounds returns the boundaries of the application client area measured in pixels. This is not the size
+ // of the window or app on the operating system necessarily; it is the area able to be rendered to within oak.
+ // On some platforms these two concepts will usually be equal (js); on some they will have a built in scaling factor
+ // (osx, for retina displays), and if a window is manually scaled by a user and oak is not instructed to resize to
+ // match the scale, this area will be unchanged and the view will be stretched to fit the window.
+ Bounds() intgeom.Point2
+
+ // Viewport relates Bounds() to the entire content available for display. Viewport returns where the top left corner
+ // of the application client area is.
Viewport() intgeom.Point2
+ // SetViewportBounds defines the limits of where the viewport may be positioned. In other words, the total viewable
+ // content of a scene. Unless impossible, the rectangle (viewport, viewport+bounds) will never leave the area defined
+ // by SetViewportBounds.
SetViewportBounds(intgeom.Rect2)
+ // ShiftViewport is a helper method calling a.SetViewport(a.Viewport()+delta)
+ ShiftViewport(delta intgeom.Point2)
+ // SetViewport changes where the viewport position. If the resulting rectangle (viewport, viewport+bounds) would
+ // exceed the boundary set by SetViewportBounds, viewport will be clamped to the edges of that boundary.
+ SetViewport(intgeom.Point2)
- ShiftScreen(int, int)
- SetScreen(int, int)
-
+ // NextScene causes the End function to be triggered for the current scene.
NextScene()
+ // GoToScene causes the End function to be triggered for the current scene, overriding the next scene to start.
GoToScene(string)
+ // InFocus returns whether the application is currently focused on, by whatever definition the OS has for an
+ // application being in focus. For example, on linux/osx/windows a window is in focus once it is clicked on
+ // and out of focus after another window is clicked on.
InFocus() bool
+ // Quit causes the app to cleanly exit. The current scene will not call it's End function.
Quit()
+ // EventHandler returns this app's active event handler.
EventHandler() event.Handler
- GetCallerMap() *event.CallerMap
}
diff --git a/window_test.go b/window_test.go
index cdd02d7b..cd61f47b 100644
--- a/window_test.go
+++ b/window_test.go
@@ -1,12 +1,16 @@
package oak
import (
+ "image"
+ "os"
"testing"
"time"
- "github.com/oakmound/oak/v3/collision"
- "github.com/oakmound/oak/v3/event"
- "github.com/oakmound/oak/v3/mouse"
+ "github.com/oakmound/oak/v4/alg/floatgeom"
+ "github.com/oakmound/oak/v4/collision"
+ "github.com/oakmound/oak/v4/event"
+ "github.com/oakmound/oak/v4/mouse"
+ "github.com/oakmound/oak/v4/render"
)
func TestMouseClicks(t *testing.T) {
@@ -91,3 +95,91 @@ func TestPropagate(t *testing.T) {
case <-ch:
}
}
+
+func TestPropagate_StopPropagation(t *testing.T) {
+ c1 := NewWindow()
+ c1.eventHandler = event.NewBus(event.NewCallerMap())
+ c1.MouseTree = collision.NewTree()
+
+ e1 := ent{}
+ e1.CallerID = c1.eventHandler.GetCallerMap().Register(e1)
+ e2 := ent{}
+ e2.CallerID = c1.eventHandler.GetCallerMap().Register(e2)
+
+ s1 := collision.NewSpace(10, 10, 10, 10, e1.CallerID)
+ s1.SetZLayer(10)
+ c1.MouseTree.Insert(s1)
+ s2 := collision.NewSpace(10, 10, 10, 10, e2.CallerID)
+ s2.SetZLayer(1)
+ c1.MouseTree.Insert(s2)
+ var failed bool
+ <-event.Bind(c1.eventHandler, mouse.PressOn, e1, func(_ ent, ev *mouse.Event) event.Response {
+ ev.StopPropagation = true
+ return 0
+ }).Bound
+ <-event.Bind(c1.eventHandler, mouse.PressOn, e2, func(_ ent, ev *mouse.Event) event.Response {
+ failed = true
+ return 0
+ }).Bound
+ <-event.Bind(c1.eventHandler, mouse.ClickOn, e1, func(_ ent, ev *mouse.Event) event.Response {
+ ev.StopPropagation = true
+ return 0
+ }).Bound
+ <-event.Bind(c1.eventHandler, mouse.ClickOn, e2, func(_ ent, ev *mouse.Event) event.Response {
+ failed = true
+ return 0
+ }).Bound
+ <-event.Bind(c1.eventHandler, mouse.RelativeClickOn, e1, func(_ ent, ev *mouse.Event) event.Response {
+ ev.StopPropagation = true
+ return 0
+ }).Bound
+ <-event.Bind(c1.eventHandler, mouse.RelativeClickOn, e2, func(_ ent, ev *mouse.Event) event.Response {
+ failed = true
+ return 0
+ }).Bound
+ c1.TriggerMouseEvent(mouse.Event{
+ Point2: floatgeom.Point2{
+ 15, 15,
+ },
+ Button: mouse.ButtonLeft,
+ EventType: mouse.Press,
+ })
+ c1.TriggerMouseEvent(mouse.Event{
+ Point2: floatgeom.Point2{
+ 15, 15,
+ },
+ Button: mouse.ButtonLeft,
+ EventType: mouse.Release,
+ })
+ if failed {
+ t.Fatal("stop propagation failed")
+ }
+}
+
+func TestWindowGetters(t *testing.T) {
+ c1 := NewWindow()
+ c1.debugConsole(os.Stdin, os.Stdout)
+ if c1.InFocus() {
+ t.Errorf("new windows should not be in focus")
+ }
+ if c1.EventHandler() != event.DefaultBus {
+ t.Errorf("new windows should have the default event bus")
+ }
+ if c1.GetBackgroundImage() != image.Black {
+ t.Errorf("new windows should have a black background")
+ }
+ c1.SetColorBackground(image.White)
+ if c1.GetBackgroundImage() != image.White {
+ t.Errorf("set color background failed")
+ }
+ rend := render.EmptyRenderable()
+ c1.SetLoadingRenderable(rend)
+ if c1.LoadingR != rend {
+ t.Errorf("Set loading renderable failed")
+ }
+ c1.SetBackground(rend)
+ r, g, b, a := c1.bkgFn().At(0, 0).RGBA()
+ if r != 0 || g != 0 || b != 0 || a != 0 {
+ t.Errorf("background was not set to empty renderable")
+ }
+}