From 297819dc3512d9f0ae8b48ddacc342f7dd36d028 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Fri, 26 May 2023 16:38:20 -0700 Subject: [PATCH 1/9] grid: don't overwrite nested graph label positions --- d2layouts/d2grid/layout.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/d2layouts/d2grid/layout.go b/d2layouts/d2grid/layout.go index 5ddc75839..26bfb26b7 100644 --- a/d2layouts/d2grid/layout.go +++ b/d2layouts/d2grid/layout.go @@ -139,7 +139,9 @@ func withoutGridDiagrams(ctx context.Context, g *d2graph.Graph, layout d2graph.L } } - obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) + if obj.LabelPosition == nil { + obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) + } gridDiagrams[obj.AbsID()] = gd for _, o := range gd.objects { @@ -191,10 +193,17 @@ func layoutGrid(g *d2graph.Graph, obj *d2graph.Object) (*gridDiagram, error) { // position labels and icons for _, o := range gd.objects { if o.Icon != nil { - o.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) - o.IconPosition = go2.Pointer(string(label.InsideMiddleCenter)) + // don't overwrite position if nested graph layout positioned label/icon + if o.LabelPosition == nil { + o.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) + } + if o.IconPosition == nil { + o.IconPosition = go2.Pointer(string(label.InsideMiddleCenter)) + } } else { - o.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter)) + if o.LabelPosition == nil { + o.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter)) + } } } From 6d8dde153e409abbacd365742d6cd836a758a8f7 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Fri, 26 May 2023 17:47:31 -0700 Subject: [PATCH 2/9] adjust grid for outside top labels --- d2graph/d2graph.go | 34 +++++++++++++++++++++++++++ d2layouts/d2dagrelayout/layout.go | 8 +++---- d2layouts/d2elklayout/layout.go | 4 ++-- d2layouts/d2grid/layout.go | 38 +++++++++++++++++++++++++------ 4 files changed, 70 insertions(+), 14 deletions(-) diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go index 006bfae85..f43837ef8 100644 --- a/d2graph/d2graph.go +++ b/d2graph/d2graph.go @@ -26,6 +26,7 @@ import ( "oss.terrastruct.com/d2/d2themes/d2themescatalog" "oss.terrastruct.com/d2/lib/color" "oss.terrastruct.com/d2/lib/geo" + "oss.terrastruct.com/d2/lib/label" "oss.terrastruct.com/d2/lib/shape" "oss.terrastruct.com/d2/lib/textmeasure" ) @@ -1989,3 +1990,36 @@ func (obj *Object) GetModifierElementAdjustments() (dx, dy float64) { } return dx, dy } + +func (obj *Object) ToShape() shape.Shape { + tl := obj.TopLeft + if tl == nil { + tl = geo.NewPoint(0, 0) + } + dslShape := strings.ToLower(obj.Shape.Value) + shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[dslShape] + contentBox := geo.NewBox(tl, obj.Width, obj.Height) + return shape.NewShape(shapeType, contentBox) +} + +func (obj *Object) GetLabelTopLeft() *geo.Point { + if obj.LabelPosition == nil { + return nil + } + + s := obj.ToShape() + labelPosition := label.Position(*obj.LabelPosition) + + var box *geo.Box + if labelPosition.IsOutside() { + box = s.GetBox() + } else { + box = s.GetInnerBox() + } + + labelTL := labelPosition.GetPointOnBox(box, label.PADDING, + float64(obj.LabelDimensions.Width), + float64(obj.LabelDimensions.Height), + ) + return labelTL +} diff --git a/d2layouts/d2dagrelayout/layout.go b/d2layouts/d2dagrelayout/layout.go index db9c4f4b3..39cd1f9fe 100644 --- a/d2layouts/d2dagrelayout/layout.go +++ b/d2layouts/d2dagrelayout/layout.go @@ -119,9 +119,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err } if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage { - contentBox := geo.NewBox(geo.NewPoint(0, 0), float64(obj.Width), float64(obj.Height)) - shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[obj.Shape.Value] - s := shape.NewShape(shapeType, contentBox) + s := obj.ToShape() iconSize := d2target.GetIconSize(s.GetInnerBox(), string(label.InsideTopLeft)) // Since dagre container labels are pushed up, we don't want a child container to collide maxContainerLabelHeight = go2.Max(maxContainerLabelHeight, (iconSize+label.PADDING*2)*2) @@ -491,8 +489,8 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err } } - srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Shape.Value)], edge.Src.Box) - dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Shape.Value)], edge.Dst.Box) + srcShape := edge.Src.ToShape() + dstShape := edge.Dst.ToShape() // trace the edge to the specific shape's border points[startIndex] = shape.TraceToShapeBorder(srcShape, start, points[startIndex+1]) diff --git a/d2layouts/d2elklayout/layout.go b/d2layouts/d2elklayout/layout.go index 5c29020a2..be6687f7e 100644 --- a/d2layouts/d2elklayout/layout.go +++ b/d2layouts/d2elklayout/layout.go @@ -501,8 +501,8 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err } } - srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Shape.Value)], edge.Src.Box) - dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Shape.Value)], edge.Dst.Box) + srcShape := edge.Src.ToShape() + dstShape := edge.Dst.ToShape() // trace the edge to the specific shape's border points[startIndex] = shape.TraceToShapeBorder(srcShape, points[startIndex], points[startIndex+1]) diff --git a/d2layouts/d2grid/layout.go b/d2layouts/d2grid/layout.go index 26bfb26b7..6b747f7b3 100644 --- a/d2layouts/d2grid/layout.go +++ b/d2layouts/d2grid/layout.go @@ -6,13 +6,10 @@ import ( "fmt" "math" "sort" - "strings" "oss.terrastruct.com/d2/d2graph" - "oss.terrastruct.com/d2/d2target" "oss.terrastruct.com/d2/lib/geo" "oss.terrastruct.com/d2/lib/label" - "oss.terrastruct.com/d2/lib/shape" "oss.terrastruct.com/util-go/go2" ) @@ -104,13 +101,12 @@ func withoutGridDiagrams(ctx context.Context, g *d2graph.Graph, layout d2graph.L if obj.GridGap != nil || obj.VerticalGap != nil { verticalPadding = gd.verticalGap } + // size shape according to grid - obj.SizeToContent(float64(gd.width), float64(gd.height), float64(2*horizontalPadding), float64(2*verticalPadding)) + obj.SizeToContent(gd.width, gd.height, float64(2*horizontalPadding), float64(2*verticalPadding)) // compute where the grid should be placed inside shape - dslShape := strings.ToLower(obj.Shape.Value) - shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[dslShape] - s := shape.NewShape(shapeType, geo.NewBox(geo.NewPoint(0, 0), obj.Width, obj.Height)) + s := obj.ToShape() innerBox := s.GetInnerBox() if innerBox.TopLeft.X != 0 || innerBox.TopLeft.Y != 0 { gd.shift(innerBox.TopLeft.X, innerBox.TopLeft.Y) @@ -126,6 +122,34 @@ func withoutGridDiagrams(ctx context.Context, g *d2graph.Graph, layout d2graph.L } if obj.LabelDimensions.Height != 0 { labelHeight := float64(obj.LabelDimensions.Height) + 2*label.PADDING + + { + // also check for grid cells with outside top labels + topY := gd.objects[0].TopLeft.Y + highestLabel := topY + for _, o := range gd.objects { + if o.TopLeft.Y > topY { + if gd.rowDirected { + break + } else { + continue + } + } + if o.LabelPosition != nil { + labelPosition := label.Position(*o.LabelPosition) + if labelPosition.IsOutside() { + labelTL := o.GetLabelTopLeft() + if labelTL.Y < highestLabel { + highestLabel = labelTL.Y + } + } + } + } + if highestLabel < topY { + labelHeight += topY - highestLabel + 2*label.PADDING + } + } + if labelHeight > float64(verticalPadding) { // if the label doesn't fit within the padding, we need to add more grow := labelHeight - float64(verticalPadding) From 6ee5204b4dce226fc4e01e3e0dfc0924744a2373 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Fri, 26 May 2023 17:48:37 -0700 Subject: [PATCH 3/9] update tests --- .../stable/grid_nested/dagre/board.exp.json | 100 +++++------ .../stable/grid_nested/dagre/sketch.exp.svg | 160 +++++++++--------- .../stable/grid_nested/elk/board.exp.json | 4 +- .../stable/grid_nested/elk/sketch.exp.svg | 158 ++++++++--------- 4 files changed, 211 insertions(+), 211 deletions(-) diff --git a/e2etests/testdata/stable/grid_nested/dagre/board.exp.json b/e2etests/testdata/stable/grid_nested/dagre/board.exp.json index 7a47434c9..539206801 100644 --- a/e2etests/testdata/stable/grid_nested/dagre/board.exp.json +++ b/e2etests/testdata/stable/grid_nested/dagre/board.exp.json @@ -14,7 +14,7 @@ "y": 150 }, "width": 384, - "height": 356, + "height": 388, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -52,7 +52,7 @@ "type": "rectangle", "pos": { "x": 60, - "y": 210 + "y": 242 }, "width": 53, "height": 130, @@ -93,7 +93,7 @@ "type": "rectangle", "pos": { "x": 153, - "y": 210 + "y": 242 }, "width": 171, "height": 130, @@ -125,7 +125,7 @@ "underline": false, "labelWidth": 12, "labelHeight": 31, - "labelPosition": "INSIDE_MIDDLE_CENTER", + "labelPosition": "OUTSIDE_TOP_CENTER", "zIndex": 0, "level": 2 }, @@ -134,7 +134,7 @@ "type": "rectangle", "pos": { "x": 193, - "y": 242 + "y": 274 }, "width": 91, "height": 66, @@ -175,7 +175,7 @@ "type": "rectangle", "pos": { "x": 60, - "y": 380 + "y": 412 }, "width": 53, "height": 66, @@ -216,7 +216,7 @@ "type": "rectangle", "pos": { "x": 153, - "y": 380 + "y": 412 }, "width": 171, "height": 66, @@ -263,7 +263,7 @@ "y": 0 }, "width": 692, - "height": 656, + "height": 688, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -301,7 +301,7 @@ "type": "rectangle", "pos": { "x": 504, - "y": 60 + "y": 92 }, "width": 53, "height": 430, @@ -342,7 +342,7 @@ "type": "rectangle", "pos": { "x": 597, - "y": 60 + "y": 92 }, "width": 479, "height": 430, @@ -374,7 +374,7 @@ "underline": false, "labelWidth": 12, "labelHeight": 31, - "labelPosition": "INSIDE_MIDDLE_CENTER", + "labelPosition": "OUTSIDE_TOP_CENTER", "zIndex": 0, "level": 2 }, @@ -383,7 +383,7 @@ "type": "rectangle", "pos": { "x": 617, - "y": 123 + "y": 155 }, "width": 439, "height": 335, @@ -424,7 +424,7 @@ "type": "rectangle", "pos": { "x": 637, - "y": 183 + "y": 215 }, "width": 186, "height": 240, @@ -465,7 +465,7 @@ "type": "rectangle", "pos": { "x": 657, - "y": 246 + "y": 278 }, "width": 146, "height": 140, @@ -506,7 +506,7 @@ "type": "rectangle", "pos": { "x": 697, - "y": 283 + "y": 315 }, "width": 66, "height": 66, @@ -547,7 +547,7 @@ "type": "rectangle", "pos": { "x": 843, - "y": 183 + "y": 215 }, "width": 193, "height": 240, @@ -588,7 +588,7 @@ "type": "rectangle", "pos": { "x": 863, - "y": 246 + "y": 278 }, "width": 153, "height": 140, @@ -629,7 +629,7 @@ "type": "rectangle", "pos": { "x": 903, - "y": 283 + "y": 315 }, "width": 73, "height": 66, @@ -670,7 +670,7 @@ "type": "rectangle", "pos": { "x": 504, - "y": 530 + "y": 562 }, "width": 53, "height": 66, @@ -711,7 +711,7 @@ "type": "rectangle", "pos": { "x": 597, - "y": 530 + "y": 562 }, "width": 479, "height": 66, @@ -755,7 +755,7 @@ ], "pos": { "x": 1196, - "y": 69 + "y": 85 }, "width": 480, "height": 518, @@ -796,7 +796,7 @@ "type": "rectangle", "pos": { "x": 1256, - "y": 129 + "y": 145 }, "width": 53, "height": 292, @@ -840,7 +840,7 @@ ], "pos": { "x": 1349, - "y": 129 + "y": 145 }, "width": 267, "height": 292, @@ -881,7 +881,7 @@ "type": "rectangle", "pos": { "x": 1409, - "y": 189 + "y": 205 }, "width": 53, "height": 66, @@ -922,7 +922,7 @@ "type": "rectangle", "pos": { "x": 1502, - "y": 189 + "y": 205 }, "width": 54, "height": 66, @@ -963,7 +963,7 @@ "type": "rectangle", "pos": { "x": 1409, - "y": 295 + "y": 311 }, "width": 53, "height": 66, @@ -1004,7 +1004,7 @@ "type": "rectangle", "pos": { "x": 1502, - "y": 295 + "y": 311 }, "width": 54, "height": 66, @@ -1045,7 +1045,7 @@ "type": "rectangle", "pos": { "x": 1256, - "y": 461 + "y": 477 }, "width": 53, "height": 66, @@ -1086,7 +1086,7 @@ "type": "rectangle", "pos": { "x": 1349, - "y": 461 + "y": 477 }, "width": 267, "height": 66, @@ -1127,7 +1127,7 @@ "type": "rectangle", "pos": { "x": 1736, - "y": 38 + "y": 54 }, "width": 321, "height": 581, @@ -1168,7 +1168,7 @@ "type": "rectangle", "pos": { "x": 1736, - "y": 84 + "y": 100 }, "width": 53, "height": 469, @@ -1209,7 +1209,7 @@ "type": "rectangle", "pos": { "x": 1789, - "y": 84 + "y": 100 }, "width": 268, "height": 469, @@ -1250,7 +1250,7 @@ "type": "rectangle", "pos": { "x": 1789, - "y": 125 + "y": 141 }, "width": 214, "height": 66, @@ -1291,7 +1291,7 @@ "type": "rectangle", "pos": { "x": 2003, - "y": 125 + "y": 141 }, "width": 54, "height": 66, @@ -1332,7 +1332,7 @@ "type": "rectangle", "pos": { "x": 1789, - "y": 191 + "y": 207 }, "width": 214, "height": 362, @@ -1373,7 +1373,7 @@ "type": "rectangle", "pos": { "x": 1789, - "y": 227 + "y": 243 }, "width": 53, "height": 66, @@ -1414,7 +1414,7 @@ "type": "rectangle", "pos": { "x": 1842, - "y": 227 + "y": 243 }, "width": 161, "height": 66, @@ -1455,7 +1455,7 @@ "type": "rectangle", "pos": { "x": 1789, - "y": 293 + "y": 309 }, "width": 53, "height": 260, @@ -1496,7 +1496,7 @@ "type": "rectangle", "pos": { "x": 1842, - "y": 293 + "y": 309 }, "width": 161, "height": 260, @@ -1537,7 +1537,7 @@ "type": "rectangle", "pos": { "x": 1842, - "y": 324 + "y": 340 }, "width": 107, "height": 163, @@ -1578,7 +1578,7 @@ "type": "rectangle", "pos": { "x": 1842, - "y": 355 + "y": 371 }, "width": 53, "height": 66, @@ -1619,7 +1619,7 @@ "type": "rectangle", "pos": { "x": 1895, - "y": 355 + "y": 371 }, "width": 54, "height": 66, @@ -1660,7 +1660,7 @@ "type": "rectangle", "pos": { "x": 1842, - "y": 421 + "y": 437 }, "width": 53, "height": 66, @@ -1701,7 +1701,7 @@ "type": "rectangle", "pos": { "x": 1895, - "y": 421 + "y": 437 }, "width": 54, "height": 66, @@ -1742,7 +1742,7 @@ "type": "rectangle", "pos": { "x": 1949, - "y": 324 + "y": 340 }, "width": 54, "height": 163, @@ -1783,7 +1783,7 @@ "type": "rectangle", "pos": { "x": 1842, - "y": 487 + "y": 503 }, "width": 107, "height": 66, @@ -1824,7 +1824,7 @@ "type": "rectangle", "pos": { "x": 1949, - "y": 487 + "y": 503 }, "width": 54, "height": 66, @@ -1865,7 +1865,7 @@ "type": "rectangle", "pos": { "x": 2003, - "y": 191 + "y": 207 }, "width": 54, "height": 362, @@ -1906,7 +1906,7 @@ "type": "rectangle", "pos": { "x": 1736, - "y": 553 + "y": 569 }, "width": 53, "height": 66, @@ -1947,7 +1947,7 @@ "type": "rectangle", "pos": { "x": 1789, - "y": 553 + "y": 569 }, "width": 268, "height": 66, diff --git a/e2etests/testdata/stable/grid_nested/dagre/sketch.exp.svg b/e2etests/testdata/stable/grid_nested/dagre/sketch.exp.svg index 136a16b78..7812cc589 100644 --- a/e2etests/testdata/stable/grid_nested/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/grid_nested/dagre/sketch.exp.svg @@ -1,16 +1,16 @@ -grid w/ containergrid w/ nested containersgrid in gridgrid w/ grid w/ gridabcdabcdabcdabcdb childb 1abcdabcdb 2b 2aabcdb 3b 3aabcdb 4b 3aabcd - + .d2-2603497423 .fill-N1{fill:#0A0F25;} + .d2-2603497423 .fill-N2{fill:#676C7E;} + .d2-2603497423 .fill-N3{fill:#9499AB;} + .d2-2603497423 .fill-N4{fill:#CFD2DD;} + .d2-2603497423 .fill-N5{fill:#DEE1EB;} + .d2-2603497423 .fill-N6{fill:#EEF1F8;} + .d2-2603497423 .fill-N7{fill:#FFFFFF;} + .d2-2603497423 .fill-B1{fill:#0D32B2;} + .d2-2603497423 .fill-B2{fill:#0D32B2;} + .d2-2603497423 .fill-B3{fill:#E3E9FD;} + .d2-2603497423 .fill-B4{fill:#E3E9FD;} + .d2-2603497423 .fill-B5{fill:#EDF0FD;} + .d2-2603497423 .fill-B6{fill:#F7F8FE;} + .d2-2603497423 .fill-AA2{fill:#4A6FF3;} + .d2-2603497423 .fill-AA4{fill:#EDF0FD;} + .d2-2603497423 .fill-AA5{fill:#F7F8FE;} + .d2-2603497423 .fill-AB4{fill:#EDF0FD;} + .d2-2603497423 .fill-AB5{fill:#F7F8FE;} + .d2-2603497423 .stroke-N1{stroke:#0A0F25;} + .d2-2603497423 .stroke-N2{stroke:#676C7E;} + .d2-2603497423 .stroke-N3{stroke:#9499AB;} + .d2-2603497423 .stroke-N4{stroke:#CFD2DD;} + .d2-2603497423 .stroke-N5{stroke:#DEE1EB;} + .d2-2603497423 .stroke-N6{stroke:#EEF1F8;} + .d2-2603497423 .stroke-N7{stroke:#FFFFFF;} + .d2-2603497423 .stroke-B1{stroke:#0D32B2;} + .d2-2603497423 .stroke-B2{stroke:#0D32B2;} + .d2-2603497423 .stroke-B3{stroke:#E3E9FD;} + .d2-2603497423 .stroke-B4{stroke:#E3E9FD;} + .d2-2603497423 .stroke-B5{stroke:#EDF0FD;} + .d2-2603497423 .stroke-B6{stroke:#F7F8FE;} + .d2-2603497423 .stroke-AA2{stroke:#4A6FF3;} + .d2-2603497423 .stroke-AA4{stroke:#EDF0FD;} + .d2-2603497423 .stroke-AA5{stroke:#F7F8FE;} + .d2-2603497423 .stroke-AB4{stroke:#EDF0FD;} + .d2-2603497423 .stroke-AB5{stroke:#F7F8FE;} + .d2-2603497423 .background-color-N1{background-color:#0A0F25;} + .d2-2603497423 .background-color-N2{background-color:#676C7E;} + .d2-2603497423 .background-color-N3{background-color:#9499AB;} + .d2-2603497423 .background-color-N4{background-color:#CFD2DD;} + .d2-2603497423 .background-color-N5{background-color:#DEE1EB;} + .d2-2603497423 .background-color-N6{background-color:#EEF1F8;} + .d2-2603497423 .background-color-N7{background-color:#FFFFFF;} + .d2-2603497423 .background-color-B1{background-color:#0D32B2;} + .d2-2603497423 .background-color-B2{background-color:#0D32B2;} + .d2-2603497423 .background-color-B3{background-color:#E3E9FD;} + .d2-2603497423 .background-color-B4{background-color:#E3E9FD;} + .d2-2603497423 .background-color-B5{background-color:#EDF0FD;} + .d2-2603497423 .background-color-B6{background-color:#F7F8FE;} + .d2-2603497423 .background-color-AA2{background-color:#4A6FF3;} + .d2-2603497423 .background-color-AA4{background-color:#EDF0FD;} + .d2-2603497423 .background-color-AA5{background-color:#F7F8FE;} + .d2-2603497423 .background-color-AB4{background-color:#EDF0FD;} + .d2-2603497423 .background-color-AB5{background-color:#F7F8FE;} + .d2-2603497423 .color-N1{color:#0A0F25;} + .d2-2603497423 .color-N2{color:#676C7E;} + .d2-2603497423 .color-N3{color:#9499AB;} + .d2-2603497423 .color-N4{color:#CFD2DD;} + .d2-2603497423 .color-N5{color:#DEE1EB;} + .d2-2603497423 .color-N6{color:#EEF1F8;} + .d2-2603497423 .color-N7{color:#FFFFFF;} + .d2-2603497423 .color-B1{color:#0D32B2;} + .d2-2603497423 .color-B2{color:#0D32B2;} + .d2-2603497423 .color-B3{color:#E3E9FD;} + .d2-2603497423 .color-B4{color:#E3E9FD;} + .d2-2603497423 .color-B5{color:#EDF0FD;} + .d2-2603497423 .color-B6{color:#F7F8FE;} + .d2-2603497423 .color-AA2{color:#4A6FF3;} + .d2-2603497423 .color-AA4{color:#EDF0FD;} + .d2-2603497423 .color-AA5{color:#F7F8FE;} + .d2-2603497423 .color-AB4{color:#EDF0FD;} + .d2-2603497423 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>grid w/ containergrid w/ nested containersgrid in gridgrid w/ grid w/ gridabcdabcdabcdabcdb childb 1abcdabcdb 2b 2aabcdb 3b 3aabcdb 4b 3aabcd + \ No newline at end of file diff --git a/e2etests/testdata/stable/grid_nested/elk/board.exp.json b/e2etests/testdata/stable/grid_nested/elk/board.exp.json index 09aabada3..08a18ac92 100644 --- a/e2etests/testdata/stable/grid_nested/elk/board.exp.json +++ b/e2etests/testdata/stable/grid_nested/elk/board.exp.json @@ -125,7 +125,7 @@ "underline": false, "labelWidth": 12, "labelHeight": 31, - "labelPosition": "INSIDE_MIDDLE_CENTER", + "labelPosition": "INSIDE_TOP_CENTER", "zIndex": 0, "level": 2 }, @@ -374,7 +374,7 @@ "underline": false, "labelWidth": 12, "labelHeight": 31, - "labelPosition": "INSIDE_MIDDLE_CENTER", + "labelPosition": "INSIDE_TOP_CENTER", "zIndex": 0, "level": 2 }, diff --git a/e2etests/testdata/stable/grid_nested/elk/sketch.exp.svg b/e2etests/testdata/stable/grid_nested/elk/sketch.exp.svg index cb2d63c26..9a8649b41 100644 --- a/e2etests/testdata/stable/grid_nested/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/grid_nested/elk/sketch.exp.svg @@ -1,16 +1,16 @@ -grid w/ containergrid w/ nested containersgrid in gridgrid w/ grid w/ gridabcdabcdabcdabcdb childb 1abcdabcdb 2b 2aabcdb 3b 3aabcdb 4b 3aabcd + .d2-511948305 .fill-N1{fill:#0A0F25;} + .d2-511948305 .fill-N2{fill:#676C7E;} + .d2-511948305 .fill-N3{fill:#9499AB;} + .d2-511948305 .fill-N4{fill:#CFD2DD;} + .d2-511948305 .fill-N5{fill:#DEE1EB;} + .d2-511948305 .fill-N6{fill:#EEF1F8;} + .d2-511948305 .fill-N7{fill:#FFFFFF;} + .d2-511948305 .fill-B1{fill:#0D32B2;} + .d2-511948305 .fill-B2{fill:#0D32B2;} + .d2-511948305 .fill-B3{fill:#E3E9FD;} + .d2-511948305 .fill-B4{fill:#E3E9FD;} + .d2-511948305 .fill-B5{fill:#EDF0FD;} + .d2-511948305 .fill-B6{fill:#F7F8FE;} + .d2-511948305 .fill-AA2{fill:#4A6FF3;} + .d2-511948305 .fill-AA4{fill:#EDF0FD;} + .d2-511948305 .fill-AA5{fill:#F7F8FE;} + .d2-511948305 .fill-AB4{fill:#EDF0FD;} + .d2-511948305 .fill-AB5{fill:#F7F8FE;} + .d2-511948305 .stroke-N1{stroke:#0A0F25;} + .d2-511948305 .stroke-N2{stroke:#676C7E;} + .d2-511948305 .stroke-N3{stroke:#9499AB;} + .d2-511948305 .stroke-N4{stroke:#CFD2DD;} + .d2-511948305 .stroke-N5{stroke:#DEE1EB;} + .d2-511948305 .stroke-N6{stroke:#EEF1F8;} + .d2-511948305 .stroke-N7{stroke:#FFFFFF;} + .d2-511948305 .stroke-B1{stroke:#0D32B2;} + .d2-511948305 .stroke-B2{stroke:#0D32B2;} + .d2-511948305 .stroke-B3{stroke:#E3E9FD;} + .d2-511948305 .stroke-B4{stroke:#E3E9FD;} + .d2-511948305 .stroke-B5{stroke:#EDF0FD;} + .d2-511948305 .stroke-B6{stroke:#F7F8FE;} + .d2-511948305 .stroke-AA2{stroke:#4A6FF3;} + .d2-511948305 .stroke-AA4{stroke:#EDF0FD;} + .d2-511948305 .stroke-AA5{stroke:#F7F8FE;} + .d2-511948305 .stroke-AB4{stroke:#EDF0FD;} + .d2-511948305 .stroke-AB5{stroke:#F7F8FE;} + .d2-511948305 .background-color-N1{background-color:#0A0F25;} + .d2-511948305 .background-color-N2{background-color:#676C7E;} + .d2-511948305 .background-color-N3{background-color:#9499AB;} + .d2-511948305 .background-color-N4{background-color:#CFD2DD;} + .d2-511948305 .background-color-N5{background-color:#DEE1EB;} + .d2-511948305 .background-color-N6{background-color:#EEF1F8;} + .d2-511948305 .background-color-N7{background-color:#FFFFFF;} + .d2-511948305 .background-color-B1{background-color:#0D32B2;} + .d2-511948305 .background-color-B2{background-color:#0D32B2;} + .d2-511948305 .background-color-B3{background-color:#E3E9FD;} + .d2-511948305 .background-color-B4{background-color:#E3E9FD;} + .d2-511948305 .background-color-B5{background-color:#EDF0FD;} + .d2-511948305 .background-color-B6{background-color:#F7F8FE;} + .d2-511948305 .background-color-AA2{background-color:#4A6FF3;} + .d2-511948305 .background-color-AA4{background-color:#EDF0FD;} + .d2-511948305 .background-color-AA5{background-color:#F7F8FE;} + .d2-511948305 .background-color-AB4{background-color:#EDF0FD;} + .d2-511948305 .background-color-AB5{background-color:#F7F8FE;} + .d2-511948305 .color-N1{color:#0A0F25;} + .d2-511948305 .color-N2{color:#676C7E;} + .d2-511948305 .color-N3{color:#9499AB;} + .d2-511948305 .color-N4{color:#CFD2DD;} + .d2-511948305 .color-N5{color:#DEE1EB;} + .d2-511948305 .color-N6{color:#EEF1F8;} + .d2-511948305 .color-N7{color:#FFFFFF;} + .d2-511948305 .color-B1{color:#0D32B2;} + .d2-511948305 .color-B2{color:#0D32B2;} + .d2-511948305 .color-B3{color:#E3E9FD;} + .d2-511948305 .color-B4{color:#E3E9FD;} + .d2-511948305 .color-B5{color:#EDF0FD;} + .d2-511948305 .color-B6{color:#F7F8FE;} + .d2-511948305 .color-AA2{color:#4A6FF3;} + .d2-511948305 .color-AA4{color:#EDF0FD;} + .d2-511948305 .color-AA5{color:#F7F8FE;} + .d2-511948305 .color-AB4{color:#EDF0FD;} + .d2-511948305 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>grid w/ containergrid w/ nested containersgrid in gridgrid w/ grid w/ gridabcdabcdabcdabcdb childb 1abcdabcdb 2b 2aabcdb 3b 3aabcdb 4b 3aabcd \ No newline at end of file From 6a837fa03842e61f9c2d635b0a79078e02c301ef Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Fri, 26 May 2023 17:51:39 -0700 Subject: [PATCH 4/9] changelog --- ci/release/changelogs/next.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 5c37f0360..398449902 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -12,3 +12,4 @@ #### Bugfixes ⛑️ - Shadow is cut off when `--pad` is 0. Thank you @LeonardsonCC ! [#1326](https://github.com/terrastruct/d2/pull/1326) +- Fixes grid layout overwriting label placements for nested objects. [#1345](https://github.com/terrastruct/d2/pull/1345) From ad44eff47a46586ad743ec91d27e3557609516d2 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Tue, 30 May 2023 11:18:32 -0700 Subject: [PATCH 5/9] cleanup --- d2layouts/d2grid/layout.go | 44 ++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/d2layouts/d2grid/layout.go b/d2layouts/d2grid/layout.go index 6b747f7b3..a84209808 100644 --- a/d2layouts/d2grid/layout.go +++ b/d2layouts/d2grid/layout.go @@ -123,31 +123,33 @@ func withoutGridDiagrams(ctx context.Context, g *d2graph.Graph, layout d2graph.L if obj.LabelDimensions.Height != 0 { labelHeight := float64(obj.LabelDimensions.Height) + 2*label.PADDING - { - // also check for grid cells with outside top labels - topY := gd.objects[0].TopLeft.Y - highestLabel := topY - for _, o := range gd.objects { - if o.TopLeft.Y > topY { - if gd.rowDirected { - break - } else { - continue - } + // also check for grid cells with outside top labels + // the first grid object is at the top (and always exists) + topY := gd.objects[0].TopLeft.Y + highestLabel := topY + for _, o := range gd.objects { + // we only want to compute label positions for objects at the top of the grid + if o.TopLeft.Y > topY { + if gd.rowDirected { + // if the grid is rowDirected (row1, row2, etc) we can stop after finishing the first row + break + } else { + // otherwise we continue until the next column + continue } - if o.LabelPosition != nil { - labelPosition := label.Position(*o.LabelPosition) - if labelPosition.IsOutside() { - labelTL := o.GetLabelTopLeft() - if labelTL.Y < highestLabel { - highestLabel = labelTL.Y - } + } + if o.LabelPosition != nil { + labelPosition := label.Position(*o.LabelPosition) + if labelPosition.IsOutside() { + labelTL := o.GetLabelTopLeft() + if labelTL.Y < highestLabel { + highestLabel = labelTL.Y } } } - if highestLabel < topY { - labelHeight += topY - highestLabel + 2*label.PADDING - } + } + if highestLabel < topY { + labelHeight += topY - highestLabel + 2*label.PADDING } if labelHeight > float64(verticalPadding) { From f64c0f1c774bd9aec36aaa901df5800aad26a193 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Tue, 30 May 2023 11:32:40 -0700 Subject: [PATCH 6/9] add grid_icon test --- e2etests/stable_test.go | 1 + e2etests/testdata/files/grid_icon.d2 | 67 + .../stable/grid_icon/dagre/board.exp.json | 1271 +++++++++++++++++ .../stable/grid_icon/dagre/sketch.exp.svg | 102 ++ .../stable/grid_icon/elk/board.exp.json | 1271 +++++++++++++++++ .../stable/grid_icon/elk/sketch.exp.svg | 102 ++ 6 files changed, 2814 insertions(+) create mode 100644 e2etests/testdata/files/grid_icon.d2 create mode 100644 e2etests/testdata/stable/grid_icon/dagre/board.exp.json create mode 100644 e2etests/testdata/stable/grid_icon/dagre/sketch.exp.svg create mode 100644 e2etests/testdata/stable/grid_icon/elk/board.exp.json create mode 100644 e2etests/testdata/stable/grid_icon/elk/sketch.exp.svg diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go index f88f82fb5..ff25dfd6d 100644 --- a/e2etests/stable_test.go +++ b/e2etests/stable_test.go @@ -2723,6 +2723,7 @@ scenarios: { loadFromFile(t, "grid_large_checkered"), loadFromFile(t, "grid_nested"), loadFromFile(t, "grid_nested_gap0"), + loadFromFile(t, "grid_icon"), loadFromFile(t, "multiple_offset"), loadFromFile(t, "multiple_offset_left"), } diff --git a/e2etests/testdata/files/grid_icon.d2 b/e2etests/testdata/files/grid_icon.d2 new file mode 100644 index 000000000..40fdce41e --- /dev/null +++ b/e2etests/testdata/files/grid_icon.d2 @@ -0,0 +1,67 @@ +classes: { + 2x2: { + grid-rows: 2 + grid-columns: 2 + } +} + +grid w/ container + icon: { + class: 2x2 + + a + b: { + b child + + icon: https://icons.terrastruct.com/dev%2Fgithub.svg + } + c + d +} + +grid + icon: { + class: 2x2 + + icon: https://icons.terrastruct.com/dev%2Fgithub.svg + + a + b + c + d +} + +grid + icon w/ container: { + class: 2x2 + + icon: https://icons.terrastruct.com/dev%2Fgithub.svg + + a + b: { + b child + } + c + d +} + +no label grid w/ container + icon: "" { + class: 2x2 + + a + b: { + b child + + icon: https://icons.terrastruct.com/dev%2Fgithub.svg + } + c + d +} + +no label grid + icon: "" { + class: 2x2 + + icon: https://icons.terrastruct.com/dev%2Fgithub.svg + + a + b + c + d +} diff --git a/e2etests/testdata/stable/grid_icon/dagre/board.exp.json b/e2etests/testdata/stable/grid_icon/dagre/board.exp.json new file mode 100644 index 000000000..4f7aa96e8 --- /dev/null +++ b/e2etests/testdata/stable/grid_icon/dagre/board.exp.json @@ -0,0 +1,1271 @@ +{ + "name": "", + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "grid w/ container + icon", + "type": "rectangle", + "classes": [ + "2x2" + ], + "pos": { + "x": 0, + "y": 21 + }, + "width": 384, + "height": 356, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "grid w/ container + icon", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 273, + "labelHeight": 36, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "grid w/ container + icon.a", + "type": "rectangle", + "pos": { + "x": 60, + "y": 81 + }, + "width": 53, + "height": 130, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid w/ container + icon.b", + "type": "rectangle", + "pos": { + "x": 153, + "y": 81 + }, + "width": 171, + "height": 130, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": { + "Scheme": "https", + "Opaque": "", + "User": null, + "Host": "icons.terrastruct.com", + "Path": "/dev/github.svg", + "RawPath": "/dev%2Fgithub.svg", + "OmitHost": false, + "ForceQuery": false, + "RawQuery": "", + "Fragment": "", + "RawFragment": "" + }, + "iconPosition": "INSIDE_MIDDLE_CENTER", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 24, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 12, + "labelHeight": 31, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid w/ container + icon.b.b child", + "type": "rectangle", + "pos": { + "x": 193, + "y": 113 + }, + "width": 91, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b child", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 46, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 3 + }, + { + "id": "grid w/ container + icon.c", + "type": "rectangle", + "pos": { + "x": 60, + "y": 251 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid w/ container + icon.d", + "type": "rectangle", + "pos": { + "x": 153, + "y": 251 + }, + "width": 171, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 9, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon", + "type": "rectangle", + "classes": [ + "2x2" + ], + "pos": { + "x": 444, + "y": 32 + }, + "width": 267, + "height": 333, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": { + "Scheme": "https", + "Opaque": "", + "User": null, + "Host": "icons.terrastruct.com", + "Path": "/dev/github.svg", + "RawPath": "/dev%2Fgithub.svg", + "OmitHost": false, + "ForceQuery": false, + "RawQuery": "", + "Fragment": "", + "RawFragment": "" + }, + "iconPosition": "INSIDE_MIDDLE_CENTER", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "grid + icon", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 120, + "labelHeight": 36, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "grid + icon.a", + "type": "rectangle", + "pos": { + "x": 504, + "y": 92 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon.b", + "type": "rectangle", + "pos": { + "x": 597, + "y": 92 + }, + "width": 54, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon.c", + "type": "rectangle", + "pos": { + "x": 504, + "y": 198 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon.d", + "type": "rectangle", + "pos": { + "x": 597, + "y": 198 + }, + "width": 54, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 9, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon w/ container", + "type": "rectangle", + "classes": [ + "2x2" + ], + "pos": { + "x": 771, + "y": 0 + }, + "width": 384, + "height": 397, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": { + "Scheme": "https", + "Opaque": "", + "User": null, + "Host": "icons.terrastruct.com", + "Path": "/dev/github.svg", + "RawPath": "/dev%2Fgithub.svg", + "OmitHost": false, + "ForceQuery": false, + "RawQuery": "", + "Fragment": "", + "RawFragment": "" + }, + "iconPosition": "INSIDE_MIDDLE_CENTER", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "grid + icon w/ container", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 275, + "labelHeight": 36, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "grid + icon w/ container.a", + "type": "rectangle", + "pos": { + "x": 831, + "y": 60 + }, + "width": 53, + "height": 130, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon w/ container.b", + "type": "rectangle", + "pos": { + "x": 924, + "y": 60 + }, + "width": 171, + "height": 130, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 24, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 12, + "labelHeight": 31, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon w/ container.b.b child", + "type": "rectangle", + "pos": { + "x": 964, + "y": 92 + }, + "width": 91, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b child", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 46, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 3 + }, + { + "id": "grid + icon w/ container.c", + "type": "rectangle", + "pos": { + "x": 831, + "y": 230 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon w/ container.d", + "type": "rectangle", + "pos": { + "x": 924, + "y": 230 + }, + "width": 171, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 9, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid w/ container + icon", + "type": "rectangle", + "classes": [ + "2x2" + ], + "pos": { + "x": 1215, + "y": 21 + }, + "width": 384, + "height": 356, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "no label grid w/ container + icon.a", + "type": "rectangle", + "pos": { + "x": 1275, + "y": 81 + }, + "width": 53, + "height": 130, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid w/ container + icon.b", + "type": "rectangle", + "pos": { + "x": 1368, + "y": 81 + }, + "width": 171, + "height": 130, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": { + "Scheme": "https", + "Opaque": "", + "User": null, + "Host": "icons.terrastruct.com", + "Path": "/dev/github.svg", + "RawPath": "/dev%2Fgithub.svg", + "OmitHost": false, + "ForceQuery": false, + "RawQuery": "", + "Fragment": "", + "RawFragment": "" + }, + "iconPosition": "INSIDE_MIDDLE_CENTER", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 24, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 12, + "labelHeight": 31, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid w/ container + icon.b.b child", + "type": "rectangle", + "pos": { + "x": 1408, + "y": 113 + }, + "width": 91, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b child", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 46, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 3 + }, + { + "id": "no label grid w/ container + icon.c", + "type": "rectangle", + "pos": { + "x": 1275, + "y": 251 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid w/ container + icon.d", + "type": "rectangle", + "pos": { + "x": 1368, + "y": 251 + }, + "width": 171, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 9, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid + icon", + "type": "rectangle", + "classes": [ + "2x2" + ], + "pos": { + "x": 1659, + "y": 53 + }, + "width": 267, + "height": 292, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": { + "Scheme": "https", + "Opaque": "", + "User": null, + "Host": "icons.terrastruct.com", + "Path": "/dev/github.svg", + "RawPath": "/dev%2Fgithub.svg", + "OmitHost": false, + "ForceQuery": false, + "RawQuery": "", + "Fragment": "", + "RawFragment": "" + }, + "iconPosition": "INSIDE_MIDDLE_CENTER", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "no label grid + icon.a", + "type": "rectangle", + "pos": { + "x": 1719, + "y": 113 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid + icon.b", + "type": "rectangle", + "pos": { + "x": 1812, + "y": 113 + }, + "width": 54, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid + icon.c", + "type": "rectangle", + "pos": { + "x": 1719, + "y": 219 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid + icon.d", + "type": "rectangle", + "pos": { + "x": 1812, + "y": 219 + }, + "width": 54, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 9, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + } + ], + "connections": [], + "root": { + "id": "", + "type": "", + "pos": { + "x": 0, + "y": 0 + }, + "width": 0, + "height": 0, + "opacity": 0, + "strokeDash": 0, + "strokeWidth": 0, + "borderRadius": 0, + "fill": "N7", + "stroke": "", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 0, + "fontFamily": "", + "language": "", + "color": "", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 0 + } +} diff --git a/e2etests/testdata/stable/grid_icon/dagre/sketch.exp.svg b/e2etests/testdata/stable/grid_icon/dagre/sketch.exp.svg new file mode 100644 index 000000000..808c46fca --- /dev/null +++ b/e2etests/testdata/stable/grid_icon/dagre/sketch.exp.svg @@ -0,0 +1,102 @@ +grid w/ container + icongrid + icongrid + icon w/ containerabcdabcdabcdabcdabcdb childb childb child + + + \ No newline at end of file diff --git a/e2etests/testdata/stable/grid_icon/elk/board.exp.json b/e2etests/testdata/stable/grid_icon/elk/board.exp.json new file mode 100644 index 000000000..4cbb1ccf2 --- /dev/null +++ b/e2etests/testdata/stable/grid_icon/elk/board.exp.json @@ -0,0 +1,1271 @@ +{ + "name": "", + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "grid w/ container + icon", + "type": "rectangle", + "classes": [ + "2x2" + ], + "pos": { + "x": 12, + "y": 20 + }, + "width": 404, + "height": 416, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "grid w/ container + icon", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 273, + "labelHeight": 36, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "grid w/ container + icon.a", + "type": "rectangle", + "pos": { + "x": 72, + "y": 80 + }, + "width": 53, + "height": 190, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid w/ container + icon.b", + "type": "rectangle", + "pos": { + "x": 165, + "y": 80 + }, + "width": 191, + "height": 190, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": { + "Scheme": "https", + "Opaque": "", + "User": null, + "Host": "icons.terrastruct.com", + "Path": "/dev/github.svg", + "RawPath": "/dev%2Fgithub.svg", + "OmitHost": false, + "ForceQuery": false, + "RawQuery": "", + "Fragment": "", + "RawFragment": "" + }, + "iconPosition": "INSIDE_MIDDLE_CENTER", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 24, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 12, + "labelHeight": 31, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid w/ container + icon.b.b child", + "type": "rectangle", + "pos": { + "x": 215, + "y": 154 + }, + "width": 91, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b child", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 46, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 3 + }, + { + "id": "grid w/ container + icon.c", + "type": "rectangle", + "pos": { + "x": 72, + "y": 310 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid w/ container + icon.d", + "type": "rectangle", + "pos": { + "x": 165, + "y": 310 + }, + "width": 191, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 9, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon", + "type": "rectangle", + "classes": [ + "2x2" + ], + "pos": { + "x": 436, + "y": 62 + }, + "width": 267, + "height": 333, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": { + "Scheme": "https", + "Opaque": "", + "User": null, + "Host": "icons.terrastruct.com", + "Path": "/dev/github.svg", + "RawPath": "/dev%2Fgithub.svg", + "OmitHost": false, + "ForceQuery": false, + "RawQuery": "", + "Fragment": "", + "RawFragment": "" + }, + "iconPosition": "INSIDE_MIDDLE_CENTER", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "grid + icon", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 120, + "labelHeight": 36, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "grid + icon.a", + "type": "rectangle", + "pos": { + "x": 496, + "y": 122 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon.b", + "type": "rectangle", + "pos": { + "x": 589, + "y": 122 + }, + "width": 54, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon.c", + "type": "rectangle", + "pos": { + "x": 496, + "y": 228 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon.d", + "type": "rectangle", + "pos": { + "x": 589, + "y": 228 + }, + "width": 54, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 9, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon w/ container", + "type": "rectangle", + "classes": [ + "2x2" + ], + "pos": { + "x": 723, + "y": 12 + }, + "width": 404, + "height": 433, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": { + "Scheme": "https", + "Opaque": "", + "User": null, + "Host": "icons.terrastruct.com", + "Path": "/dev/github.svg", + "RawPath": "/dev%2Fgithub.svg", + "OmitHost": false, + "ForceQuery": false, + "RawQuery": "", + "Fragment": "", + "RawFragment": "" + }, + "iconPosition": "INSIDE_MIDDLE_CENTER", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "grid + icon w/ container", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 275, + "labelHeight": 36, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "grid + icon w/ container.a", + "type": "rectangle", + "pos": { + "x": 783, + "y": 72 + }, + "width": 53, + "height": 166, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon w/ container.b", + "type": "rectangle", + "pos": { + "x": 876, + "y": 72 + }, + "width": 191, + "height": 166, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 24, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 12, + "labelHeight": 31, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon w/ container.b.b child", + "type": "rectangle", + "pos": { + "x": 926, + "y": 122 + }, + "width": 91, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b child", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 46, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 3 + }, + { + "id": "grid + icon w/ container.c", + "type": "rectangle", + "pos": { + "x": 783, + "y": 278 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "grid + icon w/ container.d", + "type": "rectangle", + "pos": { + "x": 876, + "y": 278 + }, + "width": 191, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 9, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid w/ container + icon", + "type": "rectangle", + "classes": [ + "2x2" + ], + "pos": { + "x": 1147, + "y": 20 + }, + "width": 404, + "height": 416, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "no label grid w/ container + icon.a", + "type": "rectangle", + "pos": { + "x": 1207, + "y": 80 + }, + "width": 53, + "height": 190, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid w/ container + icon.b", + "type": "rectangle", + "pos": { + "x": 1300, + "y": 80 + }, + "width": 191, + "height": 190, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": { + "Scheme": "https", + "Opaque": "", + "User": null, + "Host": "icons.terrastruct.com", + "Path": "/dev/github.svg", + "RawPath": "/dev%2Fgithub.svg", + "OmitHost": false, + "ForceQuery": false, + "RawQuery": "", + "Fragment": "", + "RawFragment": "" + }, + "iconPosition": "INSIDE_MIDDLE_CENTER", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 24, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 12, + "labelHeight": 31, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid w/ container + icon.b.b child", + "type": "rectangle", + "pos": { + "x": 1350, + "y": 154 + }, + "width": 91, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b child", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 46, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 3 + }, + { + "id": "no label grid w/ container + icon.c", + "type": "rectangle", + "pos": { + "x": 1207, + "y": 310 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid w/ container + icon.d", + "type": "rectangle", + "pos": { + "x": 1300, + "y": 310 + }, + "width": 191, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 9, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid + icon", + "type": "rectangle", + "classes": [ + "2x2" + ], + "pos": { + "x": 1571, + "y": 82 + }, + "width": 267, + "height": 292, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": { + "Scheme": "https", + "Opaque": "", + "User": null, + "Host": "icons.terrastruct.com", + "Path": "/dev/github.svg", + "RawPath": "/dev%2Fgithub.svg", + "OmitHost": false, + "ForceQuery": false, + "RawQuery": "", + "Fragment": "", + "RawFragment": "" + }, + "iconPosition": "INSIDE_MIDDLE_CENTER", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "no label grid + icon.a", + "type": "rectangle", + "pos": { + "x": 1631, + "y": 142 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid + icon.b", + "type": "rectangle", + "pos": { + "x": 1724, + "y": 142 + }, + "width": 54, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid + icon.c", + "type": "rectangle", + "pos": { + "x": 1631, + "y": 248 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "no label grid + icon.d", + "type": "rectangle", + "pos": { + "x": 1724, + "y": 248 + }, + "width": 54, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 9, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + } + ], + "connections": [], + "root": { + "id": "", + "type": "", + "pos": { + "x": 0, + "y": 0 + }, + "width": 0, + "height": 0, + "opacity": 0, + "strokeDash": 0, + "strokeWidth": 0, + "borderRadius": 0, + "fill": "N7", + "stroke": "", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 0, + "fontFamily": "", + "language": "", + "color": "", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 0 + } +} diff --git a/e2etests/testdata/stable/grid_icon/elk/sketch.exp.svg b/e2etests/testdata/stable/grid_icon/elk/sketch.exp.svg new file mode 100644 index 000000000..f10778ef9 --- /dev/null +++ b/e2etests/testdata/stable/grid_icon/elk/sketch.exp.svg @@ -0,0 +1,102 @@ +grid w/ container + icongrid + icongrid + icon w/ containerabcdabcdabcdabcdabcdb childb childb child + + + \ No newline at end of file From 176df52cec88aeca09020dec188a5054d1543995 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Tue, 30 May 2023 11:59:39 -0700 Subject: [PATCH 7/9] fix grid icon positioning --- d2layouts/d2dagrelayout/layout.go | 4 +- d2layouts/d2elklayout/layout.go | 4 +- d2layouts/d2grid/layout.go | 115 ++++++++++++++++++------------ 3 files changed, 75 insertions(+), 48 deletions(-) diff --git a/d2layouts/d2dagrelayout/layout.go b/d2layouts/d2dagrelayout/layout.go index 39cd1f9fe..e033c61e7 100644 --- a/d2layouts/d2dagrelayout/layout.go +++ b/d2layouts/d2dagrelayout/layout.go @@ -238,7 +238,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err obj.Width = math.Ceil(dn.Width) obj.Height = math.Ceil(dn.Height) - if obj.HasLabel() { + if obj.HasLabel() && obj.LabelPosition == nil { if len(obj.ChildrenArray) > 0 { obj.LabelPosition = go2.Pointer(string(label.OutsideTopCenter)) } else if obj.HasOutsideBottomLabel() { @@ -251,7 +251,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter)) } } - if obj.Icon != nil { + if obj.Icon != nil && obj.IconPosition == nil { if len(obj.ChildrenArray) > 0 { obj.IconPosition = go2.Pointer(string(label.OutsideTopLeft)) obj.LabelPosition = go2.Pointer(string(label.OutsideTopRight)) diff --git a/d2layouts/d2elklayout/layout.go b/d2layouts/d2elklayout/layout.go index be6687f7e..2fb5e160d 100644 --- a/d2layouts/d2elklayout/layout.go +++ b/d2layouts/d2elklayout/layout.go @@ -407,7 +407,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err obj.Width = math.Ceil(n.Width) obj.Height = math.Ceil(n.Height) - if obj.HasLabel() { + if obj.HasLabel() && obj.LabelPosition == nil { if len(obj.ChildrenArray) > 0 { obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) } else if obj.HasOutsideBottomLabel() { @@ -419,7 +419,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter)) } } - if obj.Icon != nil { + if obj.Icon != nil && obj.IconPosition == nil { if len(obj.ChildrenArray) > 0 { obj.IconPosition = go2.Pointer(string(label.InsideTopLeft)) obj.LabelPosition = go2.Pointer(string(label.InsideTopRight)) diff --git a/d2layouts/d2grid/layout.go b/d2layouts/d2grid/layout.go index a84209808..390470583 100644 --- a/d2layouts/d2grid/layout.go +++ b/d2layouts/d2grid/layout.go @@ -8,6 +8,7 @@ import ( "sort" "oss.terrastruct.com/d2/d2graph" + "oss.terrastruct.com/d2/d2target" "oss.terrastruct.com/d2/lib/geo" "oss.terrastruct.com/d2/lib/label" "oss.terrastruct.com/util-go/go2" @@ -112,62 +113,88 @@ func withoutGridDiagrams(ctx context.Context, g *d2graph.Graph, layout d2graph.L gd.shift(innerBox.TopLeft.X, innerBox.TopLeft.Y) } + // compute how much space the label and icon occupy + var occupiedWidth, occupiedHeight float64 + if obj.Icon != nil { + iconSpace := float64(d2target.MAX_ICON_SIZE + 2*label.PADDING) + occupiedWidth = iconSpace + occupiedHeight = iconSpace + } + var dx, dy float64 - if obj.LabelDimensions.Width != 0 { - labelWidth := float64(obj.LabelDimensions.Width) + 2*label.PADDING - if labelWidth > obj.Width { - dx = (labelWidth - obj.Width) / 2 - obj.Width = labelWidth - } - } if obj.LabelDimensions.Height != 0 { - labelHeight := float64(obj.LabelDimensions.Height) + 2*label.PADDING - - // also check for grid cells with outside top labels - // the first grid object is at the top (and always exists) - topY := gd.objects[0].TopLeft.Y - highestLabel := topY - for _, o := range gd.objects { - // we only want to compute label positions for objects at the top of the grid - if o.TopLeft.Y > topY { - if gd.rowDirected { - // if the grid is rowDirected (row1, row2, etc) we can stop after finishing the first row - break - } else { - // otherwise we continue until the next column - continue - } - } - if o.LabelPosition != nil { - labelPosition := label.Position(*o.LabelPosition) - if labelPosition.IsOutside() { - labelTL := o.GetLabelTopLeft() - if labelTL.Y < highestLabel { - highestLabel = labelTL.Y - } - } - } - } - if highestLabel < topY { - labelHeight += topY - highestLabel + 2*label.PADDING - } - - if labelHeight > float64(verticalPadding) { - // if the label doesn't fit within the padding, we need to add more - grow := labelHeight - float64(verticalPadding) - dy = grow - obj.Height += grow + occupiedHeight = math.Max( + occupiedHeight, + float64(obj.LabelDimensions.Height)+2*label.PADDING, + ) + } + if obj.LabelDimensions.Width != 0 { + // . ├────┤───────├────┤ + // . icon label icon + // with an icon in top left we need 2x the space to fit the label in the center + occupiedWidth *= 2 + occupiedWidth += float64(obj.LabelDimensions.Width) + 2*label.PADDING + if occupiedWidth > obj.Width { + dx = (occupiedWidth - obj.Width) / 2 + obj.Width = occupiedWidth } } + + // also check for grid cells with outside top labels or icons + // the first grid object is at the top (and always exists) + topY := gd.objects[0].TopLeft.Y + highestOutside := topY + for _, o := range gd.objects { + // we only want to compute label positions for objects at the top of the grid + if o.TopLeft.Y > topY { + if gd.rowDirected { + // if the grid is rowDirected (row1, row2, etc) we can stop after finishing the first row + break + } else { + // otherwise we continue until the next column + continue + } + } + if o.LabelPosition != nil { + labelPosition := label.Position(*o.LabelPosition) + if labelPosition.IsOutside() { + labelTL := o.GetLabelTopLeft() + if labelTL.Y < highestOutside { + highestOutside = labelTL.Y + } + } + } + if o.IconPosition != nil { + switch label.Position(*o.IconPosition) { + case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight: + iconSpace := float64(d2target.MAX_ICON_SIZE + label.PADDING) + if topY-iconSpace < highestOutside { + highestOutside = topY - iconSpace + } + } + } + } + if highestOutside < topY { + occupiedHeight += topY - highestOutside + 2*label.PADDING + } + if occupiedHeight > float64(verticalPadding) { + // if the label doesn't fit within the padding, we need to add more + dy = occupiedHeight - float64(verticalPadding) + obj.Height += dy + } + // we need to center children if we have to expand to fit the container label if dx != 0 || dy != 0 { gd.shift(dx, dy) } } - if obj.LabelPosition == nil { + if obj.HasLabel() { obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) } + if obj.Icon != nil { + obj.IconPosition = go2.Pointer(string(label.InsideTopLeft)) + } gridDiagrams[obj.AbsID()] = gd for _, o := range gd.objects { From db2b3f2d14bc23fd8fefd417247704373931ecd6 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Tue, 30 May 2023 13:33:07 -0700 Subject: [PATCH 8/9] update tests --- .../stable/grid_icon/dagre/board.exp.json | 126 +++++++------- .../stable/grid_icon/dagre/sketch.exp.svg | 160 +++++++++--------- .../stable/grid_icon/elk/board.exp.json | 118 ++++++------- .../stable/grid_icon/elk/sketch.exp.svg | 160 +++++++++--------- 4 files changed, 282 insertions(+), 282 deletions(-) diff --git a/e2etests/testdata/stable/grid_icon/dagre/board.exp.json b/e2etests/testdata/stable/grid_icon/dagre/board.exp.json index 4f7aa96e8..4ad3f73ef 100644 --- a/e2etests/testdata/stable/grid_icon/dagre/board.exp.json +++ b/e2etests/testdata/stable/grid_icon/dagre/board.exp.json @@ -11,10 +11,10 @@ ], "pos": { "x": 0, - "y": 21 + "y": 18 }, "width": 384, - "height": 356, + "height": 421, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -52,7 +52,7 @@ "type": "rectangle", "pos": { "x": 60, - "y": 81 + "y": 143 }, "width": 53, "height": 130, @@ -93,7 +93,7 @@ "type": "rectangle", "pos": { "x": 153, - "y": 81 + "y": 143 }, "width": 171, "height": 130, @@ -122,7 +122,7 @@ "Fragment": "", "RawFragment": "" }, - "iconPosition": "INSIDE_MIDDLE_CENTER", + "iconPosition": "OUTSIDE_TOP_LEFT", "blend": false, "fields": null, "methods": null, @@ -137,7 +137,7 @@ "underline": false, "labelWidth": 12, "labelHeight": 31, - "labelPosition": "INSIDE_TOP_CENTER", + "labelPosition": "OUTSIDE_TOP_RIGHT", "zIndex": 0, "level": 2 }, @@ -146,7 +146,7 @@ "type": "rectangle", "pos": { "x": 193, - "y": 113 + "y": 175 }, "width": 91, "height": 66, @@ -187,7 +187,7 @@ "type": "rectangle", "pos": { "x": 60, - "y": 251 + "y": 313 }, "width": 53, "height": 66, @@ -228,7 +228,7 @@ "type": "rectangle", "pos": { "x": 153, - "y": 251 + "y": 313 }, "width": 171, "height": 66, @@ -272,10 +272,10 @@ ], "pos": { "x": 444, - "y": 32 + "y": 55 }, - "width": 267, - "height": 333, + "width": 278, + "height": 347, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -301,7 +301,7 @@ "Fragment": "", "RawFragment": "" }, - "iconPosition": "INSIDE_MIDDLE_CENTER", + "iconPosition": "INSIDE_TOP_LEFT", "blend": false, "fields": null, "methods": null, @@ -324,8 +324,8 @@ "id": "grid + icon.a", "type": "rectangle", "pos": { - "x": 504, - "y": 92 + "x": 509, + "y": 129 }, "width": 53, "height": 66, @@ -365,8 +365,8 @@ "id": "grid + icon.b", "type": "rectangle", "pos": { - "x": 597, - "y": 92 + "x": 602, + "y": 129 }, "width": 54, "height": 66, @@ -406,8 +406,8 @@ "id": "grid + icon.c", "type": "rectangle", "pos": { - "x": 504, - "y": 198 + "x": 509, + "y": 235 }, "width": 53, "height": 66, @@ -447,8 +447,8 @@ "id": "grid + icon.d", "type": "rectangle", "pos": { - "x": 597, - "y": 198 + "x": 602, + "y": 235 }, "width": 54, "height": 66, @@ -491,11 +491,11 @@ "2x2" ], "pos": { - "x": 771, + "x": 782, "y": 0 }, - "width": 384, - "height": 397, + "width": 433, + "height": 457, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -521,7 +521,7 @@ "Fragment": "", "RawFragment": "" }, - "iconPosition": "INSIDE_MIDDLE_CENTER", + "iconPosition": "INSIDE_TOP_LEFT", "blend": false, "fields": null, "methods": null, @@ -544,8 +544,8 @@ "id": "grid + icon w/ container.a", "type": "rectangle", "pos": { - "x": 831, - "y": 60 + "x": 866, + "y": 120 }, "width": 53, "height": 130, @@ -585,8 +585,8 @@ "id": "grid + icon w/ container.b", "type": "rectangle", "pos": { - "x": 924, - "y": 60 + "x": 959, + "y": 120 }, "width": 171, "height": 130, @@ -618,7 +618,7 @@ "underline": false, "labelWidth": 12, "labelHeight": 31, - "labelPosition": "INSIDE_MIDDLE_CENTER", + "labelPosition": "OUTSIDE_TOP_CENTER", "zIndex": 0, "level": 2 }, @@ -626,8 +626,8 @@ "id": "grid + icon w/ container.b.b child", "type": "rectangle", "pos": { - "x": 964, - "y": 92 + "x": 999, + "y": 152 }, "width": 91, "height": 66, @@ -667,8 +667,8 @@ "id": "grid + icon w/ container.c", "type": "rectangle", "pos": { - "x": 831, - "y": 230 + "x": 866, + "y": 290 }, "width": 53, "height": 66, @@ -708,8 +708,8 @@ "id": "grid + icon w/ container.d", "type": "rectangle", "pos": { - "x": 924, - "y": 230 + "x": 959, + "y": 290 }, "width": 171, "height": 66, @@ -752,11 +752,11 @@ "2x2" ], "pos": { - "x": 1215, - "y": 21 + "x": 1275, + "y": 41 }, "width": 384, - "height": 356, + "height": 375, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -793,8 +793,8 @@ "id": "no label grid w/ container + icon.a", "type": "rectangle", "pos": { - "x": 1275, - "y": 81 + "x": 1335, + "y": 120 }, "width": 53, "height": 130, @@ -834,8 +834,8 @@ "id": "no label grid w/ container + icon.b", "type": "rectangle", "pos": { - "x": 1368, - "y": 81 + "x": 1428, + "y": 120 }, "width": 171, "height": 130, @@ -864,7 +864,7 @@ "Fragment": "", "RawFragment": "" }, - "iconPosition": "INSIDE_MIDDLE_CENTER", + "iconPosition": "OUTSIDE_TOP_LEFT", "blend": false, "fields": null, "methods": null, @@ -879,7 +879,7 @@ "underline": false, "labelWidth": 12, "labelHeight": 31, - "labelPosition": "INSIDE_TOP_CENTER", + "labelPosition": "OUTSIDE_TOP_RIGHT", "zIndex": 0, "level": 2 }, @@ -887,8 +887,8 @@ "id": "no label grid w/ container + icon.b.b child", "type": "rectangle", "pos": { - "x": 1408, - "y": 113 + "x": 1468, + "y": 152 }, "width": 91, "height": 66, @@ -928,8 +928,8 @@ "id": "no label grid w/ container + icon.c", "type": "rectangle", "pos": { - "x": 1275, - "y": 251 + "x": 1335, + "y": 290 }, "width": 53, "height": 66, @@ -969,8 +969,8 @@ "id": "no label grid w/ container + icon.d", "type": "rectangle", "pos": { - "x": 1368, - "y": 251 + "x": 1428, + "y": 290 }, "width": 171, "height": 66, @@ -1013,11 +1013,11 @@ "2x2" ], "pos": { - "x": 1659, - "y": 53 + "x": 1719, + "y": 76 }, "width": 267, - "height": 292, + "height": 306, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -1043,7 +1043,7 @@ "Fragment": "", "RawFragment": "" }, - "iconPosition": "INSIDE_MIDDLE_CENTER", + "iconPosition": "INSIDE_TOP_LEFT", "blend": false, "fields": null, "methods": null, @@ -1066,8 +1066,8 @@ "id": "no label grid + icon.a", "type": "rectangle", "pos": { - "x": 1719, - "y": 113 + "x": 1779, + "y": 150 }, "width": 53, "height": 66, @@ -1107,8 +1107,8 @@ "id": "no label grid + icon.b", "type": "rectangle", "pos": { - "x": 1812, - "y": 113 + "x": 1872, + "y": 150 }, "width": 54, "height": 66, @@ -1148,8 +1148,8 @@ "id": "no label grid + icon.c", "type": "rectangle", "pos": { - "x": 1719, - "y": 219 + "x": 1779, + "y": 256 }, "width": 53, "height": 66, @@ -1189,8 +1189,8 @@ "id": "no label grid + icon.d", "type": "rectangle", "pos": { - "x": 1812, - "y": 219 + "x": 1872, + "y": 256 }, "width": 54, "height": 66, diff --git a/e2etests/testdata/stable/grid_icon/dagre/sketch.exp.svg b/e2etests/testdata/stable/grid_icon/dagre/sketch.exp.svg index 808c46fca..92e20b3c2 100644 --- a/e2etests/testdata/stable/grid_icon/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/grid_icon/dagre/sketch.exp.svg @@ -1,16 +1,16 @@ -grid w/ container + icongrid + icongrid + icon w/ containerabcdabcdabcdabcdabcdb childb childb child - + .d2-3381334095 .fill-N1{fill:#0A0F25;} + .d2-3381334095 .fill-N2{fill:#676C7E;} + .d2-3381334095 .fill-N3{fill:#9499AB;} + .d2-3381334095 .fill-N4{fill:#CFD2DD;} + .d2-3381334095 .fill-N5{fill:#DEE1EB;} + .d2-3381334095 .fill-N6{fill:#EEF1F8;} + .d2-3381334095 .fill-N7{fill:#FFFFFF;} + .d2-3381334095 .fill-B1{fill:#0D32B2;} + .d2-3381334095 .fill-B2{fill:#0D32B2;} + .d2-3381334095 .fill-B3{fill:#E3E9FD;} + .d2-3381334095 .fill-B4{fill:#E3E9FD;} + .d2-3381334095 .fill-B5{fill:#EDF0FD;} + .d2-3381334095 .fill-B6{fill:#F7F8FE;} + .d2-3381334095 .fill-AA2{fill:#4A6FF3;} + .d2-3381334095 .fill-AA4{fill:#EDF0FD;} + .d2-3381334095 .fill-AA5{fill:#F7F8FE;} + .d2-3381334095 .fill-AB4{fill:#EDF0FD;} + .d2-3381334095 .fill-AB5{fill:#F7F8FE;} + .d2-3381334095 .stroke-N1{stroke:#0A0F25;} + .d2-3381334095 .stroke-N2{stroke:#676C7E;} + .d2-3381334095 .stroke-N3{stroke:#9499AB;} + .d2-3381334095 .stroke-N4{stroke:#CFD2DD;} + .d2-3381334095 .stroke-N5{stroke:#DEE1EB;} + .d2-3381334095 .stroke-N6{stroke:#EEF1F8;} + .d2-3381334095 .stroke-N7{stroke:#FFFFFF;} + .d2-3381334095 .stroke-B1{stroke:#0D32B2;} + .d2-3381334095 .stroke-B2{stroke:#0D32B2;} + .d2-3381334095 .stroke-B3{stroke:#E3E9FD;} + .d2-3381334095 .stroke-B4{stroke:#E3E9FD;} + .d2-3381334095 .stroke-B5{stroke:#EDF0FD;} + .d2-3381334095 .stroke-B6{stroke:#F7F8FE;} + .d2-3381334095 .stroke-AA2{stroke:#4A6FF3;} + .d2-3381334095 .stroke-AA4{stroke:#EDF0FD;} + .d2-3381334095 .stroke-AA5{stroke:#F7F8FE;} + .d2-3381334095 .stroke-AB4{stroke:#EDF0FD;} + .d2-3381334095 .stroke-AB5{stroke:#F7F8FE;} + .d2-3381334095 .background-color-N1{background-color:#0A0F25;} + .d2-3381334095 .background-color-N2{background-color:#676C7E;} + .d2-3381334095 .background-color-N3{background-color:#9499AB;} + .d2-3381334095 .background-color-N4{background-color:#CFD2DD;} + .d2-3381334095 .background-color-N5{background-color:#DEE1EB;} + .d2-3381334095 .background-color-N6{background-color:#EEF1F8;} + .d2-3381334095 .background-color-N7{background-color:#FFFFFF;} + .d2-3381334095 .background-color-B1{background-color:#0D32B2;} + .d2-3381334095 .background-color-B2{background-color:#0D32B2;} + .d2-3381334095 .background-color-B3{background-color:#E3E9FD;} + .d2-3381334095 .background-color-B4{background-color:#E3E9FD;} + .d2-3381334095 .background-color-B5{background-color:#EDF0FD;} + .d2-3381334095 .background-color-B6{background-color:#F7F8FE;} + .d2-3381334095 .background-color-AA2{background-color:#4A6FF3;} + .d2-3381334095 .background-color-AA4{background-color:#EDF0FD;} + .d2-3381334095 .background-color-AA5{background-color:#F7F8FE;} + .d2-3381334095 .background-color-AB4{background-color:#EDF0FD;} + .d2-3381334095 .background-color-AB5{background-color:#F7F8FE;} + .d2-3381334095 .color-N1{color:#0A0F25;} + .d2-3381334095 .color-N2{color:#676C7E;} + .d2-3381334095 .color-N3{color:#9499AB;} + .d2-3381334095 .color-N4{color:#CFD2DD;} + .d2-3381334095 .color-N5{color:#DEE1EB;} + .d2-3381334095 .color-N6{color:#EEF1F8;} + .d2-3381334095 .color-N7{color:#FFFFFF;} + .d2-3381334095 .color-B1{color:#0D32B2;} + .d2-3381334095 .color-B2{color:#0D32B2;} + .d2-3381334095 .color-B3{color:#E3E9FD;} + .d2-3381334095 .color-B4{color:#E3E9FD;} + .d2-3381334095 .color-B5{color:#EDF0FD;} + .d2-3381334095 .color-B6{color:#F7F8FE;} + .d2-3381334095 .color-AA2{color:#4A6FF3;} + .d2-3381334095 .color-AA4{color:#EDF0FD;} + .d2-3381334095 .color-AA5{color:#F7F8FE;} + .d2-3381334095 .color-AB4{color:#EDF0FD;} + .d2-3381334095 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>grid w/ container + icongrid + icongrid + icon w/ containerabcdabcdabcdabcdabcdb childb childb child + \ No newline at end of file diff --git a/e2etests/testdata/stable/grid_icon/elk/board.exp.json b/e2etests/testdata/stable/grid_icon/elk/board.exp.json index 4cbb1ccf2..be644853a 100644 --- a/e2etests/testdata/stable/grid_icon/elk/board.exp.json +++ b/e2etests/testdata/stable/grid_icon/elk/board.exp.json @@ -11,7 +11,7 @@ ], "pos": { "x": 12, - "y": 20 + "y": 27 }, "width": 404, "height": 416, @@ -52,7 +52,7 @@ "type": "rectangle", "pos": { "x": 72, - "y": 80 + "y": 87 }, "width": 53, "height": 190, @@ -93,7 +93,7 @@ "type": "rectangle", "pos": { "x": 165, - "y": 80 + "y": 87 }, "width": 191, "height": 190, @@ -122,7 +122,7 @@ "Fragment": "", "RawFragment": "" }, - "iconPosition": "INSIDE_MIDDLE_CENTER", + "iconPosition": "INSIDE_TOP_LEFT", "blend": false, "fields": null, "methods": null, @@ -137,7 +137,7 @@ "underline": false, "labelWidth": 12, "labelHeight": 31, - "labelPosition": "INSIDE_TOP_CENTER", + "labelPosition": "INSIDE_TOP_RIGHT", "zIndex": 0, "level": 2 }, @@ -146,7 +146,7 @@ "type": "rectangle", "pos": { "x": 215, - "y": 154 + "y": 161 }, "width": 91, "height": 66, @@ -187,7 +187,7 @@ "type": "rectangle", "pos": { "x": 72, - "y": 310 + "y": 317 }, "width": 53, "height": 66, @@ -228,7 +228,7 @@ "type": "rectangle", "pos": { "x": 165, - "y": 310 + "y": 317 }, "width": 191, "height": 66, @@ -274,8 +274,8 @@ "x": 436, "y": 62 }, - "width": 267, - "height": 333, + "width": 278, + "height": 347, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -301,7 +301,7 @@ "Fragment": "", "RawFragment": "" }, - "iconPosition": "INSIDE_MIDDLE_CENTER", + "iconPosition": "INSIDE_TOP_LEFT", "blend": false, "fields": null, "methods": null, @@ -324,8 +324,8 @@ "id": "grid + icon.a", "type": "rectangle", "pos": { - "x": 496, - "y": 122 + "x": 501, + "y": 136 }, "width": 53, "height": 66, @@ -365,8 +365,8 @@ "id": "grid + icon.b", "type": "rectangle", "pos": { - "x": 589, - "y": 122 + "x": 594, + "y": 136 }, "width": 54, "height": 66, @@ -406,8 +406,8 @@ "id": "grid + icon.c", "type": "rectangle", "pos": { - "x": 496, - "y": 228 + "x": 501, + "y": 242 }, "width": 53, "height": 66, @@ -447,8 +447,8 @@ "id": "grid + icon.d", "type": "rectangle", "pos": { - "x": 589, - "y": 228 + "x": 594, + "y": 242 }, "width": 54, "height": 66, @@ -491,11 +491,11 @@ "2x2" ], "pos": { - "x": 723, + "x": 734, "y": 12 }, - "width": 404, - "height": 433, + "width": 433, + "height": 447, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -521,7 +521,7 @@ "Fragment": "", "RawFragment": "" }, - "iconPosition": "INSIDE_MIDDLE_CENTER", + "iconPosition": "INSIDE_TOP_LEFT", "blend": false, "fields": null, "methods": null, @@ -544,8 +544,8 @@ "id": "grid + icon w/ container.a", "type": "rectangle", "pos": { - "x": 783, - "y": 72 + "x": 808, + "y": 86 }, "width": 53, "height": 166, @@ -585,8 +585,8 @@ "id": "grid + icon w/ container.b", "type": "rectangle", "pos": { - "x": 876, - "y": 72 + "x": 901, + "y": 86 }, "width": 191, "height": 166, @@ -618,7 +618,7 @@ "underline": false, "labelWidth": 12, "labelHeight": 31, - "labelPosition": "INSIDE_MIDDLE_CENTER", + "labelPosition": "INSIDE_TOP_CENTER", "zIndex": 0, "level": 2 }, @@ -626,8 +626,8 @@ "id": "grid + icon w/ container.b.b child", "type": "rectangle", "pos": { - "x": 926, - "y": 122 + "x": 951, + "y": 136 }, "width": 91, "height": 66, @@ -667,8 +667,8 @@ "id": "grid + icon w/ container.c", "type": "rectangle", "pos": { - "x": 783, - "y": 278 + "x": 808, + "y": 292 }, "width": 53, "height": 66, @@ -708,8 +708,8 @@ "id": "grid + icon w/ container.d", "type": "rectangle", "pos": { - "x": 876, - "y": 278 + "x": 901, + "y": 292 }, "width": 191, "height": 66, @@ -752,8 +752,8 @@ "2x2" ], "pos": { - "x": 1147, - "y": 20 + "x": 1187, + "y": 27 }, "width": 404, "height": 416, @@ -793,8 +793,8 @@ "id": "no label grid w/ container + icon.a", "type": "rectangle", "pos": { - "x": 1207, - "y": 80 + "x": 1247, + "y": 87 }, "width": 53, "height": 190, @@ -834,8 +834,8 @@ "id": "no label grid w/ container + icon.b", "type": "rectangle", "pos": { - "x": 1300, - "y": 80 + "x": 1340, + "y": 87 }, "width": 191, "height": 190, @@ -864,7 +864,7 @@ "Fragment": "", "RawFragment": "" }, - "iconPosition": "INSIDE_MIDDLE_CENTER", + "iconPosition": "INSIDE_TOP_LEFT", "blend": false, "fields": null, "methods": null, @@ -879,7 +879,7 @@ "underline": false, "labelWidth": 12, "labelHeight": 31, - "labelPosition": "INSIDE_TOP_CENTER", + "labelPosition": "INSIDE_TOP_RIGHT", "zIndex": 0, "level": 2 }, @@ -887,8 +887,8 @@ "id": "no label grid w/ container + icon.b.b child", "type": "rectangle", "pos": { - "x": 1350, - "y": 154 + "x": 1390, + "y": 161 }, "width": 91, "height": 66, @@ -928,8 +928,8 @@ "id": "no label grid w/ container + icon.c", "type": "rectangle", "pos": { - "x": 1207, - "y": 310 + "x": 1247, + "y": 317 }, "width": 53, "height": 66, @@ -969,8 +969,8 @@ "id": "no label grid w/ container + icon.d", "type": "rectangle", "pos": { - "x": 1300, - "y": 310 + "x": 1340, + "y": 317 }, "width": 191, "height": 66, @@ -1013,11 +1013,11 @@ "2x2" ], "pos": { - "x": 1571, + "x": 1611, "y": 82 }, "width": 267, - "height": 292, + "height": 306, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -1043,7 +1043,7 @@ "Fragment": "", "RawFragment": "" }, - "iconPosition": "INSIDE_MIDDLE_CENTER", + "iconPosition": "INSIDE_TOP_LEFT", "blend": false, "fields": null, "methods": null, @@ -1066,8 +1066,8 @@ "id": "no label grid + icon.a", "type": "rectangle", "pos": { - "x": 1631, - "y": 142 + "x": 1671, + "y": 156 }, "width": 53, "height": 66, @@ -1107,8 +1107,8 @@ "id": "no label grid + icon.b", "type": "rectangle", "pos": { - "x": 1724, - "y": 142 + "x": 1764, + "y": 156 }, "width": 54, "height": 66, @@ -1148,8 +1148,8 @@ "id": "no label grid + icon.c", "type": "rectangle", "pos": { - "x": 1631, - "y": 248 + "x": 1671, + "y": 262 }, "width": 53, "height": 66, @@ -1189,8 +1189,8 @@ "id": "no label grid + icon.d", "type": "rectangle", "pos": { - "x": 1724, - "y": 248 + "x": 1764, + "y": 262 }, "width": 54, "height": 66, diff --git a/e2etests/testdata/stable/grid_icon/elk/sketch.exp.svg b/e2etests/testdata/stable/grid_icon/elk/sketch.exp.svg index f10778ef9..ab9421789 100644 --- a/e2etests/testdata/stable/grid_icon/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/grid_icon/elk/sketch.exp.svg @@ -1,16 +1,16 @@ -grid w/ container + icongrid + icongrid + icon w/ containerabcdabcdabcdabcdabcdb childb childb child - + .d2-433253505 .fill-N1{fill:#0A0F25;} + .d2-433253505 .fill-N2{fill:#676C7E;} + .d2-433253505 .fill-N3{fill:#9499AB;} + .d2-433253505 .fill-N4{fill:#CFD2DD;} + .d2-433253505 .fill-N5{fill:#DEE1EB;} + .d2-433253505 .fill-N6{fill:#EEF1F8;} + .d2-433253505 .fill-N7{fill:#FFFFFF;} + .d2-433253505 .fill-B1{fill:#0D32B2;} + .d2-433253505 .fill-B2{fill:#0D32B2;} + .d2-433253505 .fill-B3{fill:#E3E9FD;} + .d2-433253505 .fill-B4{fill:#E3E9FD;} + .d2-433253505 .fill-B5{fill:#EDF0FD;} + .d2-433253505 .fill-B6{fill:#F7F8FE;} + .d2-433253505 .fill-AA2{fill:#4A6FF3;} + .d2-433253505 .fill-AA4{fill:#EDF0FD;} + .d2-433253505 .fill-AA5{fill:#F7F8FE;} + .d2-433253505 .fill-AB4{fill:#EDF0FD;} + .d2-433253505 .fill-AB5{fill:#F7F8FE;} + .d2-433253505 .stroke-N1{stroke:#0A0F25;} + .d2-433253505 .stroke-N2{stroke:#676C7E;} + .d2-433253505 .stroke-N3{stroke:#9499AB;} + .d2-433253505 .stroke-N4{stroke:#CFD2DD;} + .d2-433253505 .stroke-N5{stroke:#DEE1EB;} + .d2-433253505 .stroke-N6{stroke:#EEF1F8;} + .d2-433253505 .stroke-N7{stroke:#FFFFFF;} + .d2-433253505 .stroke-B1{stroke:#0D32B2;} + .d2-433253505 .stroke-B2{stroke:#0D32B2;} + .d2-433253505 .stroke-B3{stroke:#E3E9FD;} + .d2-433253505 .stroke-B4{stroke:#E3E9FD;} + .d2-433253505 .stroke-B5{stroke:#EDF0FD;} + .d2-433253505 .stroke-B6{stroke:#F7F8FE;} + .d2-433253505 .stroke-AA2{stroke:#4A6FF3;} + .d2-433253505 .stroke-AA4{stroke:#EDF0FD;} + .d2-433253505 .stroke-AA5{stroke:#F7F8FE;} + .d2-433253505 .stroke-AB4{stroke:#EDF0FD;} + .d2-433253505 .stroke-AB5{stroke:#F7F8FE;} + .d2-433253505 .background-color-N1{background-color:#0A0F25;} + .d2-433253505 .background-color-N2{background-color:#676C7E;} + .d2-433253505 .background-color-N3{background-color:#9499AB;} + .d2-433253505 .background-color-N4{background-color:#CFD2DD;} + .d2-433253505 .background-color-N5{background-color:#DEE1EB;} + .d2-433253505 .background-color-N6{background-color:#EEF1F8;} + .d2-433253505 .background-color-N7{background-color:#FFFFFF;} + .d2-433253505 .background-color-B1{background-color:#0D32B2;} + .d2-433253505 .background-color-B2{background-color:#0D32B2;} + .d2-433253505 .background-color-B3{background-color:#E3E9FD;} + .d2-433253505 .background-color-B4{background-color:#E3E9FD;} + .d2-433253505 .background-color-B5{background-color:#EDF0FD;} + .d2-433253505 .background-color-B6{background-color:#F7F8FE;} + .d2-433253505 .background-color-AA2{background-color:#4A6FF3;} + .d2-433253505 .background-color-AA4{background-color:#EDF0FD;} + .d2-433253505 .background-color-AA5{background-color:#F7F8FE;} + .d2-433253505 .background-color-AB4{background-color:#EDF0FD;} + .d2-433253505 .background-color-AB5{background-color:#F7F8FE;} + .d2-433253505 .color-N1{color:#0A0F25;} + .d2-433253505 .color-N2{color:#676C7E;} + .d2-433253505 .color-N3{color:#9499AB;} + .d2-433253505 .color-N4{color:#CFD2DD;} + .d2-433253505 .color-N5{color:#DEE1EB;} + .d2-433253505 .color-N6{color:#EEF1F8;} + .d2-433253505 .color-N7{color:#FFFFFF;} + .d2-433253505 .color-B1{color:#0D32B2;} + .d2-433253505 .color-B2{color:#0D32B2;} + .d2-433253505 .color-B3{color:#E3E9FD;} + .d2-433253505 .color-B4{color:#E3E9FD;} + .d2-433253505 .color-B5{color:#EDF0FD;} + .d2-433253505 .color-B6{color:#F7F8FE;} + .d2-433253505 .color-AA2{color:#4A6FF3;} + .d2-433253505 .color-AA4{color:#EDF0FD;} + .d2-433253505 .color-AA5{color:#F7F8FE;} + .d2-433253505 .color-AB4{color:#EDF0FD;} + .d2-433253505 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>grid w/ container + icongrid + icongrid + icon w/ containerabcdabcdabcdabcdabcdb childb childb child + \ No newline at end of file From 0a78ee87eb06288bd56552c687b4c22289a30726 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Wed, 31 May 2023 11:02:10 -0700 Subject: [PATCH 9/9] factor out d2graph/layout.go --- d2graph/d2graph.go | 182 ------------------------------------------ d2graph/layout.go | 191 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+), 182 deletions(-) create mode 100644 d2graph/layout.go diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go index f43837ef8..301cc5bd8 100644 --- a/d2graph/d2graph.go +++ b/d2graph/d2graph.go @@ -26,7 +26,6 @@ import ( "oss.terrastruct.com/d2/d2themes/d2themescatalog" "oss.terrastruct.com/d2/lib/color" "oss.terrastruct.com/d2/lib/geo" - "oss.terrastruct.com/d2/lib/label" "oss.terrastruct.com/d2/lib/shape" "oss.terrastruct.com/d2/lib/textmeasure" ) @@ -1818,90 +1817,6 @@ func (g *Graph) ApplyTheme(themeID int64) error { return nil } -func (obj *Object) MoveWithDescendants(dx, dy float64) { - obj.TopLeft.X += dx - obj.TopLeft.Y += dy - for _, child := range obj.ChildrenArray { - child.MoveWithDescendants(dx, dy) - } -} - -func (obj *Object) MoveWithDescendantsTo(x, y float64) { - dx := x - obj.TopLeft.X - dy := y - obj.TopLeft.Y - obj.MoveWithDescendants(dx, dy) -} - -func (parent *Object) removeChild(child *Object) { - delete(parent.Children, strings.ToLower(child.ID)) - for i := 0; i < len(parent.ChildrenArray); i++ { - if parent.ChildrenArray[i] == child { - parent.ChildrenArray = append(parent.ChildrenArray[:i], parent.ChildrenArray[i+1:]...) - break - } - } -} - -// remove obj and all descendants from graph, as a new Graph -func (g *Graph) ExtractAsNestedGraph(obj *Object) *Graph { - descendantObjects, edges := pluckObjAndEdges(g, obj) - - tempGraph := NewGraph() - tempGraph.Root.ChildrenArray = []*Object{obj} - tempGraph.Root.Children[strings.ToLower(obj.ID)] = obj - - for _, descendantObj := range descendantObjects { - descendantObj.Graph = tempGraph - } - tempGraph.Objects = descendantObjects - tempGraph.Edges = edges - - obj.Parent.removeChild(obj) - obj.Parent = tempGraph.Root - - return tempGraph -} - -func pluckObjAndEdges(g *Graph, obj *Object) (descendantsObjects []*Object, edges []*Edge) { - for i := 0; i < len(g.Edges); i++ { - edge := g.Edges[i] - if edge.Src == obj || edge.Dst == obj { - edges = append(edges, edge) - g.Edges = append(g.Edges[:i], g.Edges[i+1:]...) - i-- - } - } - - for i := 0; i < len(g.Objects); i++ { - temp := g.Objects[i] - if temp.AbsID() == obj.AbsID() { - descendantsObjects = append(descendantsObjects, obj) - g.Objects = append(g.Objects[:i], g.Objects[i+1:]...) - for _, child := range obj.ChildrenArray { - subObjects, subEdges := pluckObjAndEdges(g, child) - descendantsObjects = append(descendantsObjects, subObjects...) - edges = append(edges, subEdges...) - } - break - } - } - - return descendantsObjects, edges -} - -func (g *Graph) InjectNestedGraph(tempGraph *Graph, parent *Object) { - obj := tempGraph.Root.ChildrenArray[0] - obj.MoveWithDescendantsTo(0, 0) - obj.Parent = parent - for _, obj := range tempGraph.Objects { - obj.Graph = g - } - g.Objects = append(g.Objects, tempGraph.Objects...) - parent.Children[strings.ToLower(obj.ID)] = obj - parent.ChildrenArray = append(parent.ChildrenArray, obj) - g.Edges = append(g.Edges, tempGraph.Edges...) -} - func (g *Graph) PrintString() string { buf := &bytes.Buffer{} fmt.Fprint(buf, "Objects: [") @@ -1919,54 +1834,6 @@ func (obj *Object) IterDescendants(apply func(parent, child *Object)) { } } -// ShiftDescendants moves Object's descendants (not including itself) -// descendants' edges are also moved by the same dx and dy (the whole route is moved if both ends are a descendant) -func (obj *Object) ShiftDescendants(dx, dy float64) { - // also need to shift edges of descendants that are shifted - movedEdges := make(map[*Edge]struct{}) - for _, e := range obj.Graph.Edges { - isSrcDesc := e.Src.IsDescendantOf(obj) - isDstDesc := e.Dst.IsDescendantOf(obj) - - if isSrcDesc && isDstDesc { - movedEdges[e] = struct{}{} - for _, p := range e.Route { - p.X += dx - p.Y += dy - } - } - } - - obj.IterDescendants(func(_, curr *Object) { - curr.TopLeft.X += dx - curr.TopLeft.Y += dy - for _, e := range obj.Graph.Edges { - if _, ok := movedEdges[e]; ok { - continue - } - isSrc := e.Src == curr - isDst := e.Dst == curr - - if isSrc && isDst { - for _, p := range e.Route { - p.X += dx - p.Y += dy - } - } else if isSrc { - e.Route[0].X += dx - e.Route[0].Y += dy - } else if isDst { - e.Route[len(e.Route)-1].X += dx - e.Route[len(e.Route)-1].Y += dy - } - - if isSrc || isDst { - movedEdges[e] = struct{}{} - } - } - }) -} - func (obj *Object) IsMultiple() bool { return obj.Style.Multiple != nil && obj.Style.Multiple.Value == "true" } @@ -1974,52 +1841,3 @@ func (obj *Object) IsMultiple() bool { func (obj *Object) Is3D() bool { return obj.Style.ThreeDee != nil && obj.Style.ThreeDee.Value == "true" } - -// GetModifierElementAdjustments returns width/height adjustments to account for shapes with 3d or multiple -func (obj *Object) GetModifierElementAdjustments() (dx, dy float64) { - if obj.Is3D() { - if obj.Shape.Value == d2target.ShapeHexagon { - dy = d2target.THREE_DEE_OFFSET / 2 - } else { - dy = d2target.THREE_DEE_OFFSET - } - dx = d2target.THREE_DEE_OFFSET - } else if obj.IsMultiple() { - dy = d2target.MULTIPLE_OFFSET - dx = d2target.MULTIPLE_OFFSET - } - return dx, dy -} - -func (obj *Object) ToShape() shape.Shape { - tl := obj.TopLeft - if tl == nil { - tl = geo.NewPoint(0, 0) - } - dslShape := strings.ToLower(obj.Shape.Value) - shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[dslShape] - contentBox := geo.NewBox(tl, obj.Width, obj.Height) - return shape.NewShape(shapeType, contentBox) -} - -func (obj *Object) GetLabelTopLeft() *geo.Point { - if obj.LabelPosition == nil { - return nil - } - - s := obj.ToShape() - labelPosition := label.Position(*obj.LabelPosition) - - var box *geo.Box - if labelPosition.IsOutside() { - box = s.GetBox() - } else { - box = s.GetInnerBox() - } - - labelTL := labelPosition.GetPointOnBox(box, label.PADDING, - float64(obj.LabelDimensions.Width), - float64(obj.LabelDimensions.Height), - ) - return labelTL -} diff --git a/d2graph/layout.go b/d2graph/layout.go new file mode 100644 index 000000000..43342daa1 --- /dev/null +++ b/d2graph/layout.go @@ -0,0 +1,191 @@ +package d2graph + +import ( + "strings" + + "oss.terrastruct.com/d2/d2target" + "oss.terrastruct.com/d2/lib/geo" + "oss.terrastruct.com/d2/lib/label" + "oss.terrastruct.com/d2/lib/shape" +) + +func (obj *Object) MoveWithDescendants(dx, dy float64) { + obj.TopLeft.X += dx + obj.TopLeft.Y += dy + for _, child := range obj.ChildrenArray { + child.MoveWithDescendants(dx, dy) + } +} + +func (obj *Object) MoveWithDescendantsTo(x, y float64) { + dx := x - obj.TopLeft.X + dy := y - obj.TopLeft.Y + obj.MoveWithDescendants(dx, dy) +} + +func (parent *Object) removeChild(child *Object) { + delete(parent.Children, strings.ToLower(child.ID)) + for i := 0; i < len(parent.ChildrenArray); i++ { + if parent.ChildrenArray[i] == child { + parent.ChildrenArray = append(parent.ChildrenArray[:i], parent.ChildrenArray[i+1:]...) + break + } + } +} + +// remove obj and all descendants from graph, as a new Graph +func (g *Graph) ExtractAsNestedGraph(obj *Object) *Graph { + descendantObjects, edges := pluckObjAndEdges(g, obj) + + tempGraph := NewGraph() + tempGraph.Root.ChildrenArray = []*Object{obj} + tempGraph.Root.Children[strings.ToLower(obj.ID)] = obj + + for _, descendantObj := range descendantObjects { + descendantObj.Graph = tempGraph + } + tempGraph.Objects = descendantObjects + tempGraph.Edges = edges + + obj.Parent.removeChild(obj) + obj.Parent = tempGraph.Root + + return tempGraph +} + +func pluckObjAndEdges(g *Graph, obj *Object) (descendantsObjects []*Object, edges []*Edge) { + for i := 0; i < len(g.Edges); i++ { + edge := g.Edges[i] + if edge.Src == obj || edge.Dst == obj { + edges = append(edges, edge) + g.Edges = append(g.Edges[:i], g.Edges[i+1:]...) + i-- + } + } + + for i := 0; i < len(g.Objects); i++ { + temp := g.Objects[i] + if temp.AbsID() == obj.AbsID() { + descendantsObjects = append(descendantsObjects, obj) + g.Objects = append(g.Objects[:i], g.Objects[i+1:]...) + for _, child := range obj.ChildrenArray { + subObjects, subEdges := pluckObjAndEdges(g, child) + descendantsObjects = append(descendantsObjects, subObjects...) + edges = append(edges, subEdges...) + } + break + } + } + + return descendantsObjects, edges +} + +func (g *Graph) InjectNestedGraph(tempGraph *Graph, parent *Object) { + obj := tempGraph.Root.ChildrenArray[0] + obj.MoveWithDescendantsTo(0, 0) + obj.Parent = parent + for _, obj := range tempGraph.Objects { + obj.Graph = g + } + g.Objects = append(g.Objects, tempGraph.Objects...) + parent.Children[strings.ToLower(obj.ID)] = obj + parent.ChildrenArray = append(parent.ChildrenArray, obj) + g.Edges = append(g.Edges, tempGraph.Edges...) +} + +// ShiftDescendants moves Object's descendants (not including itself) +// descendants' edges are also moved by the same dx and dy (the whole route is moved if both ends are a descendant) +func (obj *Object) ShiftDescendants(dx, dy float64) { + // also need to shift edges of descendants that are shifted + movedEdges := make(map[*Edge]struct{}) + for _, e := range obj.Graph.Edges { + isSrcDesc := e.Src.IsDescendantOf(obj) + isDstDesc := e.Dst.IsDescendantOf(obj) + + if isSrcDesc && isDstDesc { + movedEdges[e] = struct{}{} + for _, p := range e.Route { + p.X += dx + p.Y += dy + } + } + } + + obj.IterDescendants(func(_, curr *Object) { + curr.TopLeft.X += dx + curr.TopLeft.Y += dy + for _, e := range obj.Graph.Edges { + if _, ok := movedEdges[e]; ok { + continue + } + isSrc := e.Src == curr + isDst := e.Dst == curr + + if isSrc && isDst { + for _, p := range e.Route { + p.X += dx + p.Y += dy + } + } else if isSrc { + e.Route[0].X += dx + e.Route[0].Y += dy + } else if isDst { + e.Route[len(e.Route)-1].X += dx + e.Route[len(e.Route)-1].Y += dy + } + + if isSrc || isDst { + movedEdges[e] = struct{}{} + } + } + }) +} + +// GetModifierElementAdjustments returns width/height adjustments to account for shapes with 3d or multiple +func (obj *Object) GetModifierElementAdjustments() (dx, dy float64) { + if obj.Is3D() { + if obj.Shape.Value == d2target.ShapeHexagon { + dy = d2target.THREE_DEE_OFFSET / 2 + } else { + dy = d2target.THREE_DEE_OFFSET + } + dx = d2target.THREE_DEE_OFFSET + } else if obj.IsMultiple() { + dy = d2target.MULTIPLE_OFFSET + dx = d2target.MULTIPLE_OFFSET + } + return dx, dy +} + +func (obj *Object) ToShape() shape.Shape { + tl := obj.TopLeft + if tl == nil { + tl = geo.NewPoint(0, 0) + } + dslShape := strings.ToLower(obj.Shape.Value) + shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[dslShape] + contentBox := geo.NewBox(tl, obj.Width, obj.Height) + return shape.NewShape(shapeType, contentBox) +} + +func (obj *Object) GetLabelTopLeft() *geo.Point { + if obj.LabelPosition == nil { + return nil + } + + s := obj.ToShape() + labelPosition := label.Position(*obj.LabelPosition) + + var box *geo.Box + if labelPosition.IsOutside() { + box = s.GetBox() + } else { + box = s.GetInnerBox() + } + + labelTL := labelPosition.GetPointOnBox(box, label.PADDING, + float64(obj.LabelDimensions.Width), + float64(obj.LabelDimensions.Height), + ) + return labelTL +}