Change sorting order to be multi level
This commit is contained in:
parent
399b96ca0e
commit
684b72eb5a
5 changed files with 125 additions and 21 deletions
|
|
@ -8,7 +8,6 @@ import (
|
|||
"oss.terrastruct.com/d2/d2target"
|
||||
"oss.terrastruct.com/d2/d2themes"
|
||||
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
|
||||
"oss.terrastruct.com/d2/lib/go2"
|
||||
)
|
||||
|
||||
func Export(ctx context.Context, g *d2graph.Graph, themeID int64) (*d2target.Diagram, error) {
|
||||
|
|
@ -17,16 +16,13 @@ func Export(ctx context.Context, g *d2graph.Graph, themeID int64) (*d2target.Dia
|
|||
diagram := d2target.NewDiagram()
|
||||
|
||||
diagram.Shapes = make([]d2target.Shape, len(g.Objects))
|
||||
maxObjectZIndex := 0
|
||||
for i := range g.Objects {
|
||||
diagram.Shapes[i] = toShape(g.Objects[i], &theme)
|
||||
maxObjectZIndex = go2.IntMax(maxObjectZIndex, diagram.Shapes[i].ZIndex)
|
||||
}
|
||||
|
||||
edgeDefaultZIndex := maxObjectZIndex + 1
|
||||
diagram.Connections = make([]d2target.Connection, len(g.Edges))
|
||||
for i := range g.Edges {
|
||||
diagram.Connections[i] = toConnection(g.Edges[i], &theme, edgeDefaultZIndex)
|
||||
diagram.Connections[i] = toConnection(g.Edges[i], &theme)
|
||||
}
|
||||
|
||||
return diagram, nil
|
||||
|
|
@ -92,11 +88,8 @@ func toShape(obj *d2graph.Object, theme *d2themes.Theme) d2target.Shape {
|
|||
shape := d2target.BaseShape()
|
||||
shape.SetType(obj.Attributes.Shape.Value)
|
||||
shape.ID = obj.AbsID()
|
||||
if obj.ZIndex == nil {
|
||||
shape.ZIndex = int(obj.Level())
|
||||
} else {
|
||||
shape.ZIndex = *obj.ZIndex
|
||||
}
|
||||
shape.ZIndex = obj.ZIndex
|
||||
shape.Level = int(obj.Level())
|
||||
shape.Pos = d2target.NewPoint(int(obj.TopLeft.X), int(obj.TopLeft.Y))
|
||||
shape.Width = int(obj.Width)
|
||||
shape.Height = int(obj.Height)
|
||||
|
|
@ -138,14 +131,10 @@ func toShape(obj *d2graph.Object, theme *d2themes.Theme) d2target.Shape {
|
|||
return *shape
|
||||
}
|
||||
|
||||
func toConnection(edge *d2graph.Edge, theme *d2themes.Theme, defaultZIndex int) d2target.Connection {
|
||||
func toConnection(edge *d2graph.Edge, theme *d2themes.Theme) d2target.Connection {
|
||||
connection := d2target.BaseConnection()
|
||||
connection.ID = edge.AbsID()
|
||||
if edge.ZIndex == nil {
|
||||
connection.ZIndex = defaultZIndex
|
||||
} else {
|
||||
connection.ZIndex = *edge.ZIndex
|
||||
}
|
||||
connection.ZIndex = edge.ZIndex
|
||||
// edge.Edge.ID = go2.StringToIntHash(connection.ID)
|
||||
text := edge.Text()
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ type Object struct {
|
|||
|
||||
Attributes Attributes `json:"attributes"`
|
||||
|
||||
ZIndex *int `json:"zIndex,omitempty"`
|
||||
ZIndex int `json:"zIndex"`
|
||||
}
|
||||
|
||||
type Attributes struct {
|
||||
|
|
@ -633,7 +633,7 @@ type Edge struct {
|
|||
References []EdgeReference `json:"references,omitempty"`
|
||||
Attributes Attributes `json:"attributes"`
|
||||
|
||||
ZIndex *int `json:"zIndex,omitempty"`
|
||||
ZIndex int `json:"zIndex"`
|
||||
}
|
||||
|
||||
type EdgeReference struct {
|
||||
|
|
|
|||
|
|
@ -993,9 +993,7 @@ func Render(diagram *d2target.Diagram) ([]byte, error) {
|
|||
allObjects = append(allObjects, c)
|
||||
}
|
||||
|
||||
sort.SliceStable(allObjects, func(i, j int) bool {
|
||||
return allObjects[i].GetZIndex() < allObjects[j].GetZIndex()
|
||||
})
|
||||
sortObjects(allObjects)
|
||||
|
||||
markers := map[string]struct{}{}
|
||||
for _, obj := range allObjects {
|
||||
|
|
@ -1017,6 +1015,34 @@ func Render(diagram *d2target.Diagram) ([]byte, error) {
|
|||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// sortObjects sorts all diagrams objects (shapes and connections) in the desired drawing order
|
||||
// the sorting criteria is:
|
||||
// 1. zIndex, lower comes first
|
||||
// 2. two shapes with the same zIndex are sorted by their level (container nesting), containers come first
|
||||
// 3. two shapes with the same zIndex and same level, are sorted in the order they were exported
|
||||
// 4. shape and edge, shapes come first
|
||||
func sortObjects(allObjects []d2target.DiagramObject) {
|
||||
sort.SliceStable(allObjects, func(i, j int) bool {
|
||||
// first sort by zIndex
|
||||
iZIndex := allObjects[i].GetZIndex()
|
||||
jZIndex := allObjects[j].GetZIndex()
|
||||
if iZIndex != jZIndex {
|
||||
return iZIndex < jZIndex
|
||||
}
|
||||
|
||||
// then, if both are shapes, the containers come first
|
||||
iShape, iIsShape := allObjects[i].(d2target.Shape)
|
||||
jShape, jIsShape := allObjects[j].(d2target.Shape)
|
||||
if iIsShape && jIsShape {
|
||||
return iShape.Level < jShape.Level
|
||||
}
|
||||
|
||||
// then, shapes come before connections
|
||||
_, jIsConnection := allObjects[j].(d2target.Connection)
|
||||
return iIsShape && jIsConnection
|
||||
})
|
||||
}
|
||||
|
||||
func hash(s string) string {
|
||||
const secret = "lalalas"
|
||||
h := fnv.New32a()
|
||||
|
|
|
|||
79
d2renderers/d2svg/d2svg_test.go
Normal file
79
d2renderers/d2svg/d2svg_test.go
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package d2svg
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"oss.terrastruct.com/d2/d2target"
|
||||
)
|
||||
|
||||
func TestSortObjects(t *testing.T) {
|
||||
allObjects := []d2target.DiagramObject{
|
||||
// same zIndex and level, should keep in this order
|
||||
d2target.Shape{
|
||||
ID: "0",
|
||||
ZIndex: 0,
|
||||
Level: 0,
|
||||
},
|
||||
d2target.Shape{
|
||||
ID: "1",
|
||||
ZIndex: 0,
|
||||
Level: 0,
|
||||
},
|
||||
// same zIndex, different level, should be swapped
|
||||
d2target.Shape{
|
||||
ID: "2",
|
||||
ZIndex: 0,
|
||||
Level: 1,
|
||||
},
|
||||
d2target.Shape{
|
||||
ID: "3",
|
||||
ZIndex: 0,
|
||||
Level: 0,
|
||||
},
|
||||
// different zIndex, should come after connections
|
||||
d2target.Shape{
|
||||
ID: "4",
|
||||
ZIndex: 1,
|
||||
Level: 0,
|
||||
},
|
||||
// connections come after shapes
|
||||
d2target.Connection{
|
||||
ID: "5",
|
||||
ZIndex: 0,
|
||||
},
|
||||
d2target.Connection{
|
||||
ID: "6",
|
||||
ZIndex: 0,
|
||||
},
|
||||
// this should be last object
|
||||
d2target.Connection{
|
||||
ID: "7",
|
||||
ZIndex: 2,
|
||||
},
|
||||
// this should be the first object
|
||||
d2target.Connection{
|
||||
ID: "8",
|
||||
ZIndex: -1,
|
||||
},
|
||||
}
|
||||
|
||||
expectedOrder := []d2target.DiagramObject{
|
||||
allObjects[8],
|
||||
allObjects[0],
|
||||
allObjects[1],
|
||||
allObjects[3],
|
||||
allObjects[2],
|
||||
allObjects[5],
|
||||
allObjects[6],
|
||||
allObjects[4],
|
||||
allObjects[7],
|
||||
}
|
||||
|
||||
sortObjects(allObjects)
|
||||
|
||||
for i := 0; i < len(allObjects); i++ {
|
||||
if allObjects[i].GetID() != expectedOrder[i].GetID() {
|
||||
t.Fatalf("object order differs at index %d, got '%s' expected '%s'", i, allObjects[i].GetID(), expectedOrder[i].GetID())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ const (
|
|||
)
|
||||
|
||||
type DiagramObject interface {
|
||||
GetID() string
|
||||
GetZIndex() int
|
||||
}
|
||||
|
||||
|
|
@ -122,6 +123,7 @@ type Shape struct {
|
|||
LabelPosition string `json:"labelPosition,omitempty"`
|
||||
|
||||
ZIndex int `json:"zIndex"`
|
||||
Level int `json:"level"`
|
||||
}
|
||||
|
||||
func (s *Shape) SetType(t string) {
|
||||
|
|
@ -139,6 +141,10 @@ func (s Shape) GetZIndex() int {
|
|||
return s.ZIndex
|
||||
}
|
||||
|
||||
func (s Shape) GetID() string {
|
||||
return s.ID
|
||||
}
|
||||
|
||||
type Text struct {
|
||||
Label string `json:"label"`
|
||||
FontSize int `json:"fontSize"`
|
||||
|
|
@ -225,6 +231,10 @@ func (c Connection) GetZIndex() int {
|
|||
return c.ZIndex
|
||||
}
|
||||
|
||||
func (c Connection) GetID() string {
|
||||
return c.ID
|
||||
}
|
||||
|
||||
type Arrowhead string
|
||||
|
||||
const (
|
||||
|
|
|
|||
Loading…
Reference in a new issue