Merge pull request #1315 from gavin-ts/grid-adjustments
improve grid performance with similarly sized objects
This commit is contained in:
commit
c6820c89cc
9 changed files with 39668 additions and 28 deletions
|
|
@ -4,5 +4,6 @@
|
||||||
|
|
||||||
- Use shape specific sizing for grid containers [#1294](https://github.com/terrastruct/d2/pull/1294)
|
- Use shape specific sizing for grid containers [#1294](https://github.com/terrastruct/d2/pull/1294)
|
||||||
- Watch mode browser uses an error favicon to easily indicate compiler errors. Thanks @sinyo-matu ! [#1240](https://github.com/terrastruct/d2/pull/1240)
|
- Watch mode browser uses an error favicon to easily indicate compiler errors. Thanks @sinyo-matu ! [#1240](https://github.com/terrastruct/d2/pull/1240)
|
||||||
|
- Improves grid layout performance when there are many similarly sized shapes. [#1315](https://github.com/terrastruct/d2/pull/1315)
|
||||||
|
|
||||||
#### Bugfixes ⛑️
|
#### Bugfixes ⛑️
|
||||||
|
|
|
||||||
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"
|
||||||
|
|
@ -466,6 +467,7 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
|
||||||
// generate the best layout of objects aiming for each row to be the targetSize width
|
// generate the best layout of objects aiming for each row to be the targetSize width
|
||||||
// if columns is true, each column aims to have the targetSize height
|
// if columns is true, each column aims to have the targetSize height
|
||||||
func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2graph.Object {
|
func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2graph.Object {
|
||||||
|
debug := false
|
||||||
var nCuts int
|
var nCuts int
|
||||||
if columns {
|
if columns {
|
||||||
nCuts = gd.columns - 1
|
nCuts = gd.columns - 1
|
||||||
|
|
@ -476,6 +478,23 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
||||||
return genLayout(gd.objects, nil)
|
return genLayout(gd.objects, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var bestLayout [][]*d2graph.Object
|
||||||
|
bestDist := math.MaxFloat64
|
||||||
|
fastIsBest := false
|
||||||
|
// try fast layout algorithm as a baseline
|
||||||
|
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 fastLayout
|
||||||
|
}
|
||||||
|
bestDist = dist
|
||||||
|
bestLayout = fastLayout
|
||||||
|
fastIsBest = true
|
||||||
|
}
|
||||||
|
|
||||||
var gap float64
|
var gap float64
|
||||||
if columns {
|
if columns {
|
||||||
gap = float64(gd.verticalGap)
|
gap = float64(gd.verticalGap)
|
||||||
|
|
@ -490,17 +509,24 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug := false
|
sizes := []float64{}
|
||||||
|
for _, obj := range gd.objects {
|
||||||
|
size := getSize(obj)
|
||||||
|
sizes = append(sizes, size)
|
||||||
|
}
|
||||||
|
sd := stddev(sizes)
|
||||||
|
if debug {
|
||||||
|
fmt.Printf("sizes (%d): %v\n", len(sizes), sizes)
|
||||||
|
fmt.Printf("std dev: %v; targetSize %v\n", sd, targetSize)
|
||||||
|
}
|
||||||
|
|
||||||
skipCount := 0
|
skipCount := 0
|
||||||
|
count := 0
|
||||||
// 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
|
||||||
|
|
@ -523,21 +549,24 @@ 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 >= SKIP_LIMIT {
|
||||||
|
// there may even be too many to skip
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 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 >= SKIP_LIMIT {
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
var bestLayout [][]*d2graph.Object
|
|
||||||
bestDist := math.MaxFloat64
|
|
||||||
count := 0
|
|
||||||
attemptLimit := 100_000
|
|
||||||
// get all options for where to place these cuts, preferring later cuts over earlier cuts
|
// get all options for where to place these cuts, preferring later cuts over earlier cuts
|
||||||
// with 5 objects and 2 cuts we have these options:
|
// with 5 objects and 2 cuts we have these options:
|
||||||
// . A B C │ D │ E <- these cuts would produce: ┌A─┐ ┌B─┐ ┌C─┐
|
// . A B C │ D │ E <- these cuts would produce: ┌A─┐ ┌B─┐ ┌C─┐
|
||||||
|
|
@ -553,33 +582,94 @@ 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
|
return count >= ATTEMPT_LIMIT || skipCount >= SKIP_LIMIT
|
||||||
}
|
}
|
||||||
|
|
||||||
// try at least 3 different okThresholds
|
// try number of different okThresholds depending on std deviation of sizes
|
||||||
for i := 0; i < 3 || 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)
|
iterDivisions(gd.objects, nCuts, tryDivision, rowOk)
|
||||||
okThreshold += thresholdStep
|
okThreshold += THRESHOLD_STEP_SIZE
|
||||||
if debug {
|
if debug {
|
||||||
fmt.Printf("increasing ok threshold to %v\n", okThreshold)
|
fmt.Printf("count %d, skip count %d, bestDist %v increasing ok threshold to %v\n", count, skipCount, bestDist, okThreshold)
|
||||||
}
|
}
|
||||||
startingCache = make(map[int]bool)
|
startingCache = make(map[int]bool)
|
||||||
count = 0.
|
if skipCount == 0 {
|
||||||
|
// 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 {
|
|
||||||
fmt.Printf("final count %d, skip count %d\n", count, skipCount)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// try fast layout algorithm, see if it is better than first 1mil attempts
|
if debug {
|
||||||
|
fmt.Printf("best layout: %v\n", layoutString(bestLayout, sizes))
|
||||||
|
}
|
||||||
|
return bestLayout
|
||||||
|
}
|
||||||
|
|
||||||
|
func sum(values []float64) float64 {
|
||||||
|
s := 0.
|
||||||
|
for _, v := range values {
|
||||||
|
s += v
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func avg(values []float64) float64 {
|
||||||
|
return sum(values) / float64(len(values))
|
||||||
|
}
|
||||||
|
|
||||||
|
func variance(values []float64) float64 {
|
||||||
|
mean := avg(values)
|
||||||
|
total := 0.
|
||||||
|
for _, value := range values {
|
||||||
|
dev := mean - value
|
||||||
|
total += dev * dev
|
||||||
|
}
|
||||||
|
return total / float64(len(values))
|
||||||
|
}
|
||||||
|
|
||||||
|
func stddev(values []float64) float64 {
|
||||||
|
return math.Sqrt(variance(values))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gd *gridDiagram) fastLayout(targetSize float64, nCuts int, columns bool) (layout [][]*d2graph.Object) {
|
||||||
|
var gap float64
|
||||||
|
if columns {
|
||||||
|
gap = float64(gd.verticalGap)
|
||||||
|
} else {
|
||||||
|
gap = float64(gd.horizontalGap)
|
||||||
|
}
|
||||||
|
|
||||||
debt := 0.
|
debt := 0.
|
||||||
fastDivision := make([]int, 0, nCuts)
|
fastDivision := make([]int, 0, nCuts)
|
||||||
rowSize := 0.
|
rowSize := 0.
|
||||||
for i := 0; i < len(gd.objects); i++ {
|
for i := 0; i < len(gd.objects); i++ {
|
||||||
o := gd.objects[i]
|
o := gd.objects[i]
|
||||||
size := getSize(o)
|
var size float64
|
||||||
|
if columns {
|
||||||
|
size = o.Height
|
||||||
|
} else {
|
||||||
|
size = o.Width
|
||||||
|
}
|
||||||
if rowSize == 0 {
|
if rowSize == 0 {
|
||||||
if size > targetSize-debt {
|
if size > targetSize-debt {
|
||||||
fastDivision = append(fastDivision, i-1)
|
fastDivision = append(fastDivision, i-1)
|
||||||
|
|
@ -602,14 +692,23 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(fastDivision) == nCuts {
|
if len(fastDivision) == nCuts {
|
||||||
layout := genLayout(gd.objects, fastDivision)
|
layout = genLayout(gd.objects, fastDivision)
|
||||||
dist := getDistToTarget(layout, targetSize, float64(gd.horizontalGap), float64(gd.verticalGap), columns)
|
|
||||||
if dist < bestDist {
|
|
||||||
bestLayout = layout
|
|
||||||
bestDist = dist
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
return bestLayout
|
fmt.Fprintf(buf, "]\n")
|
||||||
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// process current division, return true to stop iterating
|
// process current division, return true to stop iterating
|
||||||
|
|
|
||||||
|
|
@ -2720,6 +2720,7 @@ scenarios: {
|
||||||
loadFromFile(t, "grid_even"),
|
loadFromFile(t, "grid_even"),
|
||||||
loadFromFile(t, "ent2d2_basic"),
|
loadFromFile(t, "ent2d2_basic"),
|
||||||
loadFromFile(t, "ent2d2_right"),
|
loadFromFile(t, "ent2d2_right"),
|
||||||
|
loadFromFile(t, "grid_large_checkered"),
|
||||||
}
|
}
|
||||||
|
|
||||||
runa(t, tcs)
|
runa(t, tcs)
|
||||||
|
|
|
||||||
446
e2etests/testdata/files/grid_large_checkered.d2
vendored
Normal file
446
e2etests/testdata/files/grid_large_checkered.d2
vendored
Normal file
|
|
@ -0,0 +1,446 @@
|
||||||
|
classes.BLACK: {style.fill: black; style.stroke-width: 0}
|
||||||
|
classes.WHITE: {style.fill: white; style.stroke-width: 0}
|
||||||
|
# grid-rows: 21
|
||||||
|
grid-columns: 21
|
||||||
|
grid-gap: 0
|
||||||
|
1: "" {class: BLACK}
|
||||||
|
2: "" {class: WHITE}
|
||||||
|
3: "" {class: BLACK}
|
||||||
|
4: "" {class: WHITE}
|
||||||
|
5: "" {class: BLACK}
|
||||||
|
6: "" {class: WHITE}
|
||||||
|
7: "" {class: BLACK}
|
||||||
|
8: "" {class: WHITE}
|
||||||
|
9: "" {class: BLACK}
|
||||||
|
10: "" {class: WHITE}
|
||||||
|
11: "" {class: BLACK}
|
||||||
|
12: "" {class: WHITE}
|
||||||
|
13: "" {class: BLACK}
|
||||||
|
14: "" {class: WHITE}
|
||||||
|
15: "" {class: BLACK}
|
||||||
|
16: "" {class: WHITE}
|
||||||
|
17: "" {class: BLACK}
|
||||||
|
18: "" {class: WHITE}
|
||||||
|
19: "" {class: BLACK}
|
||||||
|
20: "" {class: WHITE}
|
||||||
|
21: "" {class: BLACK}
|
||||||
|
22: "" {class: WHITE}
|
||||||
|
23: "" {class: BLACK}
|
||||||
|
24: "" {class: WHITE}
|
||||||
|
25: "" {class: BLACK}
|
||||||
|
26: "" {class: WHITE}
|
||||||
|
27: "" {class: BLACK}
|
||||||
|
28: "" {class: WHITE}
|
||||||
|
29: "" {class: BLACK}
|
||||||
|
30: "" {class: WHITE}
|
||||||
|
31: "" {class: BLACK}
|
||||||
|
32: "" {class: WHITE}
|
||||||
|
33: "" {class: BLACK}
|
||||||
|
34: "" {class: WHITE}
|
||||||
|
35: "" {class: BLACK}
|
||||||
|
36: "" {class: WHITE}
|
||||||
|
37: "" {class: BLACK}
|
||||||
|
38: "" {class: WHITE}
|
||||||
|
39: "" {class: BLACK}
|
||||||
|
40: "" {class: WHITE}
|
||||||
|
41: "" {class: BLACK}
|
||||||
|
42: "" {class: WHITE}
|
||||||
|
43: "" {class: BLACK}
|
||||||
|
44: "" {class: WHITE}
|
||||||
|
45: "" {class: BLACK}
|
||||||
|
46: "" {class: WHITE}
|
||||||
|
47: "" {class: BLACK}
|
||||||
|
48: "" {class: WHITE}
|
||||||
|
49: "" {class: BLACK}
|
||||||
|
50: "" {class: WHITE}
|
||||||
|
51: "" {class: BLACK}
|
||||||
|
52: "" {class: WHITE}
|
||||||
|
53: "" {class: BLACK}
|
||||||
|
54: "" {class: WHITE}
|
||||||
|
55: "" {class: BLACK}
|
||||||
|
56: "" {class: WHITE}
|
||||||
|
57: "" {class: BLACK}
|
||||||
|
58: "" {class: WHITE}
|
||||||
|
59: "" {class: BLACK}
|
||||||
|
60: "" {class: WHITE}
|
||||||
|
61: "" {class: BLACK}
|
||||||
|
62: "" {class: WHITE}
|
||||||
|
63: "" {class: BLACK}
|
||||||
|
64: "" {class: WHITE}
|
||||||
|
65: "" {class: BLACK}
|
||||||
|
66: "" {class: WHITE}
|
||||||
|
67: "" {class: BLACK}
|
||||||
|
68: "" {class: WHITE}
|
||||||
|
69: "" {class: BLACK}
|
||||||
|
70: "" {class: WHITE}
|
||||||
|
71: "" {class: BLACK}
|
||||||
|
72: "" {class: WHITE}
|
||||||
|
73: "" {class: BLACK}
|
||||||
|
74: "" {class: WHITE}
|
||||||
|
75: "" {class: BLACK}
|
||||||
|
76: "" {class: WHITE}
|
||||||
|
77: "" {class: BLACK}
|
||||||
|
78: "" {class: WHITE}
|
||||||
|
79: "" {class: BLACK}
|
||||||
|
80: "" {class: WHITE}
|
||||||
|
81: "" {class: BLACK}
|
||||||
|
82: "" {class: WHITE}
|
||||||
|
83: "" {class: BLACK}
|
||||||
|
84: "" {class: WHITE}
|
||||||
|
85: "" {class: BLACK}
|
||||||
|
86: "" {class: WHITE}
|
||||||
|
87: "" {class: BLACK}
|
||||||
|
88: "" {class: WHITE}
|
||||||
|
89: "" {class: BLACK}
|
||||||
|
90: "" {class: WHITE}
|
||||||
|
91: "" {class: BLACK}
|
||||||
|
92: "" {class: WHITE}
|
||||||
|
93: "" {class: BLACK}
|
||||||
|
94: "" {class: WHITE}
|
||||||
|
95: "" {class: BLACK}
|
||||||
|
96: "" {class: WHITE}
|
||||||
|
97: "" {class: BLACK}
|
||||||
|
98: "" {class: WHITE}
|
||||||
|
99: "" {class: BLACK}
|
||||||
|
100: "" {class: WHITE}
|
||||||
|
101: "" {class: BLACK}
|
||||||
|
102: "" {class: WHITE}
|
||||||
|
103: "" {class: BLACK}
|
||||||
|
104: "" {class: WHITE}
|
||||||
|
105: "" {class: BLACK}
|
||||||
|
106: "" {class: WHITE}
|
||||||
|
107: "" {class: BLACK}
|
||||||
|
108: "" {class: WHITE}
|
||||||
|
109: "" {class: BLACK}
|
||||||
|
110: "" {class: WHITE}
|
||||||
|
111: "" {class: BLACK}
|
||||||
|
112: "" {class: WHITE}
|
||||||
|
113: "" {class: BLACK}
|
||||||
|
114: "" {class: WHITE}
|
||||||
|
115: "" {class: BLACK}
|
||||||
|
116: "" {class: WHITE}
|
||||||
|
117: "" {class: BLACK}
|
||||||
|
118: "" {class: WHITE}
|
||||||
|
119: "" {class: BLACK}
|
||||||
|
120: "" {class: WHITE}
|
||||||
|
121: "" {class: BLACK}
|
||||||
|
122: "" {class: WHITE}
|
||||||
|
123: "" {class: BLACK}
|
||||||
|
124: "" {class: WHITE}
|
||||||
|
125: "" {class: BLACK}
|
||||||
|
126: "" {class: WHITE}
|
||||||
|
127: "" {class: BLACK}
|
||||||
|
128: "" {class: WHITE}
|
||||||
|
129: "" {class: BLACK}
|
||||||
|
130: "" {class: WHITE}
|
||||||
|
131: "" {class: BLACK}
|
||||||
|
132: "" {class: WHITE}
|
||||||
|
133: "" {class: BLACK}
|
||||||
|
134: "" {class: WHITE}
|
||||||
|
135: "" {class: BLACK}
|
||||||
|
136: "" {class: WHITE}
|
||||||
|
137: "" {class: BLACK}
|
||||||
|
138: "" {class: WHITE}
|
||||||
|
139: "" {class: BLACK}
|
||||||
|
140: "" {class: WHITE}
|
||||||
|
141: "" {class: BLACK}
|
||||||
|
142: "" {class: WHITE}
|
||||||
|
143: "" {class: BLACK}
|
||||||
|
144: "" {class: WHITE}
|
||||||
|
145: "" {class: BLACK}
|
||||||
|
146: "" {class: WHITE}
|
||||||
|
147: "" {class: BLACK}
|
||||||
|
148: "" {class: WHITE}
|
||||||
|
149: "" {class: BLACK}
|
||||||
|
150: "" {class: WHITE}
|
||||||
|
151: "" {class: BLACK}
|
||||||
|
152: "" {class: WHITE}
|
||||||
|
153: "" {class: BLACK}
|
||||||
|
154: "" {class: WHITE}
|
||||||
|
155: "" {class: BLACK}
|
||||||
|
156: "" {class: WHITE}
|
||||||
|
157: "" {class: BLACK}
|
||||||
|
158: "" {class: WHITE}
|
||||||
|
159: "" {class: BLACK}
|
||||||
|
160: "" {class: WHITE}
|
||||||
|
161: "" {class: BLACK}
|
||||||
|
162: "" {class: WHITE}
|
||||||
|
163: "" {class: BLACK}
|
||||||
|
164: "" {class: WHITE}
|
||||||
|
165: "" {class: BLACK}
|
||||||
|
166: "" {class: WHITE}
|
||||||
|
167: "" {class: BLACK}
|
||||||
|
168: "" {class: WHITE}
|
||||||
|
169: "" {class: BLACK}
|
||||||
|
170: "" {class: WHITE}
|
||||||
|
171: "" {class: BLACK}
|
||||||
|
172: "" {class: WHITE}
|
||||||
|
173: "" {class: BLACK}
|
||||||
|
174: "" {class: WHITE}
|
||||||
|
175: "" {class: BLACK}
|
||||||
|
176: "" {class: WHITE}
|
||||||
|
177: "" {class: BLACK}
|
||||||
|
178: "" {class: WHITE}
|
||||||
|
179: "" {class: BLACK}
|
||||||
|
180: "" {class: WHITE}
|
||||||
|
181: "" {class: BLACK}
|
||||||
|
182: "" {class: WHITE}
|
||||||
|
183: "" {class: BLACK}
|
||||||
|
184: "" {class: WHITE}
|
||||||
|
185: "" {class: BLACK}
|
||||||
|
186: "" {class: WHITE}
|
||||||
|
187: "" {class: BLACK}
|
||||||
|
188: "" {class: WHITE}
|
||||||
|
189: "" {class: BLACK}
|
||||||
|
190: "" {class: WHITE}
|
||||||
|
191: "" {class: BLACK}
|
||||||
|
192: "" {class: WHITE}
|
||||||
|
193: "" {class: BLACK}
|
||||||
|
194: "" {class: WHITE}
|
||||||
|
195: "" {class: BLACK}
|
||||||
|
196: "" {class: WHITE}
|
||||||
|
197: "" {class: BLACK}
|
||||||
|
198: "" {class: WHITE}
|
||||||
|
199: "" {class: BLACK}
|
||||||
|
200: "" {class: WHITE}
|
||||||
|
201: "" {class: BLACK}
|
||||||
|
202: "" {class: WHITE}
|
||||||
|
203: "" {class: BLACK}
|
||||||
|
204: "" {class: WHITE}
|
||||||
|
205: "" {class: BLACK}
|
||||||
|
206: "" {class: WHITE}
|
||||||
|
207: "" {class: BLACK}
|
||||||
|
208: "" {class: WHITE}
|
||||||
|
209: "" {class: BLACK}
|
||||||
|
210: "" {class: WHITE}
|
||||||
|
211: "" {class: BLACK}
|
||||||
|
212: "" {class: WHITE}
|
||||||
|
213: "" {class: BLACK}
|
||||||
|
214: "" {class: WHITE}
|
||||||
|
215: "" {class: BLACK}
|
||||||
|
216: "" {class: WHITE}
|
||||||
|
217: "" {class: BLACK}
|
||||||
|
218: "" {class: WHITE}
|
||||||
|
219: "" {class: BLACK}
|
||||||
|
220: "" {class: WHITE}
|
||||||
|
221: "" {class: BLACK}
|
||||||
|
222: "" {class: WHITE}
|
||||||
|
223: "" {class: BLACK}
|
||||||
|
224: "" {class: WHITE}
|
||||||
|
225: "" {class: BLACK}
|
||||||
|
226: "" {class: WHITE}
|
||||||
|
227: "" {class: BLACK}
|
||||||
|
228: "" {class: WHITE}
|
||||||
|
229: "" {class: BLACK}
|
||||||
|
230: "" {class: WHITE}
|
||||||
|
231: "" {class: BLACK}
|
||||||
|
232: "" {class: WHITE}
|
||||||
|
233: "" {class: BLACK}
|
||||||
|
234: "" {class: WHITE}
|
||||||
|
235: "" {class: BLACK}
|
||||||
|
236: "" {class: WHITE}
|
||||||
|
237: "" {class: BLACK}
|
||||||
|
238: "" {class: WHITE}
|
||||||
|
239: "" {class: BLACK}
|
||||||
|
240: "" {class: WHITE}
|
||||||
|
241: "" {class: BLACK}
|
||||||
|
242: "" {class: WHITE}
|
||||||
|
243: "" {class: BLACK}
|
||||||
|
244: "" {class: WHITE}
|
||||||
|
245: "" {class: BLACK}
|
||||||
|
246: "" {class: WHITE}
|
||||||
|
247: "" {class: BLACK}
|
||||||
|
248: "" {class: WHITE}
|
||||||
|
249: "" {class: BLACK}
|
||||||
|
250: "" {class: WHITE}
|
||||||
|
251: "" {class: BLACK}
|
||||||
|
252: "" {class: WHITE}
|
||||||
|
253: "" {class: BLACK}
|
||||||
|
254: "" {class: WHITE}
|
||||||
|
255: "" {class: BLACK}
|
||||||
|
256: "" {class: WHITE}
|
||||||
|
257: "" {class: BLACK}
|
||||||
|
258: "" {class: WHITE}
|
||||||
|
259: "" {class: BLACK}
|
||||||
|
260: "" {class: WHITE}
|
||||||
|
261: "" {class: BLACK}
|
||||||
|
262: "" {class: WHITE}
|
||||||
|
263: "" {class: BLACK}
|
||||||
|
264: "" {class: WHITE}
|
||||||
|
265: "" {class: BLACK}
|
||||||
|
266: "" {class: WHITE}
|
||||||
|
267: "" {class: BLACK}
|
||||||
|
268: "" {class: WHITE}
|
||||||
|
269: "" {class: BLACK}
|
||||||
|
270: "" {class: WHITE}
|
||||||
|
271: "" {class: BLACK}
|
||||||
|
272: "" {class: WHITE}
|
||||||
|
273: "" {class: BLACK}
|
||||||
|
274: "" {class: WHITE}
|
||||||
|
275: "" {class: BLACK}
|
||||||
|
276: "" {class: WHITE}
|
||||||
|
277: "" {class: BLACK}
|
||||||
|
278: "" {class: WHITE}
|
||||||
|
279: "" {class: BLACK}
|
||||||
|
280: "" {class: WHITE}
|
||||||
|
281: "" {class: BLACK}
|
||||||
|
282: "" {class: WHITE}
|
||||||
|
283: "" {class: BLACK}
|
||||||
|
284: "" {class: WHITE}
|
||||||
|
285: "" {class: BLACK}
|
||||||
|
286: "" {class: WHITE}
|
||||||
|
287: "" {class: BLACK}
|
||||||
|
288: "" {class: WHITE}
|
||||||
|
289: "" {class: BLACK}
|
||||||
|
290: "" {class: WHITE}
|
||||||
|
291: "" {class: BLACK}
|
||||||
|
292: "" {class: WHITE}
|
||||||
|
293: "" {class: BLACK}
|
||||||
|
294: "" {class: WHITE}
|
||||||
|
295: "" {class: BLACK}
|
||||||
|
296: "" {class: WHITE}
|
||||||
|
297: "" {class: BLACK}
|
||||||
|
298: "" {class: WHITE}
|
||||||
|
299: "" {class: BLACK}
|
||||||
|
300: "" {class: WHITE}
|
||||||
|
301: "" {class: BLACK}
|
||||||
|
302: "" {class: WHITE}
|
||||||
|
303: "" {class: BLACK}
|
||||||
|
304: "" {class: WHITE}
|
||||||
|
305: "" {class: BLACK}
|
||||||
|
306: "" {class: WHITE}
|
||||||
|
307: "" {class: BLACK}
|
||||||
|
308: "" {class: WHITE}
|
||||||
|
309: "" {class: BLACK}
|
||||||
|
310: "" {class: WHITE}
|
||||||
|
311: "" {class: BLACK}
|
||||||
|
312: "" {class: WHITE}
|
||||||
|
313: "" {class: BLACK}
|
||||||
|
314: "" {class: WHITE}
|
||||||
|
315: "" {class: BLACK}
|
||||||
|
316: "" {class: WHITE}
|
||||||
|
317: "" {class: BLACK}
|
||||||
|
318: "" {class: WHITE}
|
||||||
|
319: "" {class: BLACK}
|
||||||
|
320: "" {class: WHITE}
|
||||||
|
321: "" {class: BLACK}
|
||||||
|
322: "" {class: WHITE}
|
||||||
|
323: "" {class: BLACK}
|
||||||
|
324: "" {class: WHITE}
|
||||||
|
325: "" {class: BLACK}
|
||||||
|
326: "" {class: WHITE}
|
||||||
|
327: "" {class: BLACK}
|
||||||
|
328: "" {class: WHITE}
|
||||||
|
329: "" {class: BLACK}
|
||||||
|
330: "" {class: WHITE}
|
||||||
|
331: "" {class: BLACK}
|
||||||
|
332: "" {class: WHITE}
|
||||||
|
333: "" {class: BLACK}
|
||||||
|
334: "" {class: WHITE}
|
||||||
|
335: "" {class: BLACK}
|
||||||
|
336: "" {class: WHITE}
|
||||||
|
337: "" {class: BLACK}
|
||||||
|
338: "" {class: WHITE}
|
||||||
|
339: "" {class: BLACK}
|
||||||
|
340: "" {class: WHITE}
|
||||||
|
341: "" {class: BLACK}
|
||||||
|
342: "" {class: WHITE}
|
||||||
|
343: "" {class: BLACK}
|
||||||
|
344: "" {class: WHITE}
|
||||||
|
345: "" {class: BLACK}
|
||||||
|
346: "" {class: WHITE}
|
||||||
|
347: "" {class: BLACK}
|
||||||
|
348: "" {class: WHITE}
|
||||||
|
349: "" {class: BLACK}
|
||||||
|
350: "" {class: WHITE}
|
||||||
|
351: "" {class: BLACK}
|
||||||
|
352: "" {class: WHITE}
|
||||||
|
353: "" {class: BLACK}
|
||||||
|
354: "" {class: WHITE}
|
||||||
|
355: "" {class: BLACK}
|
||||||
|
356: "" {class: WHITE}
|
||||||
|
357: "" {class: BLACK}
|
||||||
|
358: "" {class: WHITE}
|
||||||
|
359: "" {class: BLACK}
|
||||||
|
360: "" {class: WHITE}
|
||||||
|
361: "" {class: BLACK}
|
||||||
|
362: "" {class: WHITE}
|
||||||
|
363: "" {class: BLACK}
|
||||||
|
364: "" {class: WHITE}
|
||||||
|
365: "" {class: BLACK}
|
||||||
|
366: "" {class: WHITE}
|
||||||
|
367: "" {class: BLACK}
|
||||||
|
368: "" {class: WHITE}
|
||||||
|
369: "" {class: BLACK}
|
||||||
|
370: "" {class: WHITE}
|
||||||
|
371: "" {class: BLACK}
|
||||||
|
372: "" {class: WHITE}
|
||||||
|
373: "" {class: BLACK}
|
||||||
|
374: "" {class: WHITE}
|
||||||
|
375: "" {class: BLACK}
|
||||||
|
376: "" {class: WHITE}
|
||||||
|
377: "" {class: BLACK}
|
||||||
|
378: "" {class: WHITE}
|
||||||
|
379: "" {class: BLACK}
|
||||||
|
380: "" {class: WHITE}
|
||||||
|
381: "" {class: BLACK}
|
||||||
|
382: "" {class: WHITE}
|
||||||
|
383: "" {class: BLACK}
|
||||||
|
384: "" {class: WHITE}
|
||||||
|
385: "" {class: BLACK}
|
||||||
|
386: "" {class: WHITE}
|
||||||
|
387: "" {class: BLACK}
|
||||||
|
388: "" {class: WHITE}
|
||||||
|
389: "" {class: BLACK}
|
||||||
|
390: "" {class: WHITE}
|
||||||
|
391: "" {class: BLACK}
|
||||||
|
392: "" {class: WHITE}
|
||||||
|
393: "" {class: BLACK}
|
||||||
|
394: "" {class: WHITE}
|
||||||
|
395: "" {class: BLACK}
|
||||||
|
396: "" {class: WHITE}
|
||||||
|
397: "" {class: BLACK}
|
||||||
|
398: "" {class: WHITE}
|
||||||
|
399: "" {class: BLACK}
|
||||||
|
400: "" {class: WHITE}
|
||||||
|
401: "" {class: BLACK}
|
||||||
|
402: "" {class: WHITE}
|
||||||
|
403: "" {class: BLACK}
|
||||||
|
404: "" {class: WHITE}
|
||||||
|
405: "" {class: BLACK}
|
||||||
|
406: "" {class: WHITE}
|
||||||
|
407: "" {class: BLACK}
|
||||||
|
408: "" {class: WHITE}
|
||||||
|
409: "" {class: BLACK}
|
||||||
|
410: "" {class: WHITE}
|
||||||
|
411: "" {class: BLACK}
|
||||||
|
412: "" {class: WHITE}
|
||||||
|
413: "" {class: BLACK}
|
||||||
|
414: "" {class: WHITE}
|
||||||
|
415: "" {class: BLACK}
|
||||||
|
416: "" {class: WHITE}
|
||||||
|
417: "" {class: BLACK}
|
||||||
|
418: "" {class: WHITE}
|
||||||
|
419: "" {class: BLACK}
|
||||||
|
420: "" {class: WHITE}
|
||||||
|
421: "" {class: BLACK}
|
||||||
|
422: "" {class: WHITE}
|
||||||
|
423: "" {class: BLACK}
|
||||||
|
424: "" {class: WHITE}
|
||||||
|
425: "" {class: BLACK}
|
||||||
|
426: "" {class: WHITE}
|
||||||
|
427: "" {class: BLACK}
|
||||||
|
428: "" {class: WHITE}
|
||||||
|
429: "" {class: BLACK}
|
||||||
|
430: "" {class: WHITE}
|
||||||
|
431: "" {class: BLACK}
|
||||||
|
432: "" {class: WHITE}
|
||||||
|
433: "" {class: BLACK}
|
||||||
|
434: "" {class: WHITE}
|
||||||
|
435: "" {class: BLACK}
|
||||||
|
436: "" {class: WHITE}
|
||||||
|
437: "" {class: BLACK}
|
||||||
|
438: "" {class: WHITE}
|
||||||
|
439: "" {class: BLACK}
|
||||||
|
440: "" {class: WHITE}
|
||||||
|
441: "" {class: BLACK}
|
||||||
19452
e2etests/testdata/stable/grid_large_checkered/dagre/board.exp.json
generated
vendored
Normal file
19452
e2etests/testdata/stable/grid_large_checkered/dagre/board.exp.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
88
e2etests/testdata/stable/grid_large_checkered/dagre/sketch.exp.svg
vendored
Normal file
88
e2etests/testdata/stable/grid_large_checkered/dagre/sketch.exp.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 86 KiB |
19452
e2etests/testdata/stable/grid_large_checkered/elk/board.exp.json
generated
vendored
Normal file
19452
e2etests/testdata/stable/grid_large_checkered/elk/board.exp.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
88
e2etests/testdata/stable/grid_large_checkered/elk/sketch.exp.svg
vendored
Normal file
88
e2etests/testdata/stable/grid_large_checkered/elk/sketch.exp.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 86 KiB |
Loading…
Reference in a new issue