2021-09-15 11:53:17 +10:00
|
|
|
package engine
|
|
|
|
|
2021-09-16 21:32:03 +10:00
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
|
|
|
|
"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
|
|
|
|
|
|
|
// Bad, slow, topological sort
|
|
|
|
func (d *drawList) topsort() error {
|
2021-09-17 11:13:39 +10:00
|
|
|
// Count indegrees - O(|V|^2)
|
2021-09-16 21:32:03 +10:00
|
|
|
indegree := make(map[Drawer]int)
|
|
|
|
for _, u := range d.list {
|
2021-09-17 11:13:39 +10:00
|
|
|
indegree[u] += 0
|
2021-09-16 21:32:03 +10:00
|
|
|
for _, v := range d.list {
|
|
|
|
if u == v {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if u.DrawBefore(v) || v.DrawAfter(u) {
|
|
|
|
indegree[v]++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-09-17 11:13:39 +10:00
|
|
|
//log.Printf("indegree: %v", indegree)
|
2021-09-16 21:32:03 +10:00
|
|
|
// Sort into new list
|
|
|
|
list := make([]Drawer, 0, len(d.list))
|
|
|
|
for len(indegree) > 0 {
|
|
|
|
var bag []Drawer
|
|
|
|
for v, n := range indegree {
|
|
|
|
if n == 0 {
|
|
|
|
bag = append(bag, v)
|
|
|
|
}
|
|
|
|
}
|
2021-09-17 11:13:39 +10:00
|
|
|
//log.Printf("zero indegree vertices: %v", bag)
|
2021-09-16 21:32:03 +10:00
|
|
|
if len(bag) == 0 {
|
2021-09-17 11:13:39 +10:00
|
|
|
//log.Printf("remaining vertices: %v", indegree)
|
2021-09-16 21:32:03 +10:00
|
|
|
return errors.New("no vertices with zero indegree")
|
|
|
|
}
|
|
|
|
list = append(list, bag...)
|
|
|
|
for _, u := range bag {
|
|
|
|
delete(indegree, u)
|
|
|
|
}
|
|
|
|
for _, u := range bag {
|
|
|
|
for v := range indegree {
|
|
|
|
if u.DrawBefore(v) || v.DrawAfter(u) {
|
|
|
|
indegree[v]--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Replace list
|
|
|
|
d.list = list
|
|
|
|
// Update rev
|
|
|
|
for i, v := range list {
|
|
|
|
d.rev[v] = i
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|