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") + } +}