diff --git a/d2exporter/export.go b/d2exporter/export.go index bf923c653..7a959ae07 100644 --- a/d2exporter/export.go +++ b/d2exporter/export.go @@ -167,6 +167,10 @@ func toShape(obj *d2graph.Object, theme *d2themes.Theme) d2target.Shape { case d2target.ShapeSQLTable: shape.SQLTable = *obj.SQLTable shape.FontSize -= d2target.HeaderFontAdd + case d2target.ShapeCloud: + if obj.ContentAspectRatio != nil { + shape.ContentAspectRatio = go2.Pointer(*obj.ContentAspectRatio) + } } shape.Label = text.Text shape.LabelWidth = text.Dimensions.Width diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go index 7fb32ad87..3632dea7c 100644 --- a/d2graph/d2graph.go +++ b/d2graph/d2graph.go @@ -107,6 +107,8 @@ type Object struct { LabelPosition *string `json:"labelPosition,omitempty"` IconPosition *string `json:"iconPosition,omitempty"` + ContentAspectRatio *float64 `json:"contentAspectRatio,omitempty"` + Class *d2target.Class `json:"class,omitempty"` SQLTable *d2target.SQLTable `json:"sql_table,omitempty"` @@ -1068,13 +1070,17 @@ func (obj *Object) SizeToContent(contentWidth, contentHeight, paddingX, paddingY obj.Width = sideLength obj.Height = sideLength } else if desiredHeight == 0 || desiredWidth == 0 { - switch s.GetType() { + switch shapeType { case shape.PERSON_TYPE: obj.Width, obj.Height = shape.LimitAR(obj.Width, obj.Height, shape.PERSON_AR_LIMIT) case shape.OVAL_TYPE: obj.Width, obj.Height = shape.LimitAR(obj.Width, obj.Height, shape.OVAL_AR_LIMIT) } } + if shapeType == shape.CLOUD_TYPE { + innerBox := s.GetInnerBoxForContent(contentWidth, contentHeight) + obj.ContentAspectRatio = go2.Pointer(innerBox.Width / innerBox.Height) + } } func (obj *Object) OuterNearContainer() *Object { diff --git a/d2graph/layout.go b/d2graph/layout.go index 473178983..7567dfbdc 100644 --- a/d2graph/layout.go +++ b/d2graph/layout.go @@ -364,7 +364,11 @@ func (obj *Object) ToShape() shape.Shape { 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) + s := shape.NewShape(shapeType, contentBox) + if shapeType == shape.CLOUD_TYPE && obj.ContentAspectRatio != nil { + s.SetInnerBoxAspectRatio(*obj.ContentAspectRatio) + } + return s } func (obj *Object) GetLabelTopLeft() *geo.Point { diff --git a/d2layouts/d2grid/layout.go b/d2layouts/d2grid/layout.go index 14df29c5d..d07ddb4de 100644 --- a/d2layouts/d2grid/layout.go +++ b/d2layouts/d2grid/layout.go @@ -124,7 +124,7 @@ func Layout(ctx context.Context, g *d2graph.Graph) error { innerTL := s.GetInsidePlacement(totalWidth, totalHeight, 0, 0) // depending on the shape innerBox may be larger than totalWidth, totalHeight // if this is the case, we want to center the cells within the larger innerBox - innerBox := s.GetInnerBoxForContent(totalWidth, totalHeight) + innerBox := s.GetInnerBox() var resizeDx, resizeDy float64 if innerBox.Width > totalWidth { resizeDx = (innerBox.Width - totalWidth) / 2 diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index 3f1ae7943..6ee011d88 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -932,6 +932,9 @@ func drawShape(writer, appendixWriter io.Writer, diagramHash string, targetShape shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[targetShape.Type] s := shape.NewShape(shapeType, geo.NewBox(tl, width, height)) + if shapeType == shape.CLOUD_TYPE && targetShape.ContentAspectRatio != nil { + s.SetInnerBoxAspectRatio(*targetShape.ContentAspectRatio) + } var shadowAttr string if targetShape.Shadow { diff --git a/d2target/d2target.go b/d2target/d2target.go index 3c468b81d..1efcfd08f 100644 --- a/d2target/d2target.go +++ b/d2target/d2target.go @@ -469,6 +469,8 @@ type Shape struct { Class SQLTable + ContentAspectRatio *float64 `json:"contentAspectRatio,omitempty"` + Text LabelPosition string `json:"labelPosition,omitempty"` diff --git a/lib/shape/shape.go b/lib/shape/shape.go index a7f5ad594..50ce3e324 100644 --- a/lib/shape/shape.go +++ b/lib/shape/shape.go @@ -45,7 +45,9 @@ type Shape interface { GetBox() *geo.Box GetInnerBox() *geo.Box + // cloud shape has different innerBoxes depending on content's aspect ratio GetInnerBoxForContent(width, height float64) *geo.Box + SetInnerBoxAspectRatio(aspectRatio float64) // placing a rectangle of the given size and padding inside the shape, return the position relative to the shape's TopLeft GetInsidePlacement(width, height, paddingX, paddingY float64) geo.Point @@ -91,7 +93,11 @@ func (s baseShape) GetInnerBox() *geo.Box { // only cloud shape needs this right now func (s baseShape) GetInnerBoxForContent(width, height float64) *geo.Box { - return (*s.FullShape).GetInnerBox() + return nil +} + +func (s baseShape) SetInnerBoxAspectRatio(aspectRatio float64) { + // only used for cloud } func (s baseShape) GetInsidePlacement(_, _, paddingX, paddingY float64) geo.Point { diff --git a/lib/shape/shape_cloud.go b/lib/shape/shape_cloud.go index 0f224bf8b..c4db16f72 100644 --- a/lib/shape/shape_cloud.go +++ b/lib/shape/shape_cloud.go @@ -30,6 +30,7 @@ const CLOUD_SQUARE_INNER_HEIGHT = 0.663 type shapeCloud struct { *baseShape + innerBoxAspectRatio *float64 } func NewCloud(box *geo.Box) Shape { @@ -38,13 +39,18 @@ func NewCloud(box *geo.Box) Shape { Type: CLOUD_TYPE, Box: box, }, + innerBoxAspectRatio: go2.Pointer(0.), } shape.FullShape = go2.Pointer(Shape(shape)) return shape } func (s shapeCloud) GetInnerBox() *geo.Box { - return s.GetInnerBoxForContent(s.Box.Width, s.Box.Height) + if s.innerBoxAspectRatio != nil && *s.innerBoxAspectRatio != 0. { + return s.GetInnerBoxForContent(*s.innerBoxAspectRatio, 1) + } else { + return s.GetInnerBoxForContent(s.Box.Width, s.Box.Height) + } } // we need this since the content's aspect ratio determines which placement is used @@ -66,6 +72,11 @@ func (s shapeCloud) GetInnerBoxForContent(width, height float64) *geo.Box { return geo.NewBox(&insideTL, width, height) } +func (s shapeCloud) SetInnerBoxAspectRatio(aspectRatio float64) { + // only used for cloud + *s.innerBoxAspectRatio = aspectRatio +} + func (s shapeCloud) GetDimensionsToFit(width, height, paddingX, paddingY float64) (float64, float64) { width += paddingX height += paddingY