From 0adec7fea2b071b2cdd382f293feec57bf1e2649 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Sun, 25 Dec 2022 13:42:11 -0800 Subject: [PATCH 01/11] compiler --- d2compiler/compile.go | 11 +- d2compiler/compile_test.go | 24 ++- d2graph/d2graph.go | 26 +++ .../errors/reserved_icon_style.exp.json | 2 +- .../TestCompile/near_bad_constant.exp.json | 12 ++ .../TestCompile/near_constant.exp.json | 149 ++++++++++++++++++ .../TestCompile/nested_near_constant.exp.json | 12 ++ 7 files changed, 231 insertions(+), 5 deletions(-) create mode 100644 testdata/d2compiler/TestCompile/near_bad_constant.exp.json create mode 100644 testdata/d2compiler/TestCompile/near_constant.exp.json create mode 100644 testdata/d2compiler/TestCompile/nested_near_constant.exp.json diff --git a/d2compiler/compile.go b/d2compiler/compile.go index 9ca656098..9ca373d81 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -850,9 +850,14 @@ func (c *compiler) validateKeys(obj *d2graph.Object, m *d2ast.Map) { func (c *compiler) validateNear(g *d2graph.Graph) { for _, obj := range g.Objects { if obj.Attributes.NearKey != nil { - _, ok := g.Root.HasChild(d2graph.Key(obj.Attributes.NearKey)) - if !ok { - c.errorf(obj.Attributes.NearKey.GetRange().Start, obj.Attributes.NearKey.GetRange().End, "near key %#v does not exist. It must be the absolute path to a shape.", d2format.Format(obj.Attributes.NearKey)) + _, isKey := g.Root.HasChild(d2graph.Key(obj.Attributes.NearKey)) + _, isConst := d2graph.NearConstants[d2graph.Key(obj.Attributes.NearKey)[0]] + if !isKey && !isConst { + c.errorf(obj.Attributes.NearKey.GetRange().Start, obj.Attributes.NearKey.GetRange().End, "near key %#v must be the absolute path to a shape or one of the following constants: %s", d2format.Format(obj.Attributes.NearKey), strings.Join(d2graph.NearConstantsArray, ", ")) + continue + } + if !isKey && isConst && obj.Parent != g.Root { + c.errorf(obj.Attributes.NearKey.GetRange().Start, obj.Attributes.NearKey.GetRange().End, "constant near keys can only be set on root level shapes") continue } } diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index afe9ceec4..d6459f34d 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -1266,6 +1266,28 @@ x -> y: { } }, }, + { + name: "near_constant", + + text: `x.near: top-center +`, + }, + { + name: "near_bad_constant", + + text: `x.near: txop-center +`, + expErr: `d2/testdata/d2compiler/TestCompile/near_bad_constant.d2:1:1: near key "txop-center" must be the absolute path to a shape or one of the following constants: top-left, top-center, top-right, center-left, center-right, bottom-left, bottom-center, bottom-right +`, + }, + { + name: "nested_near_constant", + + text: `x.y.near: top-center +`, + expErr: `d2/testdata/d2compiler/TestCompile/nested_near_constant.d2:1:1: constant near keys can only be set on root level shapes +`, + }, { name: "reserved_icon_near_style", @@ -1312,7 +1334,7 @@ y expErr: `d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2:3:9: bad icon url "::????:::%%orange": parse "::????:::%%orange": missing protocol scheme d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2:4:18: expected "opacity" to be a number between 0.0 and 1.0 d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2:5:18: expected "opacity" to be a number between 0.0 and 1.0 -d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2:1:1: near key "y" does not exist. It must be the absolute path to a shape. +d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2:1:1: near key "y" must be the absolute path to a shape or one of the following constants: top-left, top-center, top-right, center-left, center-right, bottom-left, bottom-center, bottom-right `, }, { diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go index e60aa17c0..05dd13831 100644 --- a/d2graph/d2graph.go +++ b/d2graph/d2graph.go @@ -1189,6 +1189,32 @@ var StyleKeywords = map[string]struct{}{ "filled": {}, } +var NearConstants = map[string]struct{}{ + "top-left": {}, + "top-center": {}, + "top-right": {}, + + "center-left": {}, + "center-right": {}, + + "bottom-left": {}, + "bottom-center": {}, + "bottom-right": {}, +} + +var NearConstantsArray = []string{ + "top-left", + "top-center", + "top-right", + + "center-left", + "center-right", + + "bottom-left", + "bottom-center", + "bottom-right", +} + func init() { for k, v := range StyleKeywords { ReservedKeywords[k] = v diff --git a/testdata/d2compiler/TestCompile/errors/reserved_icon_style.exp.json b/testdata/d2compiler/TestCompile/errors/reserved_icon_style.exp.json index 4591c13df..3fa65dd2a 100644 --- a/testdata/d2compiler/TestCompile/errors/reserved_icon_style.exp.json +++ b/testdata/d2compiler/TestCompile/errors/reserved_icon_style.exp.json @@ -17,7 +17,7 @@ }, { "range": "d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2,0:0:0-0:1:1", - "errmsg": "d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2:1:1: near key \"y\" does not exist. It must be the absolute path to a shape." + "errmsg": "d2/testdata/d2compiler/TestCompile/errors/reserved_icon_style.d2:1:1: near key \"y\" must be the absolute path to a shape or one of the following constants: top-left, top-center, top-right, center-left, center-right, bottom-left, bottom-center, bottom-right" } ] } diff --git a/testdata/d2compiler/TestCompile/near_bad_constant.exp.json b/testdata/d2compiler/TestCompile/near_bad_constant.exp.json new file mode 100644 index 000000000..7026afd59 --- /dev/null +++ b/testdata/d2compiler/TestCompile/near_bad_constant.exp.json @@ -0,0 +1,12 @@ +{ + "graph": null, + "err": { + "ioerr": null, + "errs": [ + { + "range": "d2/testdata/d2compiler/TestCompile/near_bad_constant.d2,0:0:0-0:11:11", + "errmsg": "d2/testdata/d2compiler/TestCompile/near_bad_constant.d2:1:1: near key \"txop-center\" must be the absolute path to a shape or one of the following constants: top-left, top-center, top-right, center-left, center-right, bottom-left, bottom-center, bottom-right" + } + ] + } +} diff --git a/testdata/d2compiler/TestCompile/near_constant.exp.json b/testdata/d2compiler/TestCompile/near_constant.exp.json new file mode 100644 index 000000000..87cc9ff2b --- /dev/null +++ b/testdata/d2compiler/TestCompile/near_constant.exp.json @@ -0,0 +1,149 @@ +{ + "graph": { + "ast": { + "range": "d2/testdata/d2compiler/TestCompile/near_constant.d2,0:0:0-1:0:19", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/near_constant.d2,0:0:0-0:18:18", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/near_constant.d2,0:0:0-0:6:6", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/near_constant.d2,0:0:0-0:1:1", + "value": [ + { + "string": "x", + "raw_string": "x" + } + ] + } + }, + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/near_constant.d2,0:2:2-0:6:6", + "value": [ + { + "string": "near", + "raw_string": "near" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/near_constant.d2,0:8:8-0:18:18", + "value": [ + { + "string": "top-center", + "raw_string": "top-center" + } + ] + } + } + } + } + ] + }, + "root": { + "id": "", + "id_val": "", + "label_dimensions": { + "width": 0, + "height": 0 + }, + "attributes": { + "label": { + "value": "" + }, + "style": {}, + "near_key": null, + "shape": { + "value": "" + }, + "direction": { + "value": "" + } + }, + "zIndex": 0 + }, + "edges": null, + "objects": [ + { + "id": "x", + "id_val": "x", + "label_dimensions": { + "width": 0, + "height": 0 + }, + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/near_constant.d2,0:0:0-0:6:6", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/near_constant.d2,0:0:0-0:1:1", + "value": [ + { + "string": "x", + "raw_string": "x" + } + ] + } + }, + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/near_constant.d2,0:2:2-0:6:6", + "value": [ + { + "string": "near", + "raw_string": "near" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "x" + }, + "style": {}, + "near_key": { + "range": ",0:0:0-0:10:10", + "path": [ + { + "unquoted_string": { + "range": ",0:0:0-0:10:10", + "value": [ + { + "string": "top-center", + "raw_string": "top-center" + } + ] + } + } + ] + }, + "shape": { + "value": "" + }, + "direction": { + "value": "" + } + }, + "zIndex": 0 + } + ] + }, + "err": null +} diff --git a/testdata/d2compiler/TestCompile/nested_near_constant.exp.json b/testdata/d2compiler/TestCompile/nested_near_constant.exp.json new file mode 100644 index 000000000..ac0653715 --- /dev/null +++ b/testdata/d2compiler/TestCompile/nested_near_constant.exp.json @@ -0,0 +1,12 @@ +{ + "graph": null, + "err": { + "ioerr": null, + "errs": [ + { + "range": "d2/testdata/d2compiler/TestCompile/nested_near_constant.d2,0:0:0-0:10:10", + "errmsg": "d2/testdata/d2compiler/TestCompile/nested_near_constant.d2:1:1: constant near keys can only be set on root level shapes" + } + ] + } +} From c32bfdda340e790e4d50831fe28181c15acc9519 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Sun, 25 Dec 2022 16:33:32 -0800 Subject: [PATCH 02/11] constant nears --- d2graph/d2graph.go | 2 + d2layouts/d2near/layout.go | 147 ++++ d2layouts/d2sequence/layout.go | 1 + d2lib/d2.go | 16 +- e2etests/stable_test.go | 19 + .../stable/constant_near/dagre/board.exp.json | 456 ++++++++++ .../stable/constant_near/dagre/sketch.exp.svg | 802 ++++++++++++++++++ .../stable/constant_near/elk/board.exp.json | 447 ++++++++++ .../stable/constant_near/elk/sketch.exp.svg | 802 ++++++++++++++++++ 9 files changed, 2690 insertions(+), 2 deletions(-) create mode 100644 d2layouts/d2near/layout.go create mode 100644 e2etests/testdata/stable/constant_near/dagre/board.exp.json create mode 100644 e2etests/testdata/stable/constant_near/dagre/sketch.exp.svg create mode 100644 e2etests/testdata/stable/constant_near/elk/board.exp.json create mode 100644 e2etests/testdata/stable/constant_near/elk/sketch.exp.svg diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go index 05dd13831..4e3721dbf 100644 --- a/d2graph/d2graph.go +++ b/d2graph/d2graph.go @@ -1189,6 +1189,8 @@ var StyleKeywords = map[string]struct{}{ "filled": {}, } +// TODO maybe autofmt should allow other values, and transform them to conform +// e.g. left-center becomes center-left var NearConstants = map[string]struct{}{ "top-left": {}, "top-center": {}, diff --git a/d2layouts/d2near/layout.go b/d2layouts/d2near/layout.go new file mode 100644 index 000000000..8a735c4c0 --- /dev/null +++ b/d2layouts/d2near/layout.go @@ -0,0 +1,147 @@ +// d2near applies near keywords when they're constants +// Intended to be run as the last stage of layout after the diagram has already undergone layout +package d2near + +import ( + "context" + "math" + "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/util-go/go2" +) + +const pad = 20 + +// Layout finds the shapes which are assigned constant near keywords and places them. +func Layout(ctx context.Context, g *d2graph.Graph, constantNears []*d2graph.Object) error { + if len(constantNears) == 0 { + return nil + } + + // Imagine the grpah has two long texts, one at top center and one at top left. + // Top left should go left enough to not collide with center. + // So place the center ones first, then the later ones will consider them for bounding box + for _, obj := range constantNears { + if strings.Contains(d2graph.Key(obj.Attributes.NearKey)[0], "center") { + obj.TopLeft = geo.NewPoint(place(obj)) + } + } + for _, obj := range constantNears { + if strings.Contains(d2graph.Key(obj.Attributes.NearKey)[0], "center") { + // The z-index for constant nears does not matter, as it will not collide + g.Objects = append(g.Objects, obj) + } + } + + for _, obj := range constantNears { + if !strings.Contains(d2graph.Key(obj.Attributes.NearKey)[0], "center") { + obj.TopLeft = geo.NewPoint(place(obj)) + } + } + for _, obj := range constantNears { + if !strings.Contains(d2graph.Key(obj.Attributes.NearKey)[0], "center") { + // The z-index for constant nears does not matter, as it will not collide + g.Objects = append(g.Objects, obj) + } + } + + // These shapes skipped core layout, which means they also skipped label placements + for _, obj := range constantNears { + if obj.Attributes.Shape.Value == d2target.ShapeImage { + obj.LabelPosition = go2.Pointer(string(label.OutsideBottomCenter)) + } else if obj.Attributes.Icon != nil { + obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) + } else { + obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter)) + } + } + + return nil +} + +// position returns the position of obj, taking into consideration its near value and the diagram +func place(obj *d2graph.Object) (float64, float64) { + tl, br := boundingBox(obj.Graph) + w := br.X - tl.X + h := br.Y - tl.Y + switch d2graph.Key(obj.Attributes.NearKey)[0] { + case "top-left": + return tl.X - obj.Width - pad, tl.Y - obj.Height - pad + case "top-center": + return tl.X + w/2 - obj.Width/2, tl.Y - obj.Height - pad + case "top-right": + return br.X + pad, tl.Y - obj.Height - pad + case "center-left": + return tl.X - obj.Width - pad, tl.Y + h/2 - obj.Height/2 + case "center-right": + return br.X + pad, tl.Y + h/2 - obj.Height/2 + case "bottom-left": + return tl.X - obj.Width - pad, br.Y + pad + case "bottom-center": + return br.X - w/2 - obj.Width/2, br.Y + pad + case "bottom-right": + return br.X + pad, br.Y + pad + } + return 0, 0 + +} + +// WithoutConstantNears removes +func WithoutConstantNears(ctx context.Context, g *d2graph.Graph) (nears []*d2graph.Object) { + for i := 0; i < len(g.Objects); i++ { + obj := g.Objects[i] + if obj.Attributes.NearKey == nil { + continue + } + _, isKey := g.Root.HasChild(d2graph.Key(obj.Attributes.NearKey)) + if isKey { + continue + } + _, isConst := d2graph.NearConstants[d2graph.Key(obj.Attributes.NearKey)[0]] + if isConst { + nears = append(nears, obj) + g.Objects = append(g.Objects[:i], g.Objects[i+1:]...) + i-- + } + } + return nears +} + +// boundingBox gets the center of the graph as defined by shapes +// The bounds taking into consideration only shapes gives more of a feeling of true center +// It differs from d2target.BoundingBox which needs to include every visible thing +func boundingBox(g *d2graph.Graph) (tl, br *geo.Point) { + if len(g.Objects) == 0 { + return geo.NewPoint(0, 0), geo.NewPoint(0, 0) + } + x1 := math.Inf(1) + y1 := math.Inf(1) + x2 := math.Inf(-1) + y2 := math.Inf(-1) + + for _, obj := range g.Objects { + if obj.Attributes.NearKey != nil { + // Top left should not be MORE top than top-center + // But it should go more left if top-center label extends beyond bounds of diagram + switch d2graph.Key(obj.Attributes.NearKey)[0] { + case "top-center", "bottom-center": + x1 = math.Min(x1, obj.TopLeft.X) + x2 = math.Max(x2, obj.TopLeft.X+obj.Width) + case "center-left", "center-right": + y1 = math.Min(y1, obj.TopLeft.Y) + y2 = math.Max(y2, obj.TopLeft.Y+obj.Height) + } + } else { + x1 = math.Min(x1, obj.TopLeft.X) + y1 = math.Min(y1, obj.TopLeft.Y) + x2 = math.Max(x2, obj.TopLeft.X+obj.Width) + y2 = math.Max(y2, obj.TopLeft.Y+obj.Height) + } + } + + return geo.NewPoint(x1, y1), geo.NewPoint(x2, y2) +} diff --git a/d2layouts/d2sequence/layout.go b/d2layouts/d2sequence/layout.go index 55192ceab..1747c859b 100644 --- a/d2layouts/d2sequence/layout.go +++ b/d2layouts/d2sequence/layout.go @@ -69,6 +69,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, layout func(ctx context.Conte layoutEdges, edgeOrder := getLayoutEdges(g, edgesToRemove) g.Edges = layoutEdges layoutObjects, objectOrder := getLayoutObjects(g, objectsToRemove) + // TODO this isn't a proper deletion because the objects still appear as children of the object g.Objects = layoutObjects if g.Root.IsSequenceDiagram() { diff --git a/d2lib/d2.go b/d2lib/d2.go index be71a1536..936124b9f 100644 --- a/d2lib/d2.go +++ b/d2lib/d2.go @@ -9,6 +9,7 @@ import ( "oss.terrastruct.com/d2/d2compiler" "oss.terrastruct.com/d2/d2exporter" "oss.terrastruct.com/d2/d2graph" + "oss.terrastruct.com/d2/d2layouts/d2near" "oss.terrastruct.com/d2/d2layouts/d2sequence" "oss.terrastruct.com/d2/d2renderers/d2fonts" "oss.terrastruct.com/d2/d2target" @@ -48,9 +49,20 @@ func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target return nil, nil, err } - if layout, err := getLayout(opts); err != nil { + coreLayout, err := getLayout(opts) + if err != nil { return nil, nil, err - } else if err := d2sequence.Layout(ctx, g, layout); err != nil { + } + + constantNears := d2near.WithoutConstantNears(ctx, g) + + err = d2sequence.Layout(ctx, g, coreLayout) + if err != nil { + return nil, nil, err + } + + err = d2near.Layout(ctx, g, constantNears) + if err != nil { return nil, nil, err } } diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go index 51daaf0d3..7c54f0833 100644 --- a/e2etests/stable_test.go +++ b/e2etests/stable_test.go @@ -1585,6 +1585,25 @@ container: { label: to right container root } } +`, + }, + { + name: "constant_near", + script: `x -> y +The top of the mountain: { shape: text; near: top-center } +Joe: { shape: person; near: center-left } +Donald: { shape: person; near: center-right } +bottom: |md + # Cats, no less liquid than their shadows, offer no angles to the wind. + + If we can't fix it, it ain't broke. + + Dieters live life in the fasting lane. +| { near: bottom-center } +i am top left: { shape: text; near: top-left } +i am top right: { shape: text; near: top-right } +i am bottom left: { shape: text; near: bottom-left } +i am bottom right: { shape: text; near: bottom-right } `, }, } diff --git a/e2etests/testdata/stable/constant_near/dagre/board.exp.json b/e2etests/testdata/stable/constant_near/dagre/board.exp.json new file mode 100644 index 000000000..9d79df6b5 --- /dev/null +++ b/e2etests/testdata/stable/constant_near/dagre/board.exp.json @@ -0,0 +1,456 @@ +{ + "name": "", + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "x", + "type": "", + "pos": { + "x": 1, + "y": 0 + }, + "width": 113, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#F7F8FE", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "x", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 13, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "y", + "type": "", + "pos": { + "x": 0, + "y": 226 + }, + "width": 114, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#F7F8FE", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "y", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 14, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "The top of the mountain", + "type": "text", + "pos": { + "x": -24, + "y": -44 + }, + "width": 162, + "height": 24, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "#0A0F25", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "The top of the mountain", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 162, + "labelHeight": 24, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "Joe", + "type": "person", + "pos": { + "x": -151, + "y": 113 + }, + "width": 131, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#E3E9FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Joe", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 31, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "Donald", + "type": "person", + "pos": { + "x": 134, + "y": 113 + }, + "width": 155, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#E3E9FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Donald", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 55, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "bottom", + "type": "text", + "pos": { + "x": -414, + "y": 372 + }, + "width": 943, + "height": 130, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "#0A0F25", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "# Cats, no less liquid than their shadows, offer no angles to the wind.\n\nIf we can't fix it, it ain't broke.\n\nDieters live life in the fasting lane.", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "markdown", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 943, + "labelHeight": 130, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "i am top left", + "type": "text", + "pos": { + "x": -515, + "y": -44 + }, + "width": 81, + "height": 24, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "#0A0F25", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "i am top left", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 81, + "labelHeight": 24, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "i am top right", + "type": "text", + "pos": { + "x": 548, + "y": -44 + }, + "width": 91, + "height": 24, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "#0A0F25", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "i am top right", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 91, + "labelHeight": 24, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "i am bottom left", + "type": "text", + "pos": { + "x": -542, + "y": 372 + }, + "width": 108, + "height": 24, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "#0A0F25", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "i am bottom left", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 108, + "labelHeight": 24, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "i am bottom right", + "type": "text", + "pos": { + "x": 548, + "y": 372 + }, + "width": 118, + "height": 24, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "#0A0F25", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "i am bottom right", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 118, + "labelHeight": 24, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + } + ], + "connections": [ + { + "id": "(x -> y)[0]", + "src": "x", + "srcArrow": "none", + "srcLabel": "", + "dst": "y", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 57, + "y": 126 + }, + { + "x": 57, + "y": 166 + }, + { + "x": 57, + "y": 186 + }, + { + "x": 57, + "y": 226 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + } + ] +} diff --git a/e2etests/testdata/stable/constant_near/dagre/sketch.exp.svg b/e2etests/testdata/stable/constant_near/dagre/sketch.exp.svg new file mode 100644 index 000000000..48f61e0eb --- /dev/null +++ b/e2etests/testdata/stable/constant_near/dagre/sketch.exp.svg @@ -0,0 +1,802 @@ + +xy

The top of the mountain

+
JoeDonald

Cats, no less liquid than their shadows, offer no angles to the wind.

+

If we can't fix it, it ain't broke.

+

Dieters live life in the fasting lane.

+

i am top left

+

i am top right

+

i am bottom left

+

i am bottom right

+
+ + +
\ No newline at end of file diff --git a/e2etests/testdata/stable/constant_near/elk/board.exp.json b/e2etests/testdata/stable/constant_near/elk/board.exp.json new file mode 100644 index 000000000..2b2e89b8e --- /dev/null +++ b/e2etests/testdata/stable/constant_near/elk/board.exp.json @@ -0,0 +1,447 @@ +{ + "name": "", + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "x", + "type": "", + "pos": { + "x": 1961, + "y": 16 + }, + "width": 113, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#F7F8FE", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "x", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 13, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "y", + "type": "", + "pos": { + "x": 1960, + "y": 242 + }, + "width": 114, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#F7F8FE", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "y", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 14, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "The top of the mountain", + "type": "text", + "pos": { + "x": 1936, + "y": -28 + }, + "width": 162, + "height": 24, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "#0A0F25", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "The top of the mountain", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 162, + "labelHeight": 24, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "Joe", + "type": "person", + "pos": { + "x": 1809, + "y": 129 + }, + "width": 131, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#E3E9FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Joe", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 31, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "Donald", + "type": "person", + "pos": { + "x": 2094, + "y": 129 + }, + "width": 155, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#E3E9FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Donald", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 55, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "bottom", + "type": "text", + "pos": { + "x": 1546, + "y": 388 + }, + "width": 943, + "height": 130, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "#0A0F25", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "# Cats, no less liquid than their shadows, offer no angles to the wind.\n\nIf we can't fix it, it ain't broke.\n\nDieters live life in the fasting lane.", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "markdown", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 943, + "labelHeight": 130, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "i am top left", + "type": "text", + "pos": { + "x": 1445, + "y": -28 + }, + "width": 81, + "height": 24, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "#0A0F25", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "i am top left", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 81, + "labelHeight": 24, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "i am top right", + "type": "text", + "pos": { + "x": 2509, + "y": -28 + }, + "width": 91, + "height": 24, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "#0A0F25", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "i am top right", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 91, + "labelHeight": 24, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "i am bottom left", + "type": "text", + "pos": { + "x": 1418, + "y": 388 + }, + "width": 108, + "height": 24, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "#0A0F25", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "i am bottom left", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 108, + "labelHeight": 24, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "i am bottom right", + "type": "text", + "pos": { + "x": 2509, + "y": 388 + }, + "width": 118, + "height": 24, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "#0A0F25", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "i am bottom right", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 118, + "labelHeight": 24, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + } + ], + "connections": [ + { + "id": "(x -> y)[0]", + "src": "x", + "srcArrow": "none", + "srcLabel": "", + "dst": "y", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 2017.5, + "y": 142 + }, + { + "x": 2017.5, + "y": 242 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + } + ] +} diff --git a/e2etests/testdata/stable/constant_near/elk/sketch.exp.svg b/e2etests/testdata/stable/constant_near/elk/sketch.exp.svg new file mode 100644 index 000000000..a9a1c7387 --- /dev/null +++ b/e2etests/testdata/stable/constant_near/elk/sketch.exp.svg @@ -0,0 +1,802 @@ + +xy

The top of the mountain

+
JoeDonald

Cats, no less liquid than their shadows, offer no angles to the wind.

+

If we can't fix it, it ain't broke.

+

Dieters live life in the fasting lane.

+

i am top left

+

i am top right

+

i am bottom left

+

i am bottom right

+
+ + +
\ No newline at end of file From 9eaa4e98c4839c49c4d9aea1a04a8f9948646970 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Sun, 25 Dec 2022 16:49:19 -0800 Subject: [PATCH 03/11] cleanup --- d2layouts/d2near/layout.go | 41 ++++++++-------- .../stable/constant_near/elk/board.exp.json | 48 +++++++++---------- .../stable/constant_near/elk/sketch.exp.svg | 18 +++---- 3 files changed, 53 insertions(+), 54 deletions(-) diff --git a/d2layouts/d2near/layout.go b/d2layouts/d2near/layout.go index 8a735c4c0..954e6af30 100644 --- a/d2layouts/d2near/layout.go +++ b/d2layouts/d2near/layout.go @@ -22,30 +22,22 @@ func Layout(ctx context.Context, g *d2graph.Graph, constantNears []*d2graph.Obje return nil } - // Imagine the grpah has two long texts, one at top center and one at top left. + // Imagine the graph has two long texts, one at top center and one at top left. // Top left should go left enough to not collide with center. // So place the center ones first, then the later ones will consider them for bounding box - for _, obj := range constantNears { - if strings.Contains(d2graph.Key(obj.Attributes.NearKey)[0], "center") { - obj.TopLeft = geo.NewPoint(place(obj)) + for _, processCenters := range []bool{true, false} { + for _, obj := range constantNears { + if processCenters == strings.Contains(d2graph.Key(obj.Attributes.NearKey)[0], "center") { + obj.TopLeft = geo.NewPoint(place(obj)) + } } - } - for _, obj := range constantNears { - if strings.Contains(d2graph.Key(obj.Attributes.NearKey)[0], "center") { - // The z-index for constant nears does not matter, as it will not collide - g.Objects = append(g.Objects, obj) - } - } - - for _, obj := range constantNears { - if !strings.Contains(d2graph.Key(obj.Attributes.NearKey)[0], "center") { - obj.TopLeft = geo.NewPoint(place(obj)) - } - } - for _, obj := range constantNears { - if !strings.Contains(d2graph.Key(obj.Attributes.NearKey)[0], "center") { - // The z-index for constant nears does not matter, as it will not collide - g.Objects = append(g.Objects, obj) + for _, obj := range constantNears { + if processCenters == strings.Contains(d2graph.Key(obj.Attributes.NearKey)[0], "center") { + // The z-index for constant nears does not matter, as it will not collide + g.Objects = append(g.Objects, obj) + obj.Parent.Children[obj.ID] = obj + obj.Parent.ChildrenArray = append(obj.Parent.ChildrenArray, obj) + } } } @@ -106,6 +98,13 @@ func WithoutConstantNears(ctx context.Context, g *d2graph.Graph) (nears []*d2gra nears = append(nears, obj) g.Objects = append(g.Objects[:i], g.Objects[i+1:]...) i-- + delete(obj.Parent.Children, obj.ID) + for i := 0; i < len(obj.Parent.ChildrenArray); i++ { + if obj.Parent.ChildrenArray[i] == obj { + obj.Parent.ChildrenArray = append(obj.Parent.ChildrenArray[:i], obj.Parent.ChildrenArray[i+1:]...) + break + } + } } } return nears diff --git a/e2etests/testdata/stable/constant_near/elk/board.exp.json b/e2etests/testdata/stable/constant_near/elk/board.exp.json index 2b2e89b8e..3bde4523c 100644 --- a/e2etests/testdata/stable/constant_near/elk/board.exp.json +++ b/e2etests/testdata/stable/constant_near/elk/board.exp.json @@ -6,8 +6,8 @@ "id": "x", "type": "", "pos": { - "x": 1961, - "y": 16 + "x": 12, + "y": 12 }, "width": 113, "height": 126, @@ -46,8 +46,8 @@ "id": "y", "type": "", "pos": { - "x": 1960, - "y": 242 + "x": 12, + "y": 238 }, "width": 114, "height": 126, @@ -86,8 +86,8 @@ "id": "The top of the mountain", "type": "text", "pos": { - "x": 1936, - "y": -28 + "x": -12, + "y": -32 }, "width": 162, "height": 24, @@ -126,8 +126,8 @@ "id": "Joe", "type": "person", "pos": { - "x": 1809, - "y": 129 + "x": -139, + "y": 125 }, "width": 131, "height": 126, @@ -166,8 +166,8 @@ "id": "Donald", "type": "person", "pos": { - "x": 2094, - "y": 129 + "x": 146, + "y": 125 }, "width": 155, "height": 126, @@ -206,8 +206,8 @@ "id": "bottom", "type": "text", "pos": { - "x": 1546, - "y": 388 + "x": -402, + "y": 384 }, "width": 943, "height": 130, @@ -246,8 +246,8 @@ "id": "i am top left", "type": "text", "pos": { - "x": 1445, - "y": -28 + "x": -503, + "y": -32 }, "width": 81, "height": 24, @@ -286,8 +286,8 @@ "id": "i am top right", "type": "text", "pos": { - "x": 2509, - "y": -28 + "x": 560, + "y": -32 }, "width": 91, "height": 24, @@ -326,8 +326,8 @@ "id": "i am bottom left", "type": "text", "pos": { - "x": 1418, - "y": 388 + "x": -530, + "y": 384 }, "width": 108, "height": 24, @@ -366,8 +366,8 @@ "id": "i am bottom right", "type": "text", "pos": { - "x": 2509, - "y": 388 + "x": 560, + "y": 384 }, "width": 118, "height": 24, @@ -430,12 +430,12 @@ "labelPercentage": 0, "route": [ { - "x": 2017.5, - "y": 142 + "x": 69, + "y": 138 }, { - "x": 2017.5, - "y": 242 + "x": 69, + "y": 238 } ], "animated": false, diff --git a/e2etests/testdata/stable/constant_near/elk/sketch.exp.svg b/e2etests/testdata/stable/constant_near/elk/sketch.exp.svg index a9a1c7387..c2022876a 100644 --- a/e2etests/testdata/stable/constant_near/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/constant_near/elk/sketch.exp.svg @@ -2,7 +2,7 @@ \ No newline at end of file diff --git a/e2etests/testdata/stable/constant_near_title/elk/board.exp.json b/e2etests/testdata/stable/constant_near_title/elk/board.exp.json new file mode 100644 index 000000000..cfd0c8a4f --- /dev/null +++ b/e2etests/testdata/stable/constant_near_title/elk/board.exp.json @@ -0,0 +1,459 @@ +{ + "name": "", + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "poll the people", + "type": "", + "pos": { + "x": 81, + "y": 12 + }, + "width": 210, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#F7F8FE", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "poll the people", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 110, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "results", + "type": "", + "pos": { + "x": 74, + "y": 238 + }, + "width": 153, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#F7F8FE", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "results", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 53, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "unfavorable", + "type": "", + "pos": { + "x": 232, + "y": 464 + }, + "width": 191, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#F7F8FE", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "unfavorable", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 91, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "favorable", + "type": "", + "pos": { + "x": 39, + "y": 464 + }, + "width": 173, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#F7F8FE", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "favorable", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 73, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "will of the people", + "type": "", + "pos": { + "x": 12, + "y": 690 + }, + "width": 227, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#F7F8FE", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "will of the people", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 127, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "title", + "type": "text", + "pos": { + "x": 84, + "y": -58 + }, + "width": 266, + "height": 50, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "#0A0F25", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "# A winning strategy", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "markdown", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 266, + "labelHeight": 50, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + } + ], + "connections": [ + { + "id": "(poll the people -> results)[0]", + "src": "poll the people", + "srcArrow": "none", + "srcLabel": "", + "dst": "results", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 151, + "y": 138 + }, + { + "x": 151, + "y": 238 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(results -> unfavorable)[0]", + "src": "results", + "srcArrow": "none", + "srcLabel": "", + "dst": "unfavorable", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 176.5, + "y": 364 + }, + { + "x": 176.5, + "y": 414 + }, + { + "x": 295.6666666666667, + "y": 414 + }, + { + "x": 295.6666666666667, + "y": 464 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(unfavorable -> poll the people)[0]", + "src": "unfavorable", + "srcArrow": "none", + "srcLabel": "", + "dst": "poll the people", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 359.3333333333333, + "y": 464 + }, + { + "x": 359.3333333333333, + "y": 188 + }, + { + "x": 221, + "y": 188 + }, + { + "x": 221, + "y": 138 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(results -> favorable)[0]", + "src": "results", + "srcArrow": "none", + "srcLabel": "", + "dst": "favorable", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 125.5, + "y": 364 + }, + { + "x": 125.5, + "y": 464 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(favorable -> will of the people)[0]", + "src": "favorable", + "srcArrow": "none", + "srcLabel": "", + "dst": "will of the people", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 125.5, + "y": 590 + }, + { + "x": 125.5, + "y": 690 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + } + ] +} diff --git a/e2etests/testdata/stable/constant_near_title/elk/sketch.exp.svg b/e2etests/testdata/stable/constant_near_title/elk/sketch.exp.svg new file mode 100644 index 000000000..e5ea8add8 --- /dev/null +++ b/e2etests/testdata/stable/constant_near_title/elk/sketch.exp.svg @@ -0,0 +1,796 @@ + +poll the peopleresultsunfavorablefavorablewill of the people

A winning strategy

+
+ + +
\ No newline at end of file From 36e221a6f5649bbf1e6b38324cec7c600ed963c5 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Sun, 25 Dec 2022 17:24:48 -0800 Subject: [PATCH 06/11] 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 425aab01c..fbd621f5d 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -18,6 +18,7 @@ Hope everyone is enjoying the holidays this week! - `class` shapes use monospaced font. [#521](https://github.com/terrastruct/d2/pull/521) - Sequence diagram edge group labels have more reasonable padding. [#512](https://github.com/terrastruct/d2/pull/512) - ELK layout engine preserves order of nodes. [#282](https://github.com/terrastruct/d2/issues/282) +- Markdown headings set font-family explicitly, so that external stylesheets with more specific targeting don't override it. [#525](https://github.com/terrastruct/d2/pull/525) #### Bugfixes โ›‘๏ธ From 409717b40d97c88525b6e9475afc98cb896c4a5d Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Sun, 25 Dec 2022 17:25:35 -0800 Subject: [PATCH 07/11] more specific doc --- ci/release/changelogs/next.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index fbd621f5d..2fe46e9ac 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -9,7 +9,7 @@ Hope everyone is enjoying the holidays this week! #### Features ๐Ÿš€ - `sketch` flag renders the diagram to look like it was sketched by hand. [#492](https://github.com/terrastruct/d2/pull/492) -- `near` now takes constants like `top-center`, particularly useful for diagram titles. See [docs](https://d2lang.com/tour/text#how-do-i-position-text) for more. [#525](https://github.com/terrastruct/d2/pull/525) +- `near` now takes constants like `top-center`, particularly useful for diagram titles. See [docs](http://localhost:8888/tour/text#near-a-constant) for more. [#525](https://github.com/terrastruct/d2/pull/525) #### Improvements ๐Ÿงน From a09f614f5f66a2d3bd4de3c1fd6a357ca22901a6 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Sun, 25 Dec 2022 17:30:48 -0800 Subject: [PATCH 08/11] update tests --- d2renderers/d2sketch/testdata/twitter/sketch.exp.svg | 1 + e2etests/testdata/regression/elk_order/dagre/sketch.exp.svg | 1 + e2etests/testdata/regression/elk_order/elk/sketch.exp.svg | 1 + e2etests/testdata/stable/chaos2/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/chaos2/elk/sketch.exp.svg | 1 + .../testdata/stable/giant_markdown_test/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/giant_markdown_test/elk/sketch.exp.svg | 1 + e2etests/testdata/stable/hr/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/hr/elk/sketch.exp.svg | 1 + e2etests/testdata/stable/latex/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/latex/elk/sketch.exp.svg | 1 + e2etests/testdata/stable/li1/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/li1/elk/sketch.exp.svg | 1 + e2etests/testdata/stable/li2/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/li2/elk/sketch.exp.svg | 1 + e2etests/testdata/stable/li3/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/li3/elk/sketch.exp.svg | 1 + e2etests/testdata/stable/li4/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/li4/elk/sketch.exp.svg | 1 + e2etests/testdata/stable/lone_h1/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/lone_h1/elk/sketch.exp.svg | 1 + e2etests/testdata/stable/markdown/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/markdown/elk/sketch.exp.svg | 1 + .../testdata/stable/markdown_stroke_fill/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/markdown_stroke_fill/elk/sketch.exp.svg | 1 + e2etests/testdata/stable/md_2space_newline/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/md_2space_newline/elk/sketch.exp.svg | 1 + .../testdata/stable/md_backslash_newline/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/md_backslash_newline/elk/sketch.exp.svg | 1 + .../testdata/stable/md_code_block_fenced/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/md_code_block_fenced/elk/sketch.exp.svg | 1 + .../testdata/stable/md_code_block_indented/dagre/sketch.exp.svg | 1 + .../testdata/stable/md_code_block_indented/elk/sketch.exp.svg | 1 + e2etests/testdata/stable/md_code_inline/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/md_code_inline/elk/sketch.exp.svg | 1 + e2etests/testdata/stable/p/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/p/elk/sketch.exp.svg | 1 + e2etests/testdata/stable/pre/dagre/sketch.exp.svg | 1 + e2etests/testdata/stable/pre/elk/sketch.exp.svg | 1 + 39 files changed, 39 insertions(+) diff --git a/d2renderers/d2sketch/testdata/twitter/sketch.exp.svg b/d2renderers/d2sketch/testdata/twitter/sketch.exp.svg index 471e8fa0a..f93b93c1f 100644 --- a/d2renderers/d2sketch/testdata/twitter/sketch.exp.svg +++ b/d2renderers/d2sketch/testdata/twitter/sketch.exp.svg @@ -282,6 +282,7 @@ width="3454" height="2449" viewBox="-100 -100 3454 2449">