From af32907a8ad54aac3daa10a052d0878d9731dd91 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Thu, 22 Jun 2023 13:24:24 -0700 Subject: [PATCH] user-specified label/icon positions --- d2graph/d2graph.go | 44 +++++++++++++++++++++++++++++++ d2layouts/d2dagrelayout/layout.go | 8 ++++-- d2layouts/d2elklayout/layout.go | 7 +++-- lib/label/label.go | 28 ++++++++++++++++++++ 4 files changed, 83 insertions(+), 4 deletions(-) diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go index b65270ffe..833456cee 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" ) @@ -1381,6 +1382,18 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler for _, obj := range g.Objects { obj.Box = &geo.Box{} + // user-specified label/icon positions + if obj.HasLabel() && obj.Attributes.LabelPosition != nil { + scalar := *obj.Attributes.LabelPosition + position := LabelPositionsMapping[scalar.Value] + obj.LabelPosition = go2.Pointer(string(position)) + } + if obj.Icon != nil && obj.Attributes.IconPosition != nil { + scalar := *obj.Attributes.IconPosition + position := LabelPositionsMapping[scalar.Value] + obj.IconPosition = go2.Pointer(string(position)) + } + var desiredWidth int var desiredHeight int if obj.WidthAttr != nil { @@ -1734,6 +1747,37 @@ var LabelPositionsArray = []string{ } var LabelPositions map[string]struct{} +// convert to label.Position +var LabelPositionsMapping = map[string]label.Position{ + "top-left": label.InsideTopLeft, + "top-center": label.InsideTopCenter, + "top-right": label.InsideTopRight, + + "center-left": label.InsideMiddleLeft, + "center-center": label.InsideMiddleCenter, + "center-right": label.InsideMiddleRight, + + "bottom-left": label.InsideBottomLeft, + "bottom-center": label.InsideBottomCenter, + "bottom-right": label.InsideBottomRight, + + "outside-top-left": label.OutsideTopLeft, + "outside-top-center": label.OutsideTopCenter, + "outside-top-right": label.OutsideTopRight, + + "outside-left-top": label.OutsideLeftTop, + "outside-left-center": label.OutsideLeftMiddle, + "outside-left-bottom": label.OutsideLeftBottom, + + "outside-right-top": label.OutsideRightTop, + "outside-right-center": label.OutsideRightMiddle, + "outside-right-bottom": label.OutsideRightBottom, + + "outside-bottom-left": label.OutsideBottomLeft, + "outside-bottom-center": label.OutsideBottomCenter, + "outside-bottom-right": label.OutsideBottomRight, +} + var FillPatterns = []string{ "dots", "lines", diff --git a/d2layouts/d2dagrelayout/layout.go b/d2layouts/d2dagrelayout/layout.go index da29d818a..ba9a92bf3 100644 --- a/d2layouts/d2dagrelayout/layout.go +++ b/d2layouts/d2dagrelayout/layout.go @@ -236,7 +236,8 @@ 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() && obj.LabelPosition == nil { + hasLabelPosition := obj.LabelPosition != nil + if obj.HasLabel() && !hasLabelPosition { if len(obj.ChildrenArray) > 0 { obj.LabelPosition = go2.Pointer(string(label.OutsideTopCenter)) } else if obj.HasOutsideBottomLabel() { @@ -252,7 +253,10 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err 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)) + if !hasLabelPosition { + // overwrite LabelPosition if we just set it above + obj.LabelPosition = go2.Pointer(string(label.OutsideTopRight)) + } } else { obj.IconPosition = go2.Pointer(string(label.InsideMiddleCenter)) } diff --git a/d2layouts/d2elklayout/layout.go b/d2layouts/d2elklayout/layout.go index e93085547..738bb8b1d 100644 --- a/d2layouts/d2elklayout/layout.go +++ b/d2layouts/d2elklayout/layout.go @@ -407,7 +407,8 @@ 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() && obj.LabelPosition == nil { + hasLabelPosition := obj.LabelPosition != nil + if obj.HasLabel() && !hasLabelPosition { if len(obj.ChildrenArray) > 0 { obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) } else if obj.HasOutsideBottomLabel() { @@ -422,7 +423,9 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err 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)) + if !hasLabelPosition { + obj.LabelPosition = go2.Pointer(string(label.InsideTopRight)) + } } else { obj.IconPosition = go2.Pointer(string(label.InsideMiddleCenter)) } diff --git a/lib/label/label.go b/lib/label/label.go index 030a57a75..a85619fb7 100644 --- a/lib/label/label.go +++ b/lib/label/label.go @@ -50,6 +50,34 @@ const ( UnlockedBottom Position = "UNLOCKED_BOTTOM" ) +func (position Position) IsShapePosition() bool { + switch position { + case OutsideTopLeft, OutsideTopCenter, OutsideTopRight, + OutsideBottomLeft, OutsideBottomCenter, OutsideBottomRight, + OutsideLeftTop, OutsideLeftMiddle, OutsideLeftBottom, + OutsideRightTop, OutsideRightMiddle, OutsideRightBottom, + + InsideTopLeft, InsideTopCenter, InsideTopRight, + InsideMiddleLeft, InsideMiddleCenter, InsideMiddleRight, + InsideBottomLeft, InsideBottomCenter, InsideBottomRight: + return true + default: + return false + } +} + +func (position Position) IsEdgePosition() bool { + switch position { + case OutsideTopLeft, OutsideTopCenter, OutsideTopRight, + InsideMiddleLeft, InsideMiddleCenter, InsideMiddleRight, + OutsideBottomLeft, OutsideBottomCenter, OutsideBottomRight, + UnlockedTop, UnlockedMiddle, UnlockedBottom: + return true + default: + return false + } +} + func (position Position) IsOutside() bool { switch position { case OutsideTopLeft, OutsideTopCenter, OutsideTopRight,