remove sort method
This commit is contained in:
parent
f03ab319b2
commit
f5219da56f
7 changed files with 54 additions and 178 deletions
|
@ -8,8 +8,6 @@ import (
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const commonDrawerComparisons = true
|
|
||||||
|
|
||||||
var _ Drawer = tombstone{}
|
var _ Drawer = tombstone{}
|
||||||
|
|
||||||
type tombstone struct{}
|
type tombstone struct{}
|
||||||
|
@ -26,23 +24,15 @@ type drawList struct {
|
||||||
rev map[Drawer]int
|
rev map[Drawer]int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d drawList) Less(i, j int) bool {
|
// edge reports if there is a draw ordering constraint between u and v (where
|
||||||
// Deal with tombstones first, in case anything else thinks it
|
// u draws before v).
|
||||||
// needs to go last.
|
func edge(u, v Drawer) bool {
|
||||||
if d.list[i] == (tombstone{}) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if d.list[j] == (tombstone{}) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if commonDrawerComparisons {
|
|
||||||
// Common logic for known interfaces (BoundingBoxer, ZPositioner), to
|
// Common logic for known interfaces (BoundingBoxer, ZPositioner), to
|
||||||
// simplify Draw{Before,After} implementations.
|
// simplify DrawOrderer implementations.
|
||||||
switch x := d.list[i].(type) {
|
switch x := u.(type) {
|
||||||
case BoundingBoxer:
|
case BoundingBoxer:
|
||||||
xb := x.BoundingBox()
|
xb := x.BoundingBox()
|
||||||
switch y := d.list[j].(type) {
|
switch y := v.(type) {
|
||||||
case BoundingBoxer:
|
case BoundingBoxer:
|
||||||
yb := y.BoundingBox()
|
yb := y.BoundingBox()
|
||||||
if xb.Min.Z >= yb.Max.Z { // x is in front of y
|
if xb.Min.Z >= yb.Max.Z { // x is in front of y
|
||||||
|
@ -62,27 +52,27 @@ func (d drawList) Less(i, j int) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
case ZPositioner:
|
case ZPositioner:
|
||||||
switch y := d.list[j].(type) {
|
switch y := v.(type) {
|
||||||
case BoundingBoxer:
|
case BoundingBoxer:
|
||||||
return x.ZPos() < y.BoundingBox().Min.Z
|
return x.ZPos() < y.BoundingBox().Min.Z
|
||||||
case ZPositioner:
|
case ZPositioner:
|
||||||
return x.ZPos() < y.ZPos()
|
return x.ZPos() < y.ZPos()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback case: ask the components themselves if they have an opinion
|
||||||
|
if do, ok := u.(DrawOrderer); ok && do.DrawBefore(v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if do, ok := v.(DrawOrderer); ok && do.DrawAfter(u) {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback case: ask the components themselves
|
// No relation
|
||||||
return d.list[i].DrawBefore(d.list[j]) || d.list[j].DrawAfter(d.list[i])
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d drawList) Len() int { return len(d.list) }
|
// Topological sort. Uses a projection π to flatten bounding boxes for
|
||||||
|
|
||||||
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]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slow topological sort. Uses a projection π to flatten bounding boxes for
|
|
||||||
// overlap tests, in order to reduce edge count.
|
// overlap tests, in order to reduce edge count.
|
||||||
func (d *drawList) topsort(π geom.Projector) {
|
func (d *drawList) topsort(π geom.Projector) {
|
||||||
// Produce edge lists and count indegrees - O(|V|^2)
|
// Produce edge lists and count indegrees - O(|V|^2)
|
||||||
|
@ -113,7 +103,7 @@ func (d *drawList) topsort(π geom.Projector) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the edge goes u->v, add it.
|
// If the edge goes u->v, add it.
|
||||||
if d.Less(i, j) {
|
if edge(u, v) {
|
||||||
edges[i] = append(edges[i], j)
|
edges[i] = append(edges[i], j)
|
||||||
indegree[j]++
|
indegree[j]++
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -17,7 +16,7 @@ import (
|
||||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const topologicalDrawSort = true
|
const showDrawListSize = true
|
||||||
|
|
||||||
var _ interface {
|
var _ interface {
|
||||||
Disabler
|
Disabler
|
||||||
|
@ -114,7 +113,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
d.Draw(screen, &st.opts)
|
d.Draw(screen, &st.opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if true {
|
if showDrawListSize {
|
||||||
// Infodump about draw list
|
// Infodump about draw list
|
||||||
ebitenutil.DebugPrintAt(screen, fmt.Sprintf("len(drawList.list) = %d", len(g.drawList.list)), 0, 30)
|
ebitenutil.DebugPrintAt(screen, fmt.Sprintf("len(drawList.list) = %d", len(g.drawList.list)), 0, 30)
|
||||||
ebitenutil.DebugPrintAt(screen, fmt.Sprintf("len(drawList.rev) = %d", len(g.drawList.list)), 0, 45)
|
ebitenutil.DebugPrintAt(screen, fmt.Sprintf("len(drawList.rev) = %d", len(g.drawList.list)), 0, 45)
|
||||||
|
@ -185,19 +184,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 {
|
|
||||||
g.drawList.topsort(g.Projection)
|
g.drawList.topsort(g.Projection)
|
||||||
} else {
|
|
||||||
sort.Stable(g.drawList)
|
|
||||||
|
|
||||||
// Truncate tombstones from the end.
|
|
||||||
for i := g.drawList.Len() - 1; i >= 0; i-- {
|
|
||||||
if g.drawList.list[i] != (tombstone{}) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
g.drawList.list = g.drawList.list[:i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,11 @@ type Disabler interface {
|
||||||
// passed to Game.Register or returned from Scan).
|
// passed to Game.Register or returned from Scan).
|
||||||
type Drawer interface {
|
type Drawer interface {
|
||||||
Draw(*ebiten.Image, *ebiten.DrawImageOptions)
|
Draw(*ebiten.Image, *ebiten.DrawImageOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DrawOrderer components have more specific ideas about draw ordering than
|
||||||
|
// merely "my Z is bigger than yours".
|
||||||
|
type DrawOrderer interface {
|
||||||
DrawAfter(Drawer) bool
|
DrawAfter(Drawer) bool
|
||||||
DrawBefore(Drawer) bool
|
DrawBefore(Drawer) bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,30 +40,8 @@ func (h *Hides) Hide() { *h = true }
|
||||||
// Show sets h to false.
|
// Show sets h to false.
|
||||||
func (h *Hides) Show() { *h = false }
|
func (h *Hides) Show() { *h = false }
|
||||||
|
|
||||||
// ZPosition implements DrawAfter and DrawBefore using only Z information.
|
// ZPosition implements ZPositioner directly (as an int).
|
||||||
type ZPosition int
|
type ZPosition int
|
||||||
|
|
||||||
// DrawAfter reports if z >= x.Max.Z.
|
|
||||||
func (z ZPosition) DrawAfter(x Drawer) bool {
|
|
||||||
switch x := x.(type) {
|
|
||||||
case BoundingBoxer:
|
|
||||||
return int(z) >= x.BoundingBox().Max.Z
|
|
||||||
case ZPositioner:
|
|
||||||
return z.ZPos() > x.ZPos()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// DrawBefore reports if z < x.Min.Z.
|
|
||||||
func (z ZPosition) DrawBefore(x Drawer) bool {
|
|
||||||
switch x := x.(type) {
|
|
||||||
case BoundingBoxer:
|
|
||||||
return int(z) < x.BoundingBox().Min.Z
|
|
||||||
case ZPositioner:
|
|
||||||
return z.ZPos() < x.ZPos()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ZPos returns itself.
|
// ZPos returns itself.
|
||||||
func (z ZPosition) ZPos() int { return int(z) }
|
func (z ZPosition) ZPos() int { return int(z) }
|
||||||
|
|
|
@ -158,20 +158,6 @@ func (p *Prism) DrawAfter(x Drawer) bool {
|
||||||
|
|
||||||
case BoundingBoxer:
|
case BoundingBoxer:
|
||||||
xb := x.BoundingBox()
|
xb := x.BoundingBox()
|
||||||
if !commonDrawerComparisons {
|
|
||||||
if pb.Max.Z <= xb.Min.Z { // p is behind x
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if pb.Min.Z >= xb.Max.Z { // p is in front of x
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if pb.Min.Y >= xb.Max.Y { // p is below x
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if pb.Max.Y <= xb.Min.Y { // p is above x
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The prism special
|
// The prism special
|
||||||
split := p.m.topext[geom.North].X
|
split := p.m.topext[geom.North].X
|
||||||
threshold := p.m.topext[geom.East].Y
|
threshold := p.m.topext[geom.East].Y
|
||||||
|
@ -184,9 +170,6 @@ func (p *Prism) DrawAfter(x Drawer) bool {
|
||||||
if pb.Min.Z+threshold >= xb.Max.Z { // x is behind the front half of p
|
if pb.Min.Z+threshold >= xb.Max.Z { // x is behind the front half of p
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
case ZPositioner:
|
|
||||||
return pb.Min.Z > x.ZPos() // p is after x
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -204,20 +187,6 @@ func (p *Prism) DrawBefore(x Drawer) bool {
|
||||||
|
|
||||||
case BoundingBoxer:
|
case BoundingBoxer:
|
||||||
xb := x.BoundingBox()
|
xb := x.BoundingBox()
|
||||||
if !commonDrawerComparisons {
|
|
||||||
if pb.Min.Z >= xb.Max.Z { // p is in front of x
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if pb.Max.Z <= xb.Min.Z { // p is behind x
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if pb.Max.Y <= xb.Min.Y { // p is above x
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if pb.Min.Y >= xb.Max.Y { // p is below x
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The prism special
|
// The prism special
|
||||||
split := p.m.topext[geom.North].X
|
split := p.m.topext[geom.North].X
|
||||||
threshold := p.m.topext[geom.East].Y
|
threshold := p.m.topext[geom.East].Y
|
||||||
|
@ -230,9 +199,6 @@ func (p *Prism) DrawBefore(x Drawer) bool {
|
||||||
if pb.Min.Z+threshold <= xb.Min.Z { // x is in front of the front half of p
|
if pb.Min.Z+threshold <= xb.Min.Z { // x is in front of the front half of p
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
case ZPositioner:
|
|
||||||
return pb.Max.Z < x.ZPos() // p is before x
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,58 +40,6 @@ func (s *Sprite) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
|
||||||
screen.DrawImage(s.Sheet.SubImage(s.anim.Cell()), opts)
|
screen.DrawImage(s.Sheet.SubImage(s.anim.Cell()), opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DrawAfter reports if the sprite should be drawn after x.
|
|
||||||
func (s *Sprite) DrawAfter(x Drawer) bool {
|
|
||||||
if !commonDrawerComparisons {
|
|
||||||
sb := s.BoundingBox()
|
|
||||||
switch x := x.(type) {
|
|
||||||
case BoundingBoxer:
|
|
||||||
xb := x.BoundingBox()
|
|
||||||
if sb.Max.Z <= xb.Min.Z { // s is behind x
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if sb.Min.Z >= xb.Max.Z { // s is in front of x
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if sb.Min.Y >= xb.Max.Y { // s is below x
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if sb.Max.Y <= xb.Min.Y { // s is above x
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
case ZPositioner:
|
|
||||||
return sb.Min.Z > x.ZPos() // s is after
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// DrawBefore reports if the sprite should be drawn before x.
|
|
||||||
func (s *Sprite) DrawBefore(x Drawer) bool {
|
|
||||||
if !commonDrawerComparisons {
|
|
||||||
sb := s.BoundingBox()
|
|
||||||
switch x := x.(type) {
|
|
||||||
case BoundingBoxer:
|
|
||||||
xb := x.BoundingBox()
|
|
||||||
if sb.Min.Z >= xb.Max.Z { // s is in front of x
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if sb.Max.Z <= xb.Min.Z { // s is behind x
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if sb.Max.Y <= xb.Min.Y { // s is above x
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if sb.Min.Y >= xb.Max.Y { // s is below x
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
case ZPositioner:
|
|
||||||
return sb.Max.Z < x.ZPos() // s is before
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan returns the Actor and the Sheet.
|
// Scan returns the Actor and the Sheet.
|
||||||
func (s *Sprite) Scan() []interface{} {
|
func (s *Sprite) Scan() []interface{} {
|
||||||
return []interface{}{
|
return []interface{}{
|
||||||
|
|
|
@ -11,6 +11,8 @@ import (
|
||||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const awakemanProducesBubbles = true
|
||||||
|
|
||||||
var _ interface {
|
var _ interface {
|
||||||
engine.Identifier
|
engine.Identifier
|
||||||
engine.Disabler
|
engine.Disabler
|
||||||
|
@ -109,7 +111,7 @@ func (aw *Awakeman) realUpdate() error {
|
||||||
bubblePeriod = 6
|
bubblePeriod = 6
|
||||||
)
|
)
|
||||||
|
|
||||||
if true {
|
if awakemanProducesBubbles {
|
||||||
// Add a bubble?
|
// Add a bubble?
|
||||||
aw.bubbleTimer--
|
aw.bubbleTimer--
|
||||||
if aw.bubbleTimer <= 0 {
|
if aw.bubbleTimer <= 0 {
|
||||||
|
|
Loading…
Reference in a new issue