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)
|
||||
- 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 ⛑️
|
||||
|
|
|
|||
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"
|
||||
|
|
@ -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
|
||||
// if columns is true, each column aims to have the targetSize height
|
||||
func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2graph.Object {
|
||||
debug := false
|
||||
var nCuts int
|
||||
if columns {
|
||||
nCuts = gd.columns - 1
|
||||
|
|
@ -476,6 +478,23 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
|||
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
|
||||
if columns {
|
||||
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
|
||||
count := 0
|
||||
// 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
|
||||
|
|
@ -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 rowSize > okThreshold*targetSize {
|
||||
skipCount++
|
||||
if skipCount >= SKIP_LIMIT {
|
||||
// there may even be too many to skip
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
// row is too small to be good overall
|
||||
if rowSize < targetSize/okThreshold {
|
||||
skipCount++
|
||||
if skipCount >= SKIP_LIMIT {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
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
|
||||
// with 5 objects and 2 cuts we have these options:
|
||||
// . 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 {
|
||||
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
|
||||
return count >= ATTEMPT_LIMIT || skipCount >= SKIP_LIMIT
|
||||
}
|
||||
|
||||
// try at least 3 different okThresholds
|
||||
for i := 0; i < 3 || bestLayout == nil; i++ {
|
||||
// try number of different okThresholds depending on std deviation of sizes
|
||||
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("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)
|
||||
count = 0.
|
||||
}
|
||||
if debug {
|
||||
fmt.Printf("final count %d, skip count %d\n", count, skipCount)
|
||||
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("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)
|
||||
}
|
||||
|
||||
// try fast layout algorithm, see if it is better than first 1mil attempts
|
||||
debt := 0.
|
||||
fastDivision := make([]int, 0, nCuts)
|
||||
rowSize := 0.
|
||||
for i := 0; i < len(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 size > targetSize-debt {
|
||||
fastDivision = append(fastDivision, i-1)
|
||||
|
|
@ -602,14 +692,23 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
|||
}
|
||||
}
|
||||
if len(fastDivision) == nCuts {
|
||||
layout := genLayout(gd.objects, fastDivision)
|
||||
dist := getDistToTarget(layout, targetSize, float64(gd.horizontalGap), float64(gd.verticalGap), columns)
|
||||
if dist < bestDist {
|
||||
bestLayout = layout
|
||||
bestDist = dist
|
||||
}
|
||||
layout = genLayout(gd.objects, fastDivision)
|
||||
}
|
||||
return bestLayout
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2720,6 +2720,7 @@ scenarios: {
|
|||
loadFromFile(t, "grid_even"),
|
||||
loadFromFile(t, "ent2d2_basic"),
|
||||
loadFromFile(t, "ent2d2_right"),
|
||||
loadFromFile(t, "grid_large_checkered"),
|
||||
}
|
||||
|
||||
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