topsort cleanups
This commit is contained in:
parent
86ee887206
commit
6565fd4160
2 changed files with 28 additions and 44 deletions
|
@ -2,7 +2,7 @@ package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"image"
|
"image"
|
||||||
"log"
|
"math"
|
||||||
|
|
||||||
"drjosh.dev/gurgle/geom"
|
"drjosh.dev/gurgle/geom"
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
@ -82,41 +82,36 @@ func (d drawList) Swap(i, j int) {
|
||||||
d.list[i], d.list[j] = d.list[j], d.list[i]
|
d.list[i], d.list[j] = d.list[j], d.list[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slow topological sort
|
// Slow topological sort. Uses a projection π to flatten bounding boxes for
|
||||||
func (d *drawList) topsort() {
|
// overlap tests, so that the graph is reduced.
|
||||||
// Produce edge lists - O(|V|^2)
|
func (d *drawList) topsort(π geom.Projector) {
|
||||||
// Count indegrees - also O(|V|^2)
|
// Produce edge lists and count indegrees - O(|V|^2)
|
||||||
|
// TODO: optimise this
|
||||||
edges := make([][]int, len(d.list))
|
edges := make([][]int, len(d.list))
|
||||||
indegree := make([]int, len(d.list))
|
indegree := make([]int, len(d.list))
|
||||||
for i, u := range d.list {
|
for i, u := range d.list {
|
||||||
if u == (tombstone{}) {
|
if u == (tombstone{}) {
|
||||||
|
// Prevents processing this vertex later on
|
||||||
|
indegree[i] = -1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var ub image.Rectangle
|
// If we can't get a more specific bounding rect, assume entire plane.
|
||||||
switch x := u.(type) {
|
ub := image.Rect(math.MinInt, math.MinInt, math.MaxInt, math.MaxInt)
|
||||||
case BoundingBoxer:
|
if x, ok := u.(BoundingBoxer); ok {
|
||||||
ub = x.BoundingBox().BoundingRect(geom.IntProjection{X: 0, Y: 1})
|
ub = x.BoundingBox().BoundingRect(π)
|
||||||
default:
|
|
||||||
ub = image.Rect(0, 0, 320, 240)
|
|
||||||
}
|
}
|
||||||
|
// For each possible neighbor...
|
||||||
for j, v := range d.list {
|
for j, v := range d.list {
|
||||||
if i == j {
|
if i == j || v == (tombstone{}) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if v == (tombstone{}) {
|
// Does it have a bounding rect? Do overlap test.
|
||||||
|
if y, ok := v.(BoundingBoxer); ok {
|
||||||
|
if vb := y.BoundingBox().BoundingRect(π); !ub.Overlaps(vb) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
// If the edge goes u->v, add it.
|
||||||
if u.DrawBefore(v) || v.DrawAfter(u) {
|
if u.DrawBefore(v) || v.DrawAfter(u) {
|
||||||
edges[i] = append(edges[i], j)
|
edges[i] = append(edges[i], j)
|
||||||
indegree[j]++
|
indegree[j]++
|
||||||
|
@ -124,42 +119,31 @@ func (d *drawList) topsort() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start queue with all zero-indegree vertices
|
// Initialise queue with all the zero-indegree vertices
|
||||||
var queue []int
|
var queue []int
|
||||||
for i, n := range indegree {
|
for i, n := range indegree {
|
||||||
if d.list[i] == (tombstone{}) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
queue = append(queue, i)
|
queue = append(queue, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process into new list
|
// Process into new list. O(|V| + |E|)
|
||||||
list := make([]Drawer, 0, len(d.list))
|
list := make([]Drawer, 0, len(d.list))
|
||||||
for len(queue) > 0 {
|
for len(queue) > 0 {
|
||||||
|
// Get front of queue.
|
||||||
i := queue[0]
|
i := queue[0]
|
||||||
queue = queue[1:]
|
queue = queue[1:]
|
||||||
|
// Add to output list.
|
||||||
d.rev[d.list[i]] = len(list)
|
d.rev[d.list[i]] = len(list)
|
||||||
list = append(list, d.list[i])
|
list = append(list, d.list[i])
|
||||||
|
// Reduce indegree for all outgoing edges, enqueue if indegree now 0.
|
||||||
for _, j := range edges[i] {
|
for _, j := range edges[i] {
|
||||||
indegree[j]--
|
indegree[j]--
|
||||||
if indegree[j] <= 0 {
|
if indegree[j] == 0 {
|
||||||
if indegree[j] < 0 {
|
|
||||||
log.Printf("indegree[%d] = %d (component %v)", j, indegree[j], d.list[j])
|
|
||||||
}
|
|
||||||
queue = append(queue, j)
|
queue = append(queue, j)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Job done!
|
||||||
// Replace list
|
|
||||||
d.list = list
|
d.list = list
|
||||||
if false {
|
|
||||||
// Update rev
|
|
||||||
d.rev = make(map[Drawer]int, len(list))
|
|
||||||
for i, v := range list {
|
|
||||||
d.rev[v] = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,7 +186,7 @@ func (g *Game) Update() error {
|
||||||
|
|
||||||
// Sort the draw list (on every frame - this isn't as bad as it sounds)
|
// Sort the draw list (on every frame - this isn't as bad as it sounds)
|
||||||
if topologicalDrawSort {
|
if topologicalDrawSort {
|
||||||
g.drawList.topsort()
|
g.drawList.topsort(g.Projection)
|
||||||
} else {
|
} else {
|
||||||
sort.Stable(g.drawList)
|
sort.Stable(g.drawList)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue