Merge pull request #1263 from gavin-ts/grid-perf

improve grid layout
This commit is contained in:
gavin-ts 2023-04-28 20:35:45 -07:00 committed by GitHub
commit 7f821f2bde
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 28939 additions and 1159 deletions

View file

@ -8,6 +8,7 @@
- ELK self loops get distributed around the object instead of stacking [#1232](https://github.com/terrastruct/d2/pull/1232)
- ELK preserves order of objects in cycles [#1235](https://github.com/terrastruct/d2/pull/1235)
- Improper usages of `class` and `style` get error messages [#1254](https://github.com/terrastruct/d2/pull/1254)
- Improves scaling of object widths/heights in grid diagrams [#1263](https://github.com/terrastruct/d2/pull/1263)
#### Bugfixes ⛑️
@ -15,3 +16,4 @@
- ELK self loops always have enough space for long labels [#1232](https://github.com/terrastruct/d2/pull/1232)
- Fixes panic when setting `shape` to be `class` or `sql_table` within a class [#1251](https://github.com/terrastruct/d2/pull/1251)
- Fixes rare panic exporting to gifs [#1257](https://github.com/terrastruct/d2/pull/1257)
- Fixes bad performance in large grid diagrams [#1263](https://github.com/terrastruct/d2/pull/1263)

View file

@ -2,6 +2,7 @@ package d2grid
import (
"context"
"fmt"
"math"
"sort"
@ -266,40 +267,17 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
cursor := geo.NewPoint(0, 0)
var maxY, maxX float64
if gd.rowDirected {
// if we have 2 rows, then each row's objects should have the same height
// . ┌A─────────────┐ ┌B──┐ ┌C─────────┐ ┬ maxHeight(A,B,C)
// . ├ ─ ─ ─ ─ ─ ─ ─┤ │ │ │ │ │
// . │ │ │ │ ├ ─ ─ ─ ─ ─┤ │
// . │ │ │ │ │ │ │
// . └──────────────┘ └───┘ └──────────┘ ┴
// . ┌D────────┐ ┌E────────────────┐ ┬ maxHeight(D,E)
// . │ │ │ │ │
// . │ │ │ │ │
// . │ │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │
// . │ │ │ │ │
// . └─────────┘ └─────────────────┘ ┴
// measure row widths
rowWidths := []float64{}
for _, row := range layout {
rowHeight := 0.
x := 0.
for _, o := range row {
o.TopLeft = cursor.Copy()
cursor.X += o.Width + horizontalGap
rowHeight = math.Max(rowHeight, o.Height)
x += o.Width + horizontalGap
}
rowWidth := cursor.X - horizontalGap
rowWidth := x - horizontalGap
rowWidths = append(rowWidths, rowWidth)
maxX = math.Max(maxX, rowWidth)
// set all objects in row to the same height
for _, o := range row {
o.Height = rowHeight
}
// new row
cursor.X = 0
cursor.Y += rowHeight + verticalGap
}
maxY = cursor.Y - horizontalGap
// then expand thinnest objects to make each row the same width
// . ┌A─────────────┐ ┌B──┐ ┌C─────────┐ ┬ maxHeight(A,B,C)
@ -319,80 +297,79 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
continue
}
delta := maxX - rowWidth
objects := []*d2graph.Object{}
var widest float64
for _, o := range row {
widest = math.Max(widest, o.Width)
objects = append(objects, o)
}
sort.Slice(objects, func(i, j int) bool {
return objects[i].Width < objects[j].Width
})
// expand smaller objects to fill remaining space
for _, o := range objects {
if o.Width < widest {
var index int
for i, rowObj := range row {
if o == rowObj {
index = i
break
}
}
grow := math.Min(widest-o.Width, delta)
o.Width += grow
// shift following objects
for i := index + 1; i < len(row); i++ {
row[i].TopLeft.X += grow
}
delta -= grow
if delta <= 0 {
break
}
diffs := make([]float64, len(row))
totalDiff := 0.
for i, o := range row {
diffs[i] = widest - o.Width
totalDiff += diffs[i]
}
if totalDiff > 0 {
// expand smaller nodes up to the size of the larger ones with delta
// percentage diff
for i := range diffs {
diffs[i] /= totalDiff
}
growth := math.Min(delta, totalDiff)
// expand smaller objects to fill remaining space
for i, o := range row {
o.Width += diffs[i] * growth
}
}
if delta > 0 {
grow := delta / float64(len(row))
for i := len(row) - 1; i >= 0; i-- {
o := row[i]
o.TopLeft.X += grow * float64(i)
o.Width += grow
delta -= grow
if delta > totalDiff {
growth := (delta - totalDiff) / float64(len(row))
for _, o := range row {
o.Width += growth
}
}
}
} else {
// if we have 3 columns, then each column's objects should have the same width
// . ├maxWidth(A,B)─┤ ├maxW(C,D)─┤ ├maxWidth(E)──────┤
// . ┌A─────────────┐ ┌C─────────┐ ┌E────────────────┐
// . └──────────────┘ │ │ │
// . ┌B──┬──────────┐ └──────────┘ │
// . │ │ ┌D────────┬┐ └─────────────────┘
// . │ │ │ │ │
// . │ │ │ ││
// . └───┴──────────┘ │
// . │ │
// . └─────────┴┘
colHeights := []float64{}
for _, column := range layout {
colWidth := 0.
for _, o := range column {
// if we have 2 rows, then each row's objects should have the same height
// . ┌A─────────────┐ ┌B──┐ ┌C─────────┐ ┬ maxHeight(A,B,C)
// . ├ ─ ─ ─ ─ ─ ─ ─┤ │ │ │ │ │
// . │ │ │ │ ├ ─ ─ ─ ─ ─┤
// . │ │ │ │ │ │
// . └──────────────┘ └───┘ └──────────┘ ┴
// . ┌D────────┐ ┌E────────────────┐ ┬ maxHeight(D,E)
// . │ │ │
// . │ │ │ │
// . │ │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ┤
// . │ │ │ │ │
// . └─────────┘ └─────────────────┘ ┴
for _, row := range layout {
rowHeight := 0.
for _, o := range row {
o.TopLeft = cursor.Copy()
cursor.Y += o.Height + verticalGap
colWidth = math.Max(colWidth, o.Width)
}
colHeight := cursor.Y - verticalGap
colHeights = append(colHeights, colHeight)
maxY = math.Max(maxY, colHeight)
// set all objects in column to the same width
for _, o := range column {
o.Width = colWidth
cursor.X += o.Width + horizontalGap
rowHeight = math.Max(rowHeight, o.Height)
}
// new column
cursor.Y = 0
cursor.X += colWidth + horizontalGap
// set all objects in row to the same height
for _, o := range row {
o.Height = rowHeight
}
// new row
cursor.X = 0
cursor.Y += rowHeight + verticalGap
}
maxX = cursor.X - horizontalGap
maxY = cursor.Y - horizontalGap
} else {
// measure column heights
colHeights := []float64{}
for _, column := range layout {
y := 0.
for _, o := range column {
y += o.Height + verticalGap
}
colHeight := y - verticalGap
colHeights = append(colHeights, colHeight)
maxY = math.Max(maxY, colHeight)
}
// then expand shortest objects to make each column the same height
// . ├maxWidth(A,B)─┤ ├maxW(C,D)─┤ ├maxWidth(E)──────┤
// . ┌A─────────────┐ ┌C─────────┐ ┌E────────────────┐
@ -410,47 +387,63 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
continue
}
delta := maxY - colHeight
objects := []*d2graph.Object{}
var tallest float64
for _, o := range column {
tallest = math.Max(tallest, o.Height)
objects = append(objects, o)
}
sort.Slice(objects, func(i, j int) bool {
return objects[i].Height < objects[j].Height
})
// expand smaller objects to fill remaining space
for _, o := range objects {
if o.Height < tallest {
var index int
for i, colObj := range column {
if o == colObj {
index = i
break
}
}
grow := math.Min(tallest-o.Height, delta)
o.Height += grow
// shift following objects
for i := index + 1; i < len(column); i++ {
column[i].TopLeft.Y += grow
}
delta -= grow
if delta <= 0 {
break
}
diffs := make([]float64, len(column))
totalDiff := 0.
for i, o := range column {
diffs[i] = tallest - o.Height
totalDiff += diffs[i]
}
if totalDiff > 0 {
// expand smaller nodes up to the size of the larger ones with delta
// percentage diff
for i := range diffs {
diffs[i] /= totalDiff
}
growth := math.Min(delta, totalDiff)
// expand smaller objects to fill remaining space
for i, o := range column {
o.Height += diffs[i] * growth
}
}
if delta > 0 {
grow := delta / float64(len(column))
for i := len(column) - 1; i >= 0; i-- {
o := column[i]
o.TopLeft.Y += grow * float64(i)
o.Height += grow
delta -= grow
if delta > totalDiff {
growth := (delta - totalDiff) / float64(len(column))
for _, o := range column {
o.Height += growth
}
}
}
// if we have 3 columns, then each column's objects should have the same width
// . ├maxWidth(A,B)─┤ ├maxW(C,D)─┤ ├maxWidth(E)──────┤
// . ┌A─────────────┐ ┌C─────────┐ ┌E────────────────┐
// . └──────────────┘ │ │ │ │
// . ┌B──┬──────────┐ └──────────┘ │ │
// . │ │ ┌D────────┬┐ └─────────────────┘
// . │ │ │ │ │
// . │ │ │ ││
// . └───┴──────────┘ │ │
// . │ ││
// . └─────────┴┘
for _, column := range layout {
colWidth := 0.
for _, o := range column {
o.TopLeft = cursor.Copy()
cursor.Y += o.Height + verticalGap
colWidth = math.Max(colWidth, o.Width)
}
// set all objects in column to the same width
for _, o := range column {
o.Width = colWidth
}
// new column
cursor.Y = 0
cursor.X += colWidth + horizontalGap
}
maxX = cursor.X - horizontalGap
}
gd.width = maxX
gd.height = maxY
@ -469,6 +462,68 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
return genLayout(gd.objects, nil)
}
var gap float64
if columns {
gap = float64(gd.verticalGap)
} else {
gap = float64(gd.horizontalGap)
}
getSize := func(o *d2graph.Object) float64 {
if columns {
return o.Height
} else {
return o.Width
}
}
debug := false
skipCount := 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
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
// with starting=true it will always be the 1st N objects based on len(row)
if ok, has := startingCache[len(row)]; has {
return ok
}
defer func() {
// cache result before returning
startingCache[len(row)] = ok
}()
}
rowSize := 0.
for _, obj := range row {
rowSize += getSize(obj)
}
if len(row) > 1 {
rowSize += gap * float64(len(row)-1)
// 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++
return false
}
}
// row is too small to be good overall
if rowSize < targetSize/okThreshold {
skipCount++
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─┐
@ -477,41 +532,128 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
// . A B │ C │ D E └────────────┘
// . A │ B C │ D E ┌E───────────┐
// . A │ B │ C D E └────────────┘
divisions := genDivisions(gd.objects, nCuts)
var bestLayout [][]*d2graph.Object
bestDist := math.MaxFloat64
// of these divisions, find the layout with rows closest to the targetSize
for _, division := range divisions {
tryDivision := func(division []int) bool {
layout := genLayout(gd.objects, division)
dist := getDistToTarget(layout, targetSize, float64(gd.horizontalGap), float64(gd.verticalGap), columns)
if dist < bestDist {
bestLayout = layout
bestDist = dist
}
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
}
// try at least 3 different okThresholds
for i := 0; i < 3 || bestLayout == nil; i++ {
iterDivisions(gd.objects, nCuts, tryDivision, rowOk)
okThreshold += thresholdStep
if debug {
fmt.Printf("increasing ok threshold to %v\n", okThreshold)
}
startingCache = make(map[int]bool)
count = 0.
}
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
debt := 0.
fastDivision := make([]int, 0, nCuts)
rowSize := 0.
for i := 0; i < len(gd.objects); i++ {
o := gd.objects[i]
size := getSize(o)
if rowSize == 0 {
if size > targetSize-debt {
fastDivision = append(fastDivision, i-1)
// we build up a debt of distance past the target size across rows
newDebt := size - targetSize
debt += newDebt
} else {
rowSize += size
}
continue
}
// debt is paid by decreasing threshold to start new row and ending below targetSize
if rowSize+(gap+size)/2. > targetSize-debt {
fastDivision = append(fastDivision, i-1)
newDebt := rowSize - targetSize
debt += newDebt
rowSize = size
} else {
rowSize += gap + size
}
}
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
}
}
return bestLayout
}
// 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)
// get all possible divisions of objects by the number of cuts
func genDivisions(objects []*d2graph.Object, nCuts int) (divisions [][]int) {
func iterDivisions(objects []*d2graph.Object, nCuts int, f iterDivision, check checkCut) {
if len(objects) < 2 || nCuts == 0 {
return nil
return
}
done := false
// we go in this order to prefer extra objects in starting rows rather than later ones
lastObj := len(objects) - 1
// with objects=[A, B, C, D, E]; nCuts=2
// d:depth; i:index; n:nCuts;
// ┌────┬───┬───┬─────────────────────┬────────────┐
// │ d │ i │ n │ objects │ cuts │
// ├────┼───┼───┼─────────────────────┼────────────┤
// │ 0 │ 4 │ 2 │ [A B C D | E] │ │
// ├────┼───┼───┼─────────────────────┼────────────┤
// │ └1 │ 3 │ 1 │ [A B C | D] │ + | E] │
// ├────┼───┼───┼─────────────────────┼────────────┤
// │ └1 │ 2 │ 1 │ [A B | C D] │ + | E] │
// ├────┼───┼───┼─────────────────────┼────────────┤
// │ └1 │ 1 │ 1 │ [A | B C D] │ + | E] │
// ├────┼───┼───┼─────────────────────┼────────────┤
// │ 0 │ 3 │ 2 │ [A B C | D E] │ │
// ├────┼───┼───┼─────────────────────┼────────────┤
// │ └1 │ 2 │ 1 │ [A B | C] │ + | D E] │
// ├────┼───┼───┼─────────────────────┼────────────┤
// │ └1 │ 1 │ 1 │ [A | B C] │ + | D E] │
// ├────┼───┼───┼─────────────────────┼────────────┤
// │ 0 │ 2 │ 2 │ [A B | C D E] │ │
// ├────┼───┼───┼─────────────────────┼────────────┤
// │ └1 │ 1 │ 1 │ [A | B] │ + | C D E] │
// └────┴───┴───┴─────────────────────┴────────────┘
for index := lastObj; index >= nCuts; index-- {
if !check(objects[index:], false) {
// optimization: if current cut gives a bad grouping, don't recurse
continue
}
if nCuts > 1 {
for _, inner := range genDivisions(objects[:index], nCuts-1) {
divisions = append(divisions, append(inner, index-1))
}
iterDivisions(objects[:index], nCuts-1, func(inner []int) bool {
done = f(append(inner, index-1))
return done
}, check)
} else {
divisions = append(divisions, []int{index - 1})
if !check(objects[:index], true) {
// e.g. [A B C | D] if [A,B,C] is bad, skip it
continue
}
done = f([]int{index - 1})
}
if done {
return
}
}
return divisions
}
// generate a grid of objects from the given cut indices
@ -525,6 +667,9 @@ func genLayout(objects []*d2graph.Object, cutIndices []int) [][]*d2graph.Object
} else {
stop = len(objects) - 1
}
if stop >= objIndex {
layout[i] = make([]*d2graph.Object, 0, stop-objIndex+1)
}
for ; objIndex <= stop; objIndex++ {
layout[i] = append(layout[i], objects[objIndex])
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 201 KiB

After

Width:  |  Height:  |  Size: 201 KiB

View file

@ -932,6 +932,8 @@ d
a -> b -> c
`,
},
loadFromFile(t, "slow_grid"),
loadFromFile(t, "grid_oom"),
}
runa(t, tcs)

View file

@ -71,7 +71,18 @@ func main() {
ctx := log.Stderr(context.Background())
ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
defer cancel()
cmd := exec.CommandContext(ctx, "go", "test", testDir, testMatchString, cpuProfileStr, memProfileStr, vString)
// don't want to pass empty args to CommandContext
args := []string{"test", testDir, testMatchString}
if cpuProfileStr != "" {
args = append(args, cpuProfileStr)
}
if memProfileStr != "" {
args = append(args, memProfileStr)
}
if vString != "" {
args = append(args, vString)
}
cmd := exec.CommandContext(ctx, "go", args...)
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "FORCE_COLOR=1")
cmd.Env = append(cmd.Env, "DEBUG=1")

View file

@ -2717,6 +2717,7 @@ scenarios: {
loadFromFile(t, "executive_grid"),
loadFromFile(t, "grid_animated"),
loadFromFile(t, "grid_gap"),
loadFromFile(t, "grid_even"),
loadFromFile(t, "ent2d2_basic"),
loadFromFile(t, "ent2d2_right"),
}

69
e2etests/testdata/files/grid_even.d2 vendored Normal file
View file

@ -0,0 +1,69 @@
row no growth: {
grid-rows: 2
# row 1
a.width: 620
# row 2 width equals row 1 (with 40 in-between each)
b.width: 200
c.width: 150
d.width: 100
e.width: 50
}
row 200 growth: {
grid-rows: 2
# row 1
a.width: 820
# row 2 needs to grow 200 to equal row 1
b.width: 200
# these 3 should grow proportionally until they reach b's width
c.width: 150
d.width: 100
e.width: 50
}
row big growth: {
grid-rows: 2
# row 1
a.width: 1420
# row 2 needs to grow 800
# these should all reach width 350
b.width: 200 # 0 + 150
c.width: 150 # 50 + 150
d.width: 100 # 100 + 150
e.width: 50 # 150 + 150
}
column no growth: {
grid-columns: 2
a.height: 620
b.height: 200
c.height: 150
d.height: 100
e.height: 50
}
column 200 growth: {
grid-columns: 2
a.height: 820
b.height: 200
c.height: 150
d.height: 100
e.height: 50
}
column big growth: {
grid-columns: 2
a.height: 1420
b.height: 200
c.height: 150
d.height: 100
e.height: 50
}

1049
e2etests/testdata/files/grid_oom.d2 vendored Normal file

File diff suppressed because it is too large Load diff

147
e2etests/testdata/files/slow_grid.d2 vendored Normal file
View file

@ -0,0 +1,147 @@
grid-columns: 15
1: {
shape: class
1: ------------------
2: -------------------------------
3: ------------------------------
4: -------------------------
}
2: {
shape: class
1: -----------------
2: ----------------------------
}
3: {
shape: class
1: -----------------
2: ----------------------------
3: ------------------------------
4: -------------------------
}
4: {
shape: class
1: ----------------------------
}
5: {
shape: class
1: --------------------
2: ----------------------------------------------
3: ---------------------------------
}
6: {
shape: class
1: ----------------------------------------
2: ------------------------
3: ------------------------
4: --------------------------------------
}
7: {
shape: class
1: ----------------------------------------
2: ---------------------
3: ------------------------
4: --------------------------------------
}
5: {
shape: class
1: ----------------------------------------
2: ---------------------
3: ------------------------
4: --------------------------------------
}
9: {
shape: class
1: ----------------------
2: -------------------------------------------
3: -----------------------
4: --------------------------
5: -----------------------------
6: -----------------------------
7: --------------------------
}
10: {
shape: class
1: -----------------
}
11: {
shape: class
1: ----------------
2: ---------------------------
3: -----------------------------
4: ------------------------
}
12: {
shape: class
1: ----------------------
2: -----------------------
3: -------------
4: -------------
5: ------------------------
6: ------------------------------------------------
7: --------------------------
}
13: {
shape: class
1: --------------------------------
}
14: {
shape: class
1: ----------------
2: ---------------------------
3: -----------------------------
4: ------------------------
}
15: {
shape: class
1: ------------------------
2: ----------------------
}
16: {
shape: class
1: ----------------
2: ------------------------------
}
17: {
shape: class
1: -----------------
}
18: {
shape: class
1: ----------------
}
19: {
shape: class
1: -----------------
}
20: {
shape: class
1: -----------------
}
21: {
shape: class
1: ----------------
2: ---------------------------
3: -----------------------------
4: ------------------------
}
22: {
shape: class
1: ---------------------------------------------
2: --------------------
3: -----------------------
4: -------------------------------------
}
23: {
shape: class
1: ----------------
2: ------------------------------
}
24: {
shape: class
1: -----------------
}
25: {
shape: class
1: -----------------
}

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 291 KiB

9898
e2etests/testdata/regression/grid_oom/elk/board.exp.json generated vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 291 KiB

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 46 KiB

1463
e2etests/testdata/regression/slow_grid/elk/board.exp.json generated vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 46 KiB

View file

@ -256,7 +256,7 @@
"x": 280,
"y": 140
},
"width": 120,
"width": 100,
"height": 100,
"opacity": 1,
"strokeDash": 0,
@ -294,10 +294,10 @@
"id": "flow8",
"type": "rectangle",
"pos": {
"x": 440,
"x": 420,
"y": 140
},
"width": 160,
"width": 170,
"height": 100,
"opacity": 1,
"strokeDash": 0,
@ -335,10 +335,10 @@
"id": "flow9",
"type": "rectangle",
"pos": {
"x": 640,
"x": 630,
"y": 140
},
"width": 160,
"width": 170,
"height": 100,
"opacity": 1,
"strokeDash": 0,
@ -473,7 +473,7 @@
"x": 0,
"y": 508
},
"width": 139,
"width": 123,
"height": 66,
"opacity": 1,
"strokeDash": 0,
@ -511,10 +511,10 @@
"id": "WINDOWS",
"type": "rectangle",
"pos": {
"x": 179,
"x": 163,
"y": 508
},
"width": 117,
"width": 131,
"height": 66,
"opacity": 1,
"strokeDash": 0,
@ -552,10 +552,10 @@
"id": "LINUX",
"type": "rectangle",
"pos": {
"x": 336,
"x": 334,
"y": 508
},
"width": 139,
"width": 122,
"height": 66,
"opacity": 1,
"strokeDash": 0,
@ -593,10 +593,10 @@
"id": "MACOS",
"type": "rectangle",
"pos": {
"x": 515,
"x": 496,
"y": 508
},
"width": 106,
"width": 124,
"height": 66,
"opacity": 1,
"strokeDash": 0,

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -256,7 +256,7 @@
"x": 280,
"y": 140
},
"width": 120,
"width": 100,
"height": 100,
"opacity": 1,
"strokeDash": 0,
@ -294,10 +294,10 @@
"id": "flow8",
"type": "rectangle",
"pos": {
"x": 440,
"x": 420,
"y": 140
},
"width": 160,
"width": 170,
"height": 100,
"opacity": 1,
"strokeDash": 0,
@ -335,10 +335,10 @@
"id": "flow9",
"type": "rectangle",
"pos": {
"x": 640,
"x": 630,
"y": 140
},
"width": 160,
"width": 170,
"height": 100,
"opacity": 1,
"strokeDash": 0,
@ -473,7 +473,7 @@
"x": 0,
"y": 508
},
"width": 139,
"width": 123,
"height": 66,
"opacity": 1,
"strokeDash": 0,
@ -511,10 +511,10 @@
"id": "WINDOWS",
"type": "rectangle",
"pos": {
"x": 179,
"x": 163,
"y": 508
},
"width": 117,
"width": 131,
"height": 66,
"opacity": 1,
"strokeDash": 0,
@ -552,10 +552,10 @@
"id": "LINUX",
"type": "rectangle",
"pos": {
"x": 336,
"x": 334,
"y": 508
},
"width": 139,
"width": 122,
"height": 66,
"opacity": 1,
"strokeDash": 0,
@ -593,10 +593,10 @@
"id": "MACOS",
"type": "rectangle",
"pos": {
"x": 515,
"x": 496,
"y": 508
},
"width": 106,
"width": 124,
"height": 66,
"opacity": 1,
"strokeDash": 0,

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

1524
e2etests/testdata/stable/grid_even/dagre/board.exp.json generated vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

1524
e2etests/testdata/stable/grid_even/elk/board.exp.json generated vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -955,7 +955,7 @@
"x": 2204,
"y": 292
},
"width": 108,
"width": 103,
"height": 92,
"opacity": 1,
"strokeDash": 0,
@ -1005,7 +1005,7 @@
"id": "infra.Kubernetes",
"type": "rectangle",
"pos": {
"x": 2352,
"x": 2347,
"y": 292
},
"width": 152,
@ -1058,10 +1058,10 @@
"id": "infra.My SQL",
"type": "rectangle",
"pos": {
"x": 2544,
"x": 2539,
"y": 292
},
"width": 122,
"width": 126,
"height": 92,
"opacity": 1,
"strokeDash": 0,

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View file

@ -955,7 +955,7 @@
"x": 1323,
"y": 210
},
"width": 108,
"width": 103,
"height": 92,
"opacity": 1,
"strokeDash": 0,
@ -1005,7 +1005,7 @@
"id": "infra.Kubernetes",
"type": "rectangle",
"pos": {
"x": 1471,
"x": 1466,
"y": 210
},
"width": 152,
@ -1058,10 +1058,10 @@
"id": "infra.My SQL",
"type": "rectangle",
"pos": {
"x": 1663,
"x": 1658,
"y": 210
},
"width": 122,
"width": 126,
"height": 92,
"opacity": 1,
"strokeDash": 0,

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB