Skip to content

Commit

Permalink
Merge pull request #1380 from gavin-ts/grid-with-latex
Browse files Browse the repository at this point in the history
grid: fix dynamic layout cuts
  • Loading branch information
gavin-ts authored Jun 8, 2023
2 parents 974a8e6 + bffa156 commit f568cdd
Show file tree
Hide file tree
Showing 17 changed files with 3,566 additions and 940 deletions.
1 change: 1 addition & 0 deletions ci/release/changelogs/next.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@
all applied instead of only the last one [#1362](https://github.com/terrastruct/d2/pull/1362)
- Prevent empty block strings [#1364](https://github.com/terrastruct/d2/pull/1364)
- Fixes dagre mis-aligning a nested shape's connection. [#1370](https://github.com/terrastruct/d2/pull/1370)
- Fixes a bug in grids sometimes putting a shape on the next row/column. [#1380](https://github.com/terrastruct/d2/pull/1380)
27 changes: 21 additions & 6 deletions d2layouts/d2grid/layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
nCuts = gd.rows - 1
}
if nCuts == 0 {
return genLayout(gd.objects, nil)
return GenLayout(gd.objects, nil)
}

var bestLayout [][]*d2graph.Object
Expand Down Expand Up @@ -692,7 +692,7 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
// . A │ B │ C D E └────────────┘
// of these divisions, find the layout with rows closest to the targetSize
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)
if dist < bestDist {
bestLayout = layout
Expand Down Expand Up @@ -786,8 +786,10 @@ func (gd *gridDiagram) fastLayout(targetSize float64, nCuts int, columns bool) (
size = o.Width
}
if rowSize == 0 {
// if a single object meets the target size, end the row here
if size > targetSize-debt {
fastDivision = append(fastDivision, i-1)
// cut row with just this object
fastDivision = append(fastDivision, i)
// we build up a debt of distance past the target size across rows
newDebt := size - targetSize
debt += newDebt
Expand All @@ -797,7 +799,11 @@ func (gd *gridDiagram) fastLayout(targetSize float64, nCuts int, columns bool) (
continue
}
// debt is paid by decreasing threshold to start new row and ending below targetSize
if rowSize+(gap+size)/2. > targetSize-debt {
if rowSize+gap+(size)/2. > targetSize-debt {
// start a new row before this object since it is mostly past the target size
// . size
// ├...row─┼gap┼───┼───┤
// ├──targetSize──┤ (debt=0)
fastDivision = append(fastDivision, i-1)
newDebt := rowSize - targetSize
debt += newDebt
Expand All @@ -807,7 +813,7 @@ func (gd *gridDiagram) fastLayout(targetSize float64, nCuts int, columns bool) (
}
}
if len(fastDivision) == nCuts {
layout = genLayout(gd.objects, fastDivision)
layout = GenLayout(gd.objects, fastDivision)
}

return layout
Expand Down Expand Up @@ -885,7 +891,9 @@ func iterDivisions(objects []*d2graph.Object, nCuts int, f iterDivision, check c
}

// generate a grid of objects from the given cut indices
func genLayout(objects []*d2graph.Object, cutIndices []int) [][]*d2graph.Object {
// each cut index applies after the object at that index
// e.g. [0 1 2 3 4 5 6 7] with cutIndices [0, 2, 6] => [[0], [1, 2], [3,4,5,6], [7]]
func GenLayout(objects []*d2graph.Object, cutIndices []int) [][]*d2graph.Object {
layout := make([][]*d2graph.Object, len(cutIndices)+1)
objIndex := 0
for i := 0; i <= len(cutIndices); i++ {
Expand Down Expand Up @@ -916,6 +924,13 @@ func getDistToTarget(layout [][]*d2graph.Object, targetSize float64, horizontalG
rowSize += o.Width + horizontalGap
}
}
if len(row) > 0 {
if columns {
rowSize -= verticalGap
} else {
rowSize -= horizontalGap
}
}
totalDelta += math.Abs(rowSize - targetSize)
}
return totalDelta
Expand Down
72 changes: 72 additions & 0 deletions d2layouts/d2grid/layout_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package d2grid_test

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"

"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2layouts/d2grid"
)

func TestGenLayout(t *testing.T) {
objects := []*d2graph.Object{
{ID: "1"},
{ID: "2"},
{ID: "3"},
{ID: "4"},
{ID: "5"},
{ID: "6"},
{ID: "7"},
{ID: "8"},
}
var cutIndices []int
var layout [][]*d2graph.Object
cutIndices = []int{0}
layout = d2grid.GenLayout(objects, cutIndices)
fmt.Printf("layout %v\n", len(layout))
assert.Equalf(t, len(cutIndices)+1, len(layout), "expected 2 rows from 1 cut")
assert.Equalf(t, 1, len(layout[0]), "expected first row to be 1 object")
assert.Equalf(t, 7, len(layout[1]), "expected second row to be 7 objects")
assert.Equalf(t, objects[0].ID, layout[0][0].ID, "expected first object to be 1")

cutIndices = []int{6}
layout = d2grid.GenLayout(objects, cutIndices)
assert.Equalf(t, len(cutIndices)+1, len(layout), "expected 2 rows from 1 cut")
assert.Equalf(t, 7, len(layout[0]), "expected first row to be 7 objects")
assert.Equalf(t, 1, len(layout[1]), "expected second row to be 1 object")

cutIndices = []int{0, 6}
layout = d2grid.GenLayout(objects, cutIndices)
assert.Equalf(t, len(cutIndices)+1, len(layout), "expected 3 rows from 2 cuts")
assert.Equalf(t, 1, len(layout[0]), "expected first row to be 1 objects")
assert.Equalf(t, 6, len(layout[1]), "expected second row to be 6 objects")
assert.Equalf(t, 1, len(layout[2]), "expected second row to be 1 object")

cutIndices = []int{1, 5}
layout = d2grid.GenLayout(objects, cutIndices)
assert.Equalf(t, len(cutIndices)+1, len(layout), "expected 3 rows from 2 cuts")
assert.Equalf(t, 2, len(layout[0]), "expected first row to be 2 objects")
assert.Equalf(t, 4, len(layout[1]), "expected second row to be 6 objects")
assert.Equalf(t, 2, len(layout[2]), "expected second row to be 2 object")

cutIndices = []int{5}
layout = d2grid.GenLayout(objects, cutIndices)
assert.Equalf(t, len(cutIndices)+1, len(layout), "expected 2 rows from 1 cut")
assert.Equalf(t, 6, len(layout[0]), "expected first row to be 6 objects")
assert.Equalf(t, 2, len(layout[1]), "expected second row to be 2 object")

cutIndices = []int{1}
layout = d2grid.GenLayout(objects, cutIndices)
assert.Equalf(t, len(cutIndices)+1, len(layout), "expected 2 rows from 1 cut")
assert.Equalf(t, 2, len(layout[0]), "expected first row to be 2 object")
assert.Equalf(t, 6, len(layout[1]), "expected second row to be 6 objects")

cutIndices = []int{0, 1, 2, 3, 4, 5, 6}
layout = d2grid.GenLayout(objects, cutIndices)
assert.Equalf(t, len(cutIndices)+1, len(layout), "expected 3 rows from 2 cuts")
for i := range layout {
assert.Equalf(t, 1, len(layout[i]), "expected row %d to be 1 object", i)
}
}
1 change: 1 addition & 0 deletions e2etests/regression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,7 @@ cf many required: {
loadFromFile(t, "slow_grid"),
loadFromFile(t, "grid_oom"),
loadFromFile(t, "cylinder_grid_label"),
loadFromFile(t, "grid_with_latex"),
}

runa(t, tcs)
Expand Down
27 changes: 27 additions & 0 deletions e2etests/testdata/files/grid_with_latex.d2
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
x: {
grid-columns: 2

a.width: 50
a.height: 72
b.width: 50
b.height: 30
}

y: {
grid-columns: 2

a.width: 50
a.height: 73
b.width: 50
b.height: 30
}

z: {
grid-columns: 2
lim: |latex
\\lim_{h \\rightarrow 0 } \\frac{f(x+h)-f(x)}{h}
|
add: |latex
1 + 1
|
}
Loading

0 comments on commit f568cdd

Please sign in to comment.