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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
|
|
@ -479,18 +480,19 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
|||
|
||||
var bestLayout [][]*d2graph.Object
|
||||
bestDist := math.MaxFloat64
|
||||
|
||||
fastIsBest := false
|
||||
// try fast layout algorithm as a baseline
|
||||
bestLayout = gd.fastLayout(targetSize, nCuts, columns)
|
||||
if bestLayout != nil {
|
||||
dist := getDistToTarget(bestLayout, targetSize, float64(gd.horizontalGap), float64(gd.verticalGap), columns)
|
||||
if fastLayout := gd.fastLayout(targetSize, nCuts, columns); fastLayout != nil {
|
||||
dist := getDistToTarget(fastLayout, targetSize, float64(gd.horizontalGap), float64(gd.verticalGap), columns)
|
||||
if debug {
|
||||
fmt.Printf("fast dist %v dist per row %v\n", dist, dist/(float64(nCuts)+1))
|
||||
}
|
||||
if dist == 0 {
|
||||
return bestLayout
|
||||
return fastLayout
|
||||
}
|
||||
bestDist = dist
|
||||
bestLayout = fastLayout
|
||||
fastIsBest = true
|
||||
}
|
||||
|
||||
var gap float64
|
||||
|
|
@ -515,22 +517,16 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
|||
sd := stddev(sizes)
|
||||
if debug {
|
||||
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
|
||||
count := 0
|
||||
attemptLimit := 100_000
|
||||
skipLimit := 100 * attemptLimit
|
||||
// quickly eliminate bad row groupings
|
||||
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,
|
||||
// but the best option may require a few rows that are far from the target size.
|
||||
okThreshold := 1.2
|
||||
// if we don't find a layout try 25% larger threshold
|
||||
thresholdStep := 0.25
|
||||
okThreshold := STARTING_THRESHOLD
|
||||
rowOk := func(row []*d2graph.Object, starting bool) (ok bool) {
|
||||
if starting {
|
||||
// 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 rowSize > okThreshold*targetSize {
|
||||
skipCount++
|
||||
if skipCount >= skipLimit {
|
||||
if skipCount >= SKIP_LIMIT {
|
||||
// there may even be too many to skip
|
||||
return true
|
||||
}
|
||||
|
|
@ -563,7 +559,7 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
|||
// row is too small to be good overall
|
||||
if rowSize < targetSize/okThreshold {
|
||||
skipCount++
|
||||
if skipCount >= skipLimit {
|
||||
if skipCount >= SKIP_LIMIT {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -586,19 +582,29 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
|||
if dist < bestDist {
|
||||
bestLayout = layout
|
||||
bestDist = dist
|
||||
fastIsBest = false
|
||||
} else if fastIsBest && dist == bestDist {
|
||||
// prefer ordered search solution to fast layout solution
|
||||
bestLayout = layout
|
||||
fastIsBest = false
|
||||
}
|
||||
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
|
||||
return count >= attemptLimit || skipCount >= skipLimit
|
||||
return count >= ATTEMPT_LIMIT || skipCount >= SKIP_LIMIT
|
||||
}
|
||||
|
||||
// try number of different okThresholds depending on std deviation of sizes
|
||||
thresholds := int(math.Min(math.Max(1, math.Ceil(sd)), 3))
|
||||
for i := 0; i < thresholds || bestLayout == nil; i++ {
|
||||
thresholdAttempts := int(math.Ceil(sd))
|
||||
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.
|
||||
skipCount = 0.
|
||||
iterDivisions(gd.objects, nCuts, tryDivision, rowOk)
|
||||
okThreshold += thresholdStep
|
||||
okThreshold += THRESHOLD_STEP_SIZE
|
||||
if debug {
|
||||
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
|
||||
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 {
|
||||
i := 0
|
||||
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")
|
||||
fmt.Printf("best layout: %v\n", layoutString(bestLayout, sizes))
|
||||
}
|
||||
return bestLayout
|
||||
}
|
||||
|
|
@ -695,6 +698,19 @@ func (gd *gridDiagram) fastLayout(targetSize float64, nCuts int, columns bool) (
|
|||
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
|
||||
type iterDivision func(division []int) (done bool)
|
||||
type checkCut func(objects []*d2graph.Object, starting bool) (ok bool)
|
||||
|
|
|
|||
Loading…
Reference in a new issue