cleanup
This commit is contained in:
parent
595cdd4b0b
commit
f64588ff89
2 changed files with 56 additions and 27 deletions
13
d2layouts/d2grid/constants.go
Normal file
13
d2layouts/d2grid/constants.go
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
package d2grid
|
||||||
|
|
||||||
|
const (
|
||||||
|
// don't consider layouts with rows longer than targetSize*1.2 or shorter than targetSize/1.2
|
||||||
|
STARTING_THRESHOLD = 1.2
|
||||||
|
// next try layouts with a 25% larger threshold
|
||||||
|
THRESHOLD_STEP_SIZE = 0.25
|
||||||
|
MIN_THRESHOLD_ATTEMPTS = 1
|
||||||
|
MAX_THRESHOLD_ATTEMPTS = 3
|
||||||
|
|
||||||
|
ATTEMPT_LIMIT = 100_000
|
||||||
|
SKIP_LIMIT = 10_000_000
|
||||||
|
)
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package d2grid
|
package d2grid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
|
@ -479,18 +480,19 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
||||||
|
|
||||||
var bestLayout [][]*d2graph.Object
|
var bestLayout [][]*d2graph.Object
|
||||||
bestDist := math.MaxFloat64
|
bestDist := math.MaxFloat64
|
||||||
|
fastIsBest := false
|
||||||
// try fast layout algorithm as a baseline
|
// try fast layout algorithm as a baseline
|
||||||
bestLayout = gd.fastLayout(targetSize, nCuts, columns)
|
if fastLayout := gd.fastLayout(targetSize, nCuts, columns); fastLayout != nil {
|
||||||
if bestLayout != nil {
|
dist := getDistToTarget(fastLayout, targetSize, float64(gd.horizontalGap), float64(gd.verticalGap), columns)
|
||||||
dist := getDistToTarget(bestLayout, targetSize, float64(gd.horizontalGap), float64(gd.verticalGap), columns)
|
|
||||||
if debug {
|
if debug {
|
||||||
fmt.Printf("fast dist %v dist per row %v\n", dist, dist/(float64(nCuts)+1))
|
fmt.Printf("fast dist %v dist per row %v\n", dist, dist/(float64(nCuts)+1))
|
||||||
}
|
}
|
||||||
if dist == 0 {
|
if dist == 0 {
|
||||||
return bestLayout
|
return fastLayout
|
||||||
}
|
}
|
||||||
bestDist = dist
|
bestDist = dist
|
||||||
|
bestLayout = fastLayout
|
||||||
|
fastIsBest = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var gap float64
|
var gap float64
|
||||||
|
|
@ -515,22 +517,16 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
||||||
sd := stddev(sizes)
|
sd := stddev(sizes)
|
||||||
if debug {
|
if debug {
|
||||||
fmt.Printf("sizes (%d): %v\n", len(sizes), sizes)
|
fmt.Printf("sizes (%d): %v\n", len(sizes), sizes)
|
||||||
fmt.Printf("std dev: %v\n", sd)
|
fmt.Printf("std dev: %v; targetSize %v\n", sd, targetSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
skipCount := 0
|
skipCount := 0
|
||||||
count := 0
|
count := 0
|
||||||
attemptLimit := 100_000
|
|
||||||
skipLimit := 100 * attemptLimit
|
|
||||||
// quickly eliminate bad row groupings
|
// quickly eliminate bad row groupings
|
||||||
startingCache := make(map[int]bool)
|
startingCache := make(map[int]bool)
|
||||||
// try to find a layout with all rows within 1.2*targetSize
|
|
||||||
// skip options with a row that is 1.2*longer or shorter
|
|
||||||
// Note: we want a low threshold to explore good options within attemptLimit,
|
// Note: we want a low threshold to explore good options within attemptLimit,
|
||||||
// but the best option may require a few rows that are far from the target size.
|
// but the best option may require a few rows that are far from the target size.
|
||||||
okThreshold := 1.2
|
okThreshold := STARTING_THRESHOLD
|
||||||
// if we don't find a layout try 25% larger threshold
|
|
||||||
thresholdStep := 0.25
|
|
||||||
rowOk := func(row []*d2graph.Object, starting bool) (ok bool) {
|
rowOk := func(row []*d2graph.Object, starting bool) (ok bool) {
|
||||||
if starting {
|
if starting {
|
||||||
// we can cache results from starting positions since they repeat and don't change
|
// we can cache results from starting positions since they repeat and don't change
|
||||||
|
|
@ -553,7 +549,7 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
||||||
// if multiple nodes are too big, it isn't ok. but a single node can't shrink so only check here
|
// if multiple nodes are too big, it isn't ok. but a single node can't shrink so only check here
|
||||||
if rowSize > okThreshold*targetSize {
|
if rowSize > okThreshold*targetSize {
|
||||||
skipCount++
|
skipCount++
|
||||||
if skipCount >= skipLimit {
|
if skipCount >= SKIP_LIMIT {
|
||||||
// there may even be too many to skip
|
// there may even be too many to skip
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -563,7 +559,7 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
||||||
// row is too small to be good overall
|
// row is too small to be good overall
|
||||||
if rowSize < targetSize/okThreshold {
|
if rowSize < targetSize/okThreshold {
|
||||||
skipCount++
|
skipCount++
|
||||||
if skipCount >= skipLimit {
|
if skipCount >= SKIP_LIMIT {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
@ -586,19 +582,29 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
||||||
if dist < bestDist {
|
if dist < bestDist {
|
||||||
bestLayout = layout
|
bestLayout = layout
|
||||||
bestDist = dist
|
bestDist = dist
|
||||||
|
fastIsBest = false
|
||||||
|
} else if fastIsBest && dist == bestDist {
|
||||||
|
// prefer ordered search solution to fast layout solution
|
||||||
|
bestLayout = layout
|
||||||
|
fastIsBest = false
|
||||||
}
|
}
|
||||||
count++
|
count++
|
||||||
// with few objects we can try all options to get best result but this won't scale, so only try up to 100k options
|
// with few objects we can try all options to get best result but this won't scale, so only try up to 100k options
|
||||||
return count >= attemptLimit || skipCount >= skipLimit
|
return count >= ATTEMPT_LIMIT || skipCount >= SKIP_LIMIT
|
||||||
}
|
}
|
||||||
|
|
||||||
// try number of different okThresholds depending on std deviation of sizes
|
// try number of different okThresholds depending on std deviation of sizes
|
||||||
thresholds := int(math.Min(math.Max(1, math.Ceil(sd)), 3))
|
thresholdAttempts := int(math.Ceil(sd))
|
||||||
for i := 0; i < thresholds || bestLayout == nil; i++ {
|
if thresholdAttempts < MIN_THRESHOLD_ATTEMPTS {
|
||||||
|
thresholdAttempts = MIN_THRESHOLD_ATTEMPTS
|
||||||
|
} else if thresholdAttempts > MAX_THRESHOLD_ATTEMPTS {
|
||||||
|
thresholdAttempts = MAX_THRESHOLD_ATTEMPTS
|
||||||
|
}
|
||||||
|
for i := 0; i < thresholdAttempts || bestLayout == nil; i++ {
|
||||||
count = 0.
|
count = 0.
|
||||||
skipCount = 0.
|
skipCount = 0.
|
||||||
iterDivisions(gd.objects, nCuts, tryDivision, rowOk)
|
iterDivisions(gd.objects, nCuts, tryDivision, rowOk)
|
||||||
okThreshold += thresholdStep
|
okThreshold += THRESHOLD_STEP_SIZE
|
||||||
if debug {
|
if debug {
|
||||||
fmt.Printf("count %d, skip count %d, bestDist %v increasing ok threshold to %v\n", count, skipCount, bestDist, okThreshold)
|
fmt.Printf("count %d, skip count %d, bestDist %v increasing ok threshold to %v\n", count, skipCount, bestDist, okThreshold)
|
||||||
}
|
}
|
||||||
|
|
@ -607,17 +613,14 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
||||||
// threshold isn't skipping anything so increasing it won't help
|
// threshold isn't skipping anything so increasing it won't help
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
// okThreshold isn't high enough yet, we skipped every option so don't count it
|
||||||
|
if count == 0 && thresholdAttempts < MAX_THRESHOLD_ATTEMPTS {
|
||||||
|
thresholdAttempts++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
i := 0
|
fmt.Printf("best layout: %v\n", layoutString(bestLayout, sizes))
|
||||||
fmt.Printf("best layout: [\n")
|
|
||||||
for _, r := range bestLayout {
|
|
||||||
vals := sizes[i : i+len(r)]
|
|
||||||
fmt.Printf("%v \t=%v\n", vals, sum(vals))
|
|
||||||
i += len(r)
|
|
||||||
}
|
|
||||||
fmt.Printf("]\n")
|
|
||||||
}
|
}
|
||||||
return bestLayout
|
return bestLayout
|
||||||
}
|
}
|
||||||
|
|
@ -695,6 +698,19 @@ func (gd *gridDiagram) fastLayout(targetSize float64, nCuts int, columns bool) (
|
||||||
return layout
|
return layout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func layoutString(layout [][]*d2graph.Object, sizes []float64) string {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
i := 0
|
||||||
|
fmt.Fprintf(buf, "[\n")
|
||||||
|
for _, r := range layout {
|
||||||
|
vals := sizes[i : i+len(r)]
|
||||||
|
fmt.Fprintf(buf, "%v:\t%v\n", sum(vals), vals)
|
||||||
|
i += len(r)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(buf, "]\n")
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
// process current division, return true to stop iterating
|
// process current division, return true to stop iterating
|
||||||
type iterDivision func(division []int) (done bool)
|
type iterDivision func(division []int) (done bool)
|
||||||
type checkCut func(objects []*d2graph.Object, starting bool) (ok bool)
|
type checkCut func(objects []*d2graph.Object, starting bool) (ok bool)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue