change dag implementation

This commit is contained in:
Josh Deprez 2021-09-24 10:32:31 +10:00
parent 9c8417780a
commit 054d3bc5e5

View file

@ -35,7 +35,7 @@ type DrawDAG struct {
Child interface{} Child interface{}
Hides Hides
*dag dag
boxCache map[DrawBoxer]geom.Box // used to find components that moved boxCache map[DrawBoxer]geom.Box // used to find components that moved
chunks map[image.Point]drawerSet // chunk coord -> drawers with bounding rects intersecting chunk chunks map[image.Point]drawerSet // chunk coord -> drawers with bounding rects intersecting chunk
chunksRev map[DrawBoxer]image.Rectangle // comopnent -> rectangle of chunk coords chunksRev map[DrawBoxer]image.Rectangle // comopnent -> rectangle of chunk coords
@ -108,7 +108,7 @@ func (DrawDAG) ManagesDrawingSubcomponents() {}
// Prepare adds all subcomponents to the DAG. // Prepare adds all subcomponents to the DAG.
func (d *DrawDAG) Prepare(game *Game) error { func (d *DrawDAG) Prepare(game *Game) error {
d.dag = newDAG() d.dag = make(dag)
d.boxCache = make(map[DrawBoxer]geom.Box) d.boxCache = make(map[DrawBoxer]geom.Box)
d.chunks = make(map[image.Point]drawerSet) d.chunks = make(map[image.Point]drawerSet)
d.chunksRev = make(map[DrawBoxer]image.Rectangle) d.chunksRev = make(map[DrawBoxer]image.Rectangle)
@ -319,76 +319,70 @@ func (s drawerSet) String() string {
return sb.String() return sb.String()
} }
type dag struct { type edges struct {
all drawerSet in, out drawerSet
in, out map[Drawer]drawerSet
} }
func newDAG() *dag { type dag map[Drawer]edges
return &dag{
all: make(drawerSet),
in: make(map[Drawer]drawerSet),
out: make(map[Drawer]drawerSet),
}
}
// Dot returns a dot-syntax-like description of the graph. // Dot returns a dot-syntax-like description of the graph.
func (d *dag) Dot() string { func (d dag) Dot() string {
var sb strings.Builder var sb strings.Builder
sb.WriteString("digraph {\n") sb.WriteString("digraph {\n")
for v, e := range d.out { for v, e := range d {
fmt.Fprintf(&sb, "%v -> %v\n", v, e) fmt.Fprintf(&sb, "%v -> %v\n", v, e.out)
} }
sb.WriteString(" }\n") sb.WriteString(" }\n")
return sb.String() return sb.String()
} }
// addEdge adds the edge u-v in O(1). // addEdge adds the edge u-v in O(1).
func (d *dag) addEdge(u, v Drawer) { func (d dag) addEdge(u, v Drawer) {
d.all[u], d.all[v] = struct{}{}, struct{}{} d.addVertex(u)
if d.in[v] == nil { d.addVertex(v)
d.in[v] = make(drawerSet) d[v].in[u] = struct{}{}
} d[u].out[v] = struct{}{}
if d.out[u] == nil {
d.out[u] = make(drawerSet)
}
d.in[v][u] = struct{}{}
d.out[u][v] = struct{}{}
} }
// removeEdge removes the edge u-v in O(1). // removeEdge removes the edge u-v in O(1).
func (d *dag) removeEdge(u, v Drawer) { func (d dag) removeEdge(u, v Drawer) {
delete(d.in[v], u) delete(d[v].in, u)
delete(d.out[u], v) delete(d[u].out, v)
} }
// addVertex ensures the vertex is present, even if there are no edges. // addVertex ensures the vertex is present, even if there are no edges.
func (d *dag) addVertex(v Drawer) { func (d dag) addVertex(v Drawer) {
d.all[v] = struct{}{} if _, found := d[v]; found {
return
}
d[v] = edges{
in: make(drawerSet),
out: make(drawerSet),
}
} }
// removeVertex removes all in and out edges associated with v in O(degree(v)). // removeVertex removes all in and out edges associated with v in O(degree(v)).
func (d *dag) removeVertex(v Drawer) { func (d dag) removeVertex(v Drawer) {
for u := range d.in[v] { for u := range d[v].in {
d.removeEdge(u, v) d.removeEdge(u, v)
} }
for w := range d.out[v] { for w := range d[v].out {
d.removeEdge(v, w) d.removeEdge(v, w)
} }
delete(d.all, v) delete(d, v)
} }
// topWalk visits each vertex in topological order, in time O(|V| + |E|) and // topWalk visits each vertex in topological order, in time O(|V| + |E|) and
// O(|V|) temporary memory (for acyclic graphs) and a bit longer if it has to // O(|V|) temporary memory (for acyclic graphs) and a bit longer if it has to
// break cycles. // break cycles.
func (d *dag) topWalk(visit func(Drawer)) { func (d dag) topWalk(visit func(Drawer)) {
// Count indegrees - indegree(v) = len(d.in[v]) for each vertex v. // Count indegrees - indegree(v) = len(d.in[v]) for each vertex v.
// If indegree(v) = 0, enqueue. Total: O(|V|). // If indegree(v) = 0, enqueue. Total: O(|V|).
queue := make([]Drawer, 0, len(d.in)) queue := make([]Drawer, 0, len(d))
indegree := make(map[Drawer]int) indegree := make(map[Drawer]int)
for u := range d.all { for u := range d {
// NB: zero indegree vertices may be missing from d.in // NB: zero indegree vertices may be missing from d.in
e := d.in[u] e := d[u].in
if len(e) == 0 { if len(e) == 0 {
queue = append(queue, u) queue = append(queue, u)
} else { } else {
@ -421,7 +415,7 @@ func (d *dag) topWalk(visit func(Drawer)) {
// Decrement indegree for all out edges, and enqueue target if its // Decrement indegree for all out edges, and enqueue target if its
// indegree is now 0. // indegree is now 0.
for v := range d.out[u] { for v := range d[u].out {
if _, ready := indegree[v]; !ready { if _, ready := indegree[v]; !ready {
// Vertex already drawn. This happens if there was a cycle. // Vertex already drawn. This happens if there was a cycle.
continue continue