|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
}
|
||||
|
|
|
|||
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
|
||||
`,
|
||||
},
|
||||
loadFromFile(t, "slow_grid"),
|
||||
loadFromFile(t, "grid_oom"),
|
||||
}
|
||||
|
||||
runa(t, tcs)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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,
|
||||
"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,
|
||||
|
|
|
|||
|
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,
|
||||
"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,
|
||||
|
|
|
|||
|
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,
|
||||
"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,
|
||||
|
|
|
|||
|
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,
|
||||
"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,
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |