ichigo/engine/drawlist.go

166 lines
3.5 KiB
Go
Raw Normal View History

2021-09-15 11:53:17 +10:00
package engine
2021-09-16 21:32:03 +10:00
import (
2021-09-17 13:02:31 +10:00
"image"
2021-09-17 12:50:29 +10:00
"log"
2021-09-16 21:32:03 +10:00
2021-09-17 13:02:31 +10:00
"drjosh.dev/gurgle/geom"
2021-09-16 21:32:03 +10:00
"github.com/hajimehoshi/ebiten/v2"
)
2021-09-15 11:53:17 +10:00
2021-09-16 11:18:24 +10:00
const commonDrawerComparisons = false
2021-09-15 11:53:17 +10:00
var _ Drawer = tombstone{}
type tombstone struct{}
func (tombstone) Draw(*ebiten.Image, *ebiten.DrawImageOptions) {}
func (tombstone) DrawAfter(x Drawer) bool { return x != tombstone{} }
func (tombstone) DrawBefore(Drawer) bool { return false }
2021-09-17 11:13:39 +10:00
func (tombstone) String() string { return "tombstone" }
2021-09-15 11:53:17 +10:00
type drawList struct {
list []Drawer
rev map[Drawer]int
}
func (d drawList) Less(i, j int) bool {
2021-09-15 17:41:23 +10:00
// Deal with tombstones first, in case anything else thinks it
// needs to go last.
2021-09-15 11:53:17 +10:00
if d.list[i] == (tombstone{}) {
return false
}
if d.list[j] == (tombstone{}) {
return true
}
2021-09-15 17:41:23 +10:00
2021-09-16 11:18:24 +10:00
if commonDrawerComparisons {
// Common logic for known interfaces (BoundingBoxer, ZPositioner), to
// simplify Draw{Before,After} implementations.
switch x := d.list[i].(type) {
2021-09-15 17:41:23 +10:00
case BoundingBoxer:
2021-09-16 11:18:24 +10:00
xb := x.BoundingBox()
switch y := d.list[j].(type) {
case BoundingBoxer:
yb := y.BoundingBox()
if xb.Min.Z >= yb.Max.Z { // x is in front of y
return false
}
if xb.Max.Z <= yb.Min.Z { // x is behind y
return true
}
if xb.Max.Y <= yb.Min.Y { // x is above y
return false
}
if xb.Min.Y >= yb.Max.Y { // x is below y
return true
}
case ZPositioner:
return xb.Max.Z < y.ZPos() // x is before y
2021-09-15 17:41:23 +10:00
}
case ZPositioner:
2021-09-16 11:18:24 +10:00
switch y := d.list[j].(type) {
case BoundingBoxer:
return x.ZPos() < y.BoundingBox().Min.Z
case ZPositioner:
return x.ZPos() < y.ZPos()
}
2021-09-15 17:41:23 +10:00
}
}
// Fallback case: ask the components themselves
2021-09-15 11:53:17 +10:00
return d.list[i].DrawBefore(d.list[j]) || d.list[j].DrawAfter(d.list[i])
}
func (d drawList) Len() int { return len(d.list) }
func (d drawList) Swap(i, j int) {
d.rev[d.list[i]], d.rev[d.list[j]] = j, i
d.list[i], d.list[j] = d.list[j], d.list[i]
}
2021-09-16 21:32:03 +10:00
2021-09-17 13:02:31 +10:00
// Slow topological sort
2021-09-17 12:50:29 +10:00
func (d *drawList) topsort() {
// Produce edge lists - O(|V|^2)
// Count indegrees - also O(|V|^2)
edges := make([][]int, len(d.list))
indegree := make([]int, len(d.list))
for i, u := range d.list {
if u == (tombstone{}) {
continue
}
2021-09-17 13:02:31 +10:00
var ub image.Rectangle
switch x := u.(type) {
case BoundingBoxer:
ub = x.BoundingBox().BoundingRect(geom.IntProjection{X: 0, Y: 1})
default:
ub = image.Rect(0, 0, 320, 240)
}
2021-09-17 12:50:29 +10:00
for j, v := range d.list {
if i == j {
continue
}
if v == (tombstone{}) {
2021-09-16 21:32:03 +10:00
continue
}
2021-09-17 13:02:31 +10:00
var vb image.Rectangle
switch y := v.(type) {
case BoundingBoxer:
vb = y.BoundingBox().BoundingRect(geom.IntProjection{X: 0, Y: 1})
default:
vb = image.Rect(0, 0, 320, 240)
}
if !ub.Overlaps(vb) {
// No overlap, no need to emit an edge
continue
}
2021-09-16 21:32:03 +10:00
if u.DrawBefore(v) || v.DrawAfter(u) {
2021-09-17 12:50:29 +10:00
edges[i] = append(edges[i], j)
indegree[j]++
2021-09-16 21:32:03 +10:00
}
}
}
2021-09-17 12:50:29 +10:00
// Start queue with all zero-indegree vertices
var queue []int
for i, n := range indegree {
if d.list[i] == (tombstone{}) {
continue
2021-09-16 21:32:03 +10:00
}
2021-09-17 12:50:29 +10:00
if n == 0 {
queue = append(queue, i)
2021-09-16 21:32:03 +10:00
}
2021-09-17 12:50:29 +10:00
}
// Process into new list
list := make([]Drawer, 0, len(d.list))
for len(queue) > 0 {
i := queue[0]
queue = queue[1:]
2021-09-17 13:02:31 +10:00
d.rev[d.list[i]] = len(list)
2021-09-17 12:50:29 +10:00
list = append(list, d.list[i])
for _, j := range edges[i] {
indegree[j]--
if indegree[j] <= 0 {
if indegree[j] < 0 {
log.Printf("indegree[%d] = %d (component %v)", j, indegree[j], d.list[j])
2021-09-16 21:32:03 +10:00
}
2021-09-17 12:50:29 +10:00
queue = append(queue, j)
2021-09-16 21:32:03 +10:00
}
}
}
2021-09-17 12:50:29 +10:00
2021-09-16 21:32:03 +10:00
// Replace list
d.list = list
2021-09-17 13:02:31 +10:00
if false {
// Update rev
d.rev = make(map[Drawer]int, len(list))
for i, v := range list {
d.rev[v] = i
}
2021-09-16 21:32:03 +10:00
}
}