Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Foraging view for the visualization of patch resources, visits, and colony status #42

Merged
merged 3 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
- Random seed is stored in experiments, but can be overwritten via CLI (#34)
- Record start and end time of each run in parameters output file (#37)
- Add JSON property `Final` to table output to write rows on finalization only (#38)
- Provides more visualizations, like ECS monitor and resources and systems inspector (#39)
- Provides more visualizations, like ECS monitor and resources and systems inspector (#39)
- Adds a view for the visualization of patch resources, visits, and colony status (#42)

### Other

Expand Down
2 changes: 1 addition & 1 deletion _examples/base/observers.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"CsvSeparator": ";",
"Parameters": "out/Parameters.csv",
"Monitor": true,
"Inspector": true,
"Resources": true,
"Systems": true,
"Tables": [
{
Expand Down
1 change: 1 addition & 0 deletions _examples/patches/observers.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"ForagingView": true,
"TimeSeriesPlots": [
{
"Observer": "obs.WorkerCohorts",
Expand Down
2 changes: 1 addition & 1 deletion _examples/patches/patches.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"DistToColony": 500,
"DistToColony": 2000,
"ConstantPatch": {
"Nectar": 20,
"Pollen": 1,
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ module github.com/mlange-42/beecs-cli
go 1.22.0

require (
github.com/gopxl/pixel/v2 v2.1.0
github.com/mlange-42/arche v0.13.0
github.com/mlange-42/arche-model v0.8.2-0.20240521193509-06db52d11180
github.com/mlange-42/arche-pixel v0.9.0
github.com/mlange-42/beecs v0.1.1-0.20240522192430-609bf4f916fe
github.com/mlange-42/beecs v0.1.1-0.20240522213354-10bf02ffdd69
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
Expand All @@ -25,7 +26,6 @@ require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/gopxl/glhf/v2 v2.0.0 // indirect
github.com/gopxl/mainthread/v2 v2.0.0 // indirect
github.com/gopxl/pixel/v2 v2.1.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mazznoer/colorgrad v0.9.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ github.com/mlange-42/arche-model v0.8.2-0.20240521193509-06db52d11180 h1:R1u8Xj5
github.com/mlange-42/arche-model v0.8.2-0.20240521193509-06db52d11180/go.mod h1:5Pzd3iAgdGNTiMgFJxHdc6a7qoaQPBK9xmyhrKTMmV8=
github.com/mlange-42/arche-pixel v0.9.0 h1:UhHXUjSpvbKeJgRiWQWGYPrNbnu+007U9HaBBgW7uOY=
github.com/mlange-42/arche-pixel v0.9.0/go.mod h1:UWr4ZfpvreTRWr76dqi30VCax19kEbiSDSNrbZTbMDQ=
github.com/mlange-42/beecs v0.1.1-0.20240522192430-609bf4f916fe h1:amovK8Ki3TH11PI+2aMEMsIQFjdQnoLtL4U3Gaf740s=
github.com/mlange-42/beecs v0.1.1-0.20240522192430-609bf4f916fe/go.mod h1:dD3tTgxU6yczpKhErCY2BA65FdS3Ye0pwK6P/JE4JzQ=
github.com/mlange-42/beecs v0.1.1-0.20240522213354-10bf02ffdd69 h1:v4w91bVmHwRJdhbsiWUg5UhKquZJdYNoJI2rTpy5VlI=
github.com/mlange-42/beecs v0.1.1-0.20240522213354-10bf02ffdd69/go.mod h1:dD3tTgxU6yczpKhErCY2BA65FdS3Ye0pwK6P/JE4JzQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
17 changes: 13 additions & 4 deletions internal/util/observers.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/mlange-42/arche-pixel/plot"
"github.com/mlange-42/arche-pixel/window"
"github.com/mlange-42/beecs-cli/registry"
"github.com/mlange-42/beecs-cli/view"
)

type entry struct {
Expand Down Expand Up @@ -47,9 +48,10 @@ type ObserversDef struct {
CsvSeparator string
TimeSeriesPlots []TimeSeriesPlotDef
Tables []TableDef
Monitor bool
Inspector bool
Systems bool
Monitor bool // Show the ECS monitor.
Resources bool // Show the resources inspector.
Systems bool // Show the systems inspector.
ForagingView bool // Show the flower patch foraging view.
}

func (obs *ObserversDef) CreateObservers(withUI bool) (Observers, error) {
Expand Down Expand Up @@ -98,7 +100,7 @@ func (obs *ObserversDef) CreateObservers(withUI bool) (Observers, error) {
tsPlots = append(tsPlots, win)
}

if obs.Inspector {
if obs.Resources {
win := (&window.Window{}).
With(&plot.Resources{}).
With(&plot.Controls{})
Expand All @@ -111,6 +113,13 @@ func (obs *ObserversDef) CreateObservers(withUI bool) (Observers, error) {
With(&plot.Controls{})
tsPlots = append(tsPlots, win)
}

if obs.ForagingView {
win := (&window.Window{}).
With(&view.Foraging{}).
With(&plot.Controls{})
tsPlots = append(tsPlots, win)
}
}

tables := []*reporter.Callback{}
Expand Down
2 changes: 2 additions & 0 deletions registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ func init() {
RegisterObserver[obs.Stores]()
RegisterObserver[obs.PatchNectar]()
RegisterObserver[obs.PatchPollen]()
RegisterObserver[obs.NectarVisits]()
RegisterObserver[obs.PollenVisits]()

//RegisterResource[...]()

Expand Down
158 changes: 158 additions & 0 deletions view/foraging.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package view

import (
"image/color"
"math"

"github.com/gopxl/pixel/v2"
"github.com/gopxl/pixel/v2/backends/opengl"
"github.com/gopxl/pixel/v2/ext/imdraw"
"github.com/mlange-42/arche/ecs"
"github.com/mlange-42/arche/generic"
"github.com/mlange-42/beecs/comp"
"github.com/mlange-42/beecs/globals"
"github.com/mlange-42/beecs/params"
)

type Foraging struct {
drawer imdraw.IMDraw

stores *globals.Stores
popStats *globals.PopulationStats
energyContent *params.EnergyContent
patchFilter generic.Filter3[comp.Coords, comp.Resource, comp.Visits]
}

// Initialize the system
func (f *Foraging) Initialize(w *ecs.World, win *opengl.Window) {
f.drawer = *imdraw.New(nil)

f.stores = ecs.GetResource[globals.Stores](w)
f.popStats = ecs.GetResource[globals.PopulationStats](w)
f.energyContent = ecs.GetResource[params.EnergyContent](w)
f.patchFilter = *generic.NewFilter3[comp.Coords, comp.Resource, comp.Visits]()
}

// Update the drawer.
func (f *Foraging) Update(w *ecs.World) {}

// UpdateInputs handles input events of the previous frame update.
func (f *Foraging) UpdateInputs(w *ecs.World, win *opengl.Window) {}

// Draw the system
func (f *Foraging) Draw(w *ecs.World, win *opengl.Window) {
width := win.Canvas().Bounds().W()
height := win.Canvas().Bounds().H()

dMax := 10_100.0

scale := math.Min(width/dMax, height/dMax)

cx := width / 2
cy := height / 2
barWidth := 8.0
barHeight := 2.0

dr := &f.drawer

// Distance circles
drawCircle(dr, pixel.V(cx, cy), 1000*scale, 1, color.RGBA{60, 60, 60, 255})
drawCircle(dr, pixel.V(cx, cy), 2000*scale, 1, color.RGBA{60, 60, 60, 255})
drawCircle(dr, pixel.V(cx, cy), 3000*scale, 1, color.RGBA{60, 60, 60, 255})
drawCircle(dr, pixel.V(cx, cy), 4000*scale, 1, color.RGBA{60, 60, 60, 255})
drawCircle(dr, pixel.V(cx, cy), 5000*scale, 1, color.RGBA{60, 60, 60, 255})

// Hive resources
honeyStore := f.stores.Honey / (1000.0 * f.energyContent.Honey)
decentHoney := f.stores.DecentHoney / (1000.0 * f.energyContent.Honey)
pollenStore := f.stores.Pollen * 0.001 * 20 * barHeight
idealPollen := f.stores.IdealPollen * 0.001 * 20 * barHeight

drawRect(dr, pixel.V(cx-barWidth, cy+honeyStore), pixel.V(cx, cy), 0, color.RGBA{180, 180, 0, 255})
drawRect(dr, pixel.V(cx, cy+pollenStore), pixel.V(cx+barWidth, cy), 0, color.RGBA{180, 0, 180, 255})

drawRect(dr, pixel.V(cx-barWidth, cy+decentHoney), pixel.V(cx, cy), 1, color.RGBA{180, 180, 120, 255})
drawRect(dr, pixel.V(cx, cy+idealPollen), pixel.V(cx+barWidth, cy), 1, color.RGBA{180, 120, 180, 255})

// Hive age classes
popScale := 0.2
popLine := 0.0
drawArcS(dr, pixel.V(cx, cy),
popScale*math.Sqrt(float64(f.popStats.TotalPopulation)),
popLine, color.RGBA{128, 128, 128, 255})

drawArcS(dr, pixel.V(cx, cy),
popScale*math.Sqrt(float64(f.popStats.TotalBrood)),
popLine, color.RGBA{230, 230, 230, 255})

query := f.patchFilter.Query(w)
for query.Next() {
coords, res, vis := query.Get()
px, py := cx+coords.X*scale, cy+coords.Y*scale

// Patch marker
drawCircle(dr, pixel.V(px, py), 3, 0, color.RGBA{128, 128, 128, 255})

// Visits
if vis.Nectar > 0 {
drawArcSW(dr, pixel.V(px, py), math.Log2(float64(vis.Nectar)), 2, color.RGBA{180, 180, 80, 255})
}
if vis.Pollen > 0 {
drawArcSE(dr, pixel.V(px, py), math.Log2(float64(vis.Pollen)), 2, color.RGBA{180, 80, 180, 255})
}

// Resource bars
nectar := res.Nectar * 0.000_001 * barHeight
maxNectar := res.MaxNectar * 0.000_001 * barHeight
pollen := res.Pollen * 0.001 * 20 * barHeight
maxPollen := res.MaxPollen * 0.001 * 20 * barHeight

if maxNectar > 0 {
drawRect(dr, pixel.V(px-barWidth, py+nectar), pixel.V(px, py), 0, color.RGBA{180, 180, 0, 255})
drawRect(dr, pixel.V(px-barWidth, py+maxNectar), pixel.V(px, py), 1, color.RGBA{180, 180, 80, 255})
}

if maxPollen > 0 {
drawRect(dr, pixel.V(px, py+pollen), pixel.V(px+barWidth, py), 0, color.RGBA{180, 0, 180, 255})
drawRect(dr, pixel.V(px, py+maxPollen), pixel.V(px+barWidth, py), 1, color.RGBA{180, 80, 180, 255})
}
}

dr.Draw(win)
dr.Clear()
}

func drawCircle(dr *imdraw.IMDraw, center pixel.Vec, radius float64, thickness float64, color color.RGBA) {
dr.Color = color
dr.Push(center)
dr.Circle(radius, thickness)
dr.Reset()
}

func drawArcSW(dr *imdraw.IMDraw, center pixel.Vec, radius float64, thickness float64, color color.RGBA) {
dr.Color = color
dr.Push(center)
dr.CircleArc(radius, math.Pi, math.Pi*1.5, thickness)
dr.Reset()
}

func drawArcSE(dr *imdraw.IMDraw, center pixel.Vec, radius float64, thickness float64, color color.RGBA) {
dr.Color = color
dr.Push(center)
dr.CircleArc(radius, math.Pi*1.5, math.Pi*2, thickness)
dr.Reset()
}

func drawArcS(dr *imdraw.IMDraw, center pixel.Vec, radius float64, thickness float64, color color.RGBA) {
dr.Color = color
dr.Push(center)
dr.CircleArc(radius, math.Pi, math.Pi*2, thickness)
dr.Reset()
}

func drawRect(dr *imdraw.IMDraw, p1, p2 pixel.Vec, thickness float64, color color.RGBA) {
dr.Color = color
dr.Push(p1, p2)
dr.Rectangle(thickness)
dr.Reset()
}
Loading