|
|
@ -8,6 +8,7 @@
|
||||||
- ELK self loops get distributed around the object instead of stacking [#1232](https://github.com/terrastruct/d2/pull/1232)
|
- 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)
|
- 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)
|
- 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 ⛑️
|
#### Bugfixes ⛑️
|
||||||
|
|
||||||
|
|
@ -15,3 +16,4 @@
|
||||||
- ELK self loops always have enough space for long labels [#1232](https://github.com/terrastruct/d2/pull/1232)
|
- 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 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 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)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package d2grid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
|
@ -266,41 +267,18 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
|
||||||
cursor := geo.NewPoint(0, 0)
|
cursor := geo.NewPoint(0, 0)
|
||||||
var maxY, maxX float64
|
var maxY, maxX float64
|
||||||
if gd.rowDirected {
|
if gd.rowDirected {
|
||||||
// if we have 2 rows, then each row's objects should have the same height
|
// measure row widths
|
||||||
// . ┌A─────────────┐ ┌B──┐ ┌C─────────┐ ┬ maxHeight(A,B,C)
|
|
||||||
// . ├ ─ ─ ─ ─ ─ ─ ─┤ │ │ │ │ │
|
|
||||||
// . │ │ │ │ ├ ─ ─ ─ ─ ─┤ │
|
|
||||||
// . │ │ │ │ │ │ │
|
|
||||||
// . └──────────────┘ └───┘ └──────────┘ ┴
|
|
||||||
// . ┌D────────┐ ┌E────────────────┐ ┬ maxHeight(D,E)
|
|
||||||
// . │ │ │ │ │
|
|
||||||
// . │ │ │ │ │
|
|
||||||
// . │ │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │
|
|
||||||
// . │ │ │ │ │
|
|
||||||
// . └─────────┘ └─────────────────┘ ┴
|
|
||||||
rowWidths := []float64{}
|
rowWidths := []float64{}
|
||||||
for _, row := range layout {
|
for _, row := range layout {
|
||||||
rowHeight := 0.
|
x := 0.
|
||||||
for _, o := range row {
|
for _, o := range row {
|
||||||
o.TopLeft = cursor.Copy()
|
x += o.Width + horizontalGap
|
||||||
cursor.X += o.Width + horizontalGap
|
|
||||||
rowHeight = math.Max(rowHeight, o.Height)
|
|
||||||
}
|
}
|
||||||
rowWidth := cursor.X - horizontalGap
|
rowWidth := x - horizontalGap
|
||||||
rowWidths = append(rowWidths, rowWidth)
|
rowWidths = append(rowWidths, rowWidth)
|
||||||
maxX = math.Max(maxX, 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
|
// then expand thinnest objects to make each row the same width
|
||||||
// . ┌A─────────────┐ ┌B──┐ ┌C─────────┐ ┬ maxHeight(A,B,C)
|
// . ┌A─────────────┐ ┌B──┐ ┌C─────────┐ ┬ maxHeight(A,B,C)
|
||||||
// . │ │ │ │ │ │ │
|
// . │ │ │ │ │ │ │
|
||||||
|
|
@ -319,80 +297,79 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
delta := maxX - rowWidth
|
delta := maxX - rowWidth
|
||||||
objects := []*d2graph.Object{}
|
|
||||||
var widest float64
|
var widest float64
|
||||||
for _, o := range row {
|
for _, o := range row {
|
||||||
widest = math.Max(widest, o.Width)
|
widest = math.Max(widest, o.Width)
|
||||||
objects = append(objects, o)
|
|
||||||
}
|
}
|
||||||
sort.Slice(objects, func(i, j int) bool {
|
diffs := make([]float64, len(row))
|
||||||
return objects[i].Width < objects[j].Width
|
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
|
// expand smaller objects to fill remaining space
|
||||||
for _, o := range objects {
|
for i, o := range row {
|
||||||
if o.Width < widest {
|
o.Width += diffs[i] * growth
|
||||||
var index int
|
|
||||||
for i, rowObj := range row {
|
|
||||||
if o == rowObj {
|
|
||||||
index = i
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
grow := math.Min(widest-o.Width, delta)
|
if delta > totalDiff {
|
||||||
o.Width += grow
|
growth := (delta - totalDiff) / float64(len(row))
|
||||||
// shift following objects
|
for _, o := range row {
|
||||||
for i := index + 1; i < len(row); i++ {
|
o.Width += growth
|
||||||
row[i].TopLeft.X += grow
|
|
||||||
}
|
|
||||||
delta -= grow
|
|
||||||
if delta <= 0 {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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 {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// new column
|
// if we have 2 rows, then each row's objects should have the same height
|
||||||
cursor.Y = 0
|
// . ┌A─────────────┐ ┌B──┐ ┌C─────────┐ ┬ maxHeight(A,B,C)
|
||||||
cursor.X += colWidth + horizontalGap
|
// . ├ ─ ─ ─ ─ ─ ─ ─┤ │ │ │ │ │
|
||||||
|
// . │ │ │ │ ├ ─ ─ ─ ─ ─┤ │
|
||||||
|
// . │ │ │ │ │ │ │
|
||||||
|
// . └──────────────┘ └───┘ └──────────┘ ┴
|
||||||
|
// . ┌D────────┐ ┌E────────────────┐ ┬ maxHeight(D,E)
|
||||||
|
// . │ │ │ │ │
|
||||||
|
// . │ │ │ │ │
|
||||||
|
// . │ │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │
|
||||||
|
// . │ │ │ │ │
|
||||||
|
// . └─────────┘ └─────────────────┘ ┴
|
||||||
|
for _, row := range layout {
|
||||||
|
rowHeight := 0.
|
||||||
|
for _, o := range row {
|
||||||
|
o.TopLeft = cursor.Copy()
|
||||||
|
cursor.X += o.Width + horizontalGap
|
||||||
|
rowHeight = math.Max(rowHeight, o.Height)
|
||||||
}
|
}
|
||||||
maxX = cursor.X - 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
|
||||||
|
}
|
||||||
|
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
|
// then expand shortest objects to make each column the same height
|
||||||
// . ├maxWidth(A,B)─┤ ├maxW(C,D)─┤ ├maxWidth(E)──────┤
|
// . ├maxWidth(A,B)─┤ ├maxW(C,D)─┤ ├maxWidth(E)──────┤
|
||||||
// . ┌A─────────────┐ ┌C─────────┐ ┌E────────────────┐
|
// . ┌A─────────────┐ ┌C─────────┐ ┌E────────────────┐
|
||||||
|
|
@ -410,47 +387,63 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
delta := maxY - colHeight
|
delta := maxY - colHeight
|
||||||
objects := []*d2graph.Object{}
|
|
||||||
var tallest float64
|
var tallest float64
|
||||||
for _, o := range column {
|
for _, o := range column {
|
||||||
tallest = math.Max(tallest, o.Height)
|
tallest = math.Max(tallest, o.Height)
|
||||||
objects = append(objects, o)
|
|
||||||
}
|
}
|
||||||
sort.Slice(objects, func(i, j int) bool {
|
diffs := make([]float64, len(column))
|
||||||
return objects[i].Height < objects[j].Height
|
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
|
// expand smaller objects to fill remaining space
|
||||||
for _, o := range objects {
|
for i, o := range column {
|
||||||
if o.Height < tallest {
|
o.Height += diffs[i] * growth
|
||||||
var index int
|
|
||||||
for i, colObj := range column {
|
|
||||||
if o == colObj {
|
|
||||||
index = i
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
grow := math.Min(tallest-o.Height, delta)
|
if delta > totalDiff {
|
||||||
o.Height += grow
|
growth := (delta - totalDiff) / float64(len(column))
|
||||||
// shift following objects
|
for _, o := range column {
|
||||||
for i := index + 1; i < len(column); i++ {
|
o.Height += growth
|
||||||
column[i].TopLeft.Y += grow
|
|
||||||
}
|
|
||||||
delta -= grow
|
|
||||||
if delta <= 0 {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if delta > 0 {
|
// if we have 3 columns, then each column's objects should have the same width
|
||||||
grow := delta / float64(len(column))
|
// . ├maxWidth(A,B)─┤ ├maxW(C,D)─┤ ├maxWidth(E)──────┤
|
||||||
for i := len(column) - 1; i >= 0; i-- {
|
// . ┌A─────────────┐ ┌C─────────┐ ┌E────────────────┐
|
||||||
o := column[i]
|
// . └──────────────┘ │ │ │ │
|
||||||
o.TopLeft.Y += grow * float64(i)
|
// . ┌B──┬──────────┐ └──────────┘ │ │
|
||||||
o.Height += grow
|
// . │ │ ┌D────────┬┐ └─────────────────┘
|
||||||
delta -= grow
|
// . │ │ │ │ │
|
||||||
|
// . │ │ │ ││
|
||||||
|
// . └───┴──────────┘ │ │
|
||||||
|
// . │ ││
|
||||||
|
// . └─────────┴┘
|
||||||
|
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.width = maxX
|
||||||
gd.height = maxY
|
gd.height = maxY
|
||||||
|
|
@ -469,6 +462,68 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
||||||
return genLayout(gd.objects, nil)
|
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
|
// 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─┐
|
||||||
|
|
@ -477,41 +532,128 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
||||||
// . A B │ C │ D E └────────────┘
|
// . A B │ C │ D E └────────────┘
|
||||||
// . A │ B C │ D E ┌E───────────┐
|
// . A │ B C │ D E ┌E───────────┐
|
||||||
// . A │ B │ C D 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
|
// 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)
|
layout := genLayout(gd.objects, division)
|
||||||
dist := getDistToTarget(layout, targetSize, float64(gd.horizontalGap), float64(gd.verticalGap), columns)
|
dist := getDistToTarget(layout, targetSize, float64(gd.horizontalGap), float64(gd.verticalGap), columns)
|
||||||
if dist < bestDist {
|
if dist < bestDist {
|
||||||
bestLayout = layout
|
bestLayout = layout
|
||||||
bestDist = dist
|
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
|
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
|
// 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 {
|
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
|
// we go in this order to prefer extra objects in starting rows rather than later ones
|
||||||
lastObj := len(objects) - 1
|
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-- {
|
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 {
|
if nCuts > 1 {
|
||||||
for _, inner := range genDivisions(objects[:index], nCuts-1) {
|
iterDivisions(objects[:index], nCuts-1, func(inner []int) bool {
|
||||||
divisions = append(divisions, append(inner, index-1))
|
done = f(append(inner, index-1))
|
||||||
}
|
return done
|
||||||
|
}, check)
|
||||||
} else {
|
} 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
|
// generate a grid of objects from the given cut indices
|
||||||
|
|
@ -525,6 +667,9 @@ func genLayout(objects []*d2graph.Object, cutIndices []int) [][]*d2graph.Object
|
||||||
} else {
|
} else {
|
||||||
stop = len(objects) - 1
|
stop = len(objects) - 1
|
||||||
}
|
}
|
||||||
|
if stop >= objIndex {
|
||||||
|
layout[i] = make([]*d2graph.Object, 0, stop-objIndex+1)
|
||||||
|
}
|
||||||
for ; objIndex <= stop; objIndex++ {
|
for ; objIndex <= stop; objIndex++ {
|
||||||
layout[i] = append(layout[i], objects[objIndex])
|
layout[i] = append(layout[i], objects[objIndex])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
562
d2renderers/d2sketch/testdata/twitter/sketch.exp.svg
vendored
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 201 KiB |
|
|
@ -932,6 +932,8 @@ d
|
||||||
a -> b -> c
|
a -> b -> c
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
loadFromFile(t, "slow_grid"),
|
||||||
|
loadFromFile(t, "grid_oom"),
|
||||||
}
|
}
|
||||||
|
|
||||||
runa(t, tcs)
|
runa(t, tcs)
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,18 @@ func main() {
|
||||||
ctx := log.Stderr(context.Background())
|
ctx := log.Stderr(context.Background())
|
||||||
ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
|
ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
|
||||||
defer cancel()
|
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 = os.Environ()
|
||||||
cmd.Env = append(cmd.Env, "FORCE_COLOR=1")
|
cmd.Env = append(cmd.Env, "FORCE_COLOR=1")
|
||||||
cmd.Env = append(cmd.Env, "DEBUG=1")
|
cmd.Env = append(cmd.Env, "DEBUG=1")
|
||||||
|
|
|
||||||
|
|
@ -2717,6 +2717,7 @@ scenarios: {
|
||||||
loadFromFile(t, "executive_grid"),
|
loadFromFile(t, "executive_grid"),
|
||||||
loadFromFile(t, "grid_animated"),
|
loadFromFile(t, "grid_animated"),
|
||||||
loadFromFile(t, "grid_gap"),
|
loadFromFile(t, "grid_gap"),
|
||||||
|
loadFromFile(t, "grid_even"),
|
||||||
loadFromFile(t, "ent2d2_basic"),
|
loadFromFile(t, "ent2d2_basic"),
|
||||||
loadFromFile(t, "ent2d2_right"),
|
loadFromFile(t, "ent2d2_right"),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
69
e2etests/testdata/files/grid_even.d2
vendored
Normal 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
147
e2etests/testdata/files/slow_grid.d2
vendored
Normal 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: -----------------
|
||||||
|
}
|
||||||
9898
e2etests/testdata/regression/grid_oom/dagre/board.exp.json
generated
vendored
Normal file
95
e2etests/testdata/regression/grid_oom/dagre/sketch.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 291 KiB |
9898
e2etests/testdata/regression/grid_oom/elk/board.exp.json
generated
vendored
Normal file
95
e2etests/testdata/regression/grid_oom/elk/sketch.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 291 KiB |
1463
e2etests/testdata/regression/slow_grid/dagre/board.exp.json
generated
vendored
Normal file
95
e2etests/testdata/regression/slow_grid/dagre/sketch.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 46 KiB |
1463
e2etests/testdata/regression/slow_grid/elk/board.exp.json
generated
vendored
Normal file
95
e2etests/testdata/regression/slow_grid/elk/sketch.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 46 KiB |
24
e2etests/testdata/stable/dagger_grid/dagre/board.exp.json
generated
vendored
|
|
@ -256,7 +256,7 @@
|
||||||
"x": 280,
|
"x": 280,
|
||||||
"y": 140
|
"y": 140
|
||||||
},
|
},
|
||||||
"width": 120,
|
"width": 100,
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -294,10 +294,10 @@
|
||||||
"id": "flow8",
|
"id": "flow8",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 440,
|
"x": 420,
|
||||||
"y": 140
|
"y": 140
|
||||||
},
|
},
|
||||||
"width": 160,
|
"width": 170,
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -335,10 +335,10 @@
|
||||||
"id": "flow9",
|
"id": "flow9",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 640,
|
"x": 630,
|
||||||
"y": 140
|
"y": 140
|
||||||
},
|
},
|
||||||
"width": 160,
|
"width": 170,
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -473,7 +473,7 @@
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 508
|
"y": 508
|
||||||
},
|
},
|
||||||
"width": 139,
|
"width": 123,
|
||||||
"height": 66,
|
"height": 66,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -511,10 +511,10 @@
|
||||||
"id": "WINDOWS",
|
"id": "WINDOWS",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 179,
|
"x": 163,
|
||||||
"y": 508
|
"y": 508
|
||||||
},
|
},
|
||||||
"width": 117,
|
"width": 131,
|
||||||
"height": 66,
|
"height": 66,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -552,10 +552,10 @@
|
||||||
"id": "LINUX",
|
"id": "LINUX",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 336,
|
"x": 334,
|
||||||
"y": 508
|
"y": 508
|
||||||
},
|
},
|
||||||
"width": 139,
|
"width": 122,
|
||||||
"height": 66,
|
"height": 66,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -593,10 +593,10 @@
|
||||||
"id": "MACOS",
|
"id": "MACOS",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 515,
|
"x": 496,
|
||||||
"y": 508
|
"y": 508
|
||||||
},
|
},
|
||||||
"width": 106,
|
"width": 124,
|
||||||
"height": 66,
|
"height": 66,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 13 KiB |
24
e2etests/testdata/stable/dagger_grid/elk/board.exp.json
generated
vendored
|
|
@ -256,7 +256,7 @@
|
||||||
"x": 280,
|
"x": 280,
|
||||||
"y": 140
|
"y": 140
|
||||||
},
|
},
|
||||||
"width": 120,
|
"width": 100,
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -294,10 +294,10 @@
|
||||||
"id": "flow8",
|
"id": "flow8",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 440,
|
"x": 420,
|
||||||
"y": 140
|
"y": 140
|
||||||
},
|
},
|
||||||
"width": 160,
|
"width": 170,
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -335,10 +335,10 @@
|
||||||
"id": "flow9",
|
"id": "flow9",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 640,
|
"x": 630,
|
||||||
"y": 140
|
"y": 140
|
||||||
},
|
},
|
||||||
"width": 160,
|
"width": 170,
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -473,7 +473,7 @@
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 508
|
"y": 508
|
||||||
},
|
},
|
||||||
"width": 139,
|
"width": 123,
|
||||||
"height": 66,
|
"height": 66,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -511,10 +511,10 @@
|
||||||
"id": "WINDOWS",
|
"id": "WINDOWS",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 179,
|
"x": 163,
|
||||||
"y": 508
|
"y": 508
|
||||||
},
|
},
|
||||||
"width": 117,
|
"width": 131,
|
||||||
"height": 66,
|
"height": 66,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -552,10 +552,10 @@
|
||||||
"id": "LINUX",
|
"id": "LINUX",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 336,
|
"x": 334,
|
||||||
"y": 508
|
"y": 508
|
||||||
},
|
},
|
||||||
"width": 139,
|
"width": 122,
|
||||||
"height": 66,
|
"height": 66,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -593,10 +593,10 @@
|
||||||
"id": "MACOS",
|
"id": "MACOS",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 515,
|
"x": 496,
|
||||||
"y": 508
|
"y": 508
|
||||||
},
|
},
|
||||||
"width": 106,
|
"width": 124,
|
||||||
"height": 66,
|
"height": 66,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
|
||||||
|
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
102
e2etests/testdata/stable/grid_even/dagre/sketch.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 24 KiB |
1524
e2etests/testdata/stable/grid_even/elk/board.exp.json
generated
vendored
Normal file
102
e2etests/testdata/stable/grid_even/elk/sketch.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 24 KiB |
8
e2etests/testdata/stable/teleport_grid/dagre/board.exp.json
generated
vendored
|
|
@ -955,7 +955,7 @@
|
||||||
"x": 2204,
|
"x": 2204,
|
||||||
"y": 292
|
"y": 292
|
||||||
},
|
},
|
||||||
"width": 108,
|
"width": 103,
|
||||||
"height": 92,
|
"height": 92,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -1005,7 +1005,7 @@
|
||||||
"id": "infra.Kubernetes",
|
"id": "infra.Kubernetes",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 2352,
|
"x": 2347,
|
||||||
"y": 292
|
"y": 292
|
||||||
},
|
},
|
||||||
"width": 152,
|
"width": 152,
|
||||||
|
|
@ -1058,10 +1058,10 @@
|
||||||
"id": "infra.My SQL",
|
"id": "infra.My SQL",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 2544,
|
"x": 2539,
|
||||||
"y": 292
|
"y": 292
|
||||||
},
|
},
|
||||||
"width": 122,
|
"width": 126,
|
||||||
"height": 92,
|
"height": 92,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 54 KiB |
8
e2etests/testdata/stable/teleport_grid/elk/board.exp.json
generated
vendored
|
|
@ -955,7 +955,7 @@
|
||||||
"x": 1323,
|
"x": 1323,
|
||||||
"y": 210
|
"y": 210
|
||||||
},
|
},
|
||||||
"width": 108,
|
"width": 103,
|
||||||
"height": 92,
|
"height": 92,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
@ -1005,7 +1005,7 @@
|
||||||
"id": "infra.Kubernetes",
|
"id": "infra.Kubernetes",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 1471,
|
"x": 1466,
|
||||||
"y": 210
|
"y": 210
|
||||||
},
|
},
|
||||||
"width": 152,
|
"width": 152,
|
||||||
|
|
@ -1058,10 +1058,10 @@
|
||||||
"id": "infra.My SQL",
|
"id": "infra.My SQL",
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 1663,
|
"x": 1658,
|
||||||
"y": 210
|
"y": 210
|
||||||
},
|
},
|
||||||
"width": 122,
|
"width": 126,
|
||||||
"height": 92,
|
"height": 92,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"strokeDash": 0,
|
"strokeDash": 0,
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |