refactor SetDimensions
This commit is contained in:
parent
2f4446ee18
commit
8384d96763
1 changed files with 206 additions and 177 deletions
|
|
@ -3,6 +3,7 @@ package d2graph
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -21,6 +22,7 @@ import (
|
|||
)
|
||||
|
||||
const INNER_LABEL_PADDING int = 5
|
||||
const DEFAULT_SHAPE_PADDING = 100.
|
||||
|
||||
// TODO: Refactor with a light abstract layer on top of AST implementing scenarios,
|
||||
// variables, imports, substitutions and then a final set of structures representing
|
||||
|
|
@ -668,6 +670,164 @@ func (obj *Object) AppendReferences(ida []string, ref Reference, unresolvedObj *
|
|||
}
|
||||
}
|
||||
|
||||
func (obj *Object) GetLabelSize(mtexts []*d2target.MText, ruler *textmeasure.Ruler, fontFamily *d2fonts.FontFamily) (*d2target.TextDimensions, error) {
|
||||
shapeType := strings.ToLower(obj.Attributes.Shape.Value)
|
||||
|
||||
var dims *d2target.TextDimensions
|
||||
switch shapeType {
|
||||
case d2target.ShapeText:
|
||||
if obj.Attributes.Language == "latex" {
|
||||
width, height, err := d2latex.Measure(obj.Text().Text)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dims = d2target.NewTextDimensions(width, height)
|
||||
} else if obj.Attributes.Language != "" {
|
||||
var err error
|
||||
dims, err = getMarkdownDimensions(mtexts, ruler, obj.Text(), fontFamily)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
dims = GetTextDimensions(mtexts, ruler, obj.Text(), fontFamily)
|
||||
}
|
||||
|
||||
case d2target.ShapeClass:
|
||||
dims = GetTextDimensions(mtexts, ruler, obj.Text(), go2.Pointer(d2fonts.SourceCodePro))
|
||||
|
||||
default:
|
||||
dims = GetTextDimensions(mtexts, ruler, obj.Text(), fontFamily)
|
||||
}
|
||||
|
||||
if shapeType == d2target.ShapeSQLTable && obj.Attributes.Label.Value == "" {
|
||||
// measure with placeholder text to determine height
|
||||
placeholder := *obj.Text()
|
||||
placeholder.Text = "Table"
|
||||
dims = GetTextDimensions(mtexts, ruler, &placeholder, fontFamily)
|
||||
}
|
||||
|
||||
if dims == nil {
|
||||
if shapeType == d2target.ShapeImage {
|
||||
dims = d2target.NewTextDimensions(0, 0)
|
||||
} else {
|
||||
return nil, fmt.Errorf("dimensions for object label %#v not found", obj.Text())
|
||||
}
|
||||
}
|
||||
|
||||
return dims, nil
|
||||
}
|
||||
|
||||
func (obj *Object) GetDefaultSize(mtexts []*d2target.MText, ruler *textmeasure.Ruler, fontFamily *d2fonts.FontFamily, labelDims d2target.TextDimensions) (*d2target.TextDimensions, error) {
|
||||
dims := d2target.TextDimensions{}
|
||||
|
||||
shapeType := strings.ToLower(obj.Attributes.Shape.Value)
|
||||
|
||||
switch shapeType {
|
||||
default:
|
||||
return d2target.NewTextDimensions(labelDims.Width, labelDims.Height), nil
|
||||
|
||||
case d2target.ShapeImage:
|
||||
return d2target.NewTextDimensions(128, 128), nil
|
||||
|
||||
case d2target.ShapeClass:
|
||||
maxWidth := labelDims.Width
|
||||
|
||||
for _, f := range obj.Class.Fields {
|
||||
fdims := GetTextDimensions(mtexts, ruler, f.Text(), go2.Pointer(d2fonts.SourceCodePro))
|
||||
if fdims == nil {
|
||||
return nil, fmt.Errorf("dimensions for class field %#v not found", f.Text())
|
||||
}
|
||||
lineWidth := fdims.Width
|
||||
if maxWidth < lineWidth {
|
||||
maxWidth = lineWidth
|
||||
}
|
||||
}
|
||||
for _, m := range obj.Class.Methods {
|
||||
mdims := GetTextDimensions(mtexts, ruler, m.Text(), go2.Pointer(d2fonts.SourceCodePro))
|
||||
if mdims == nil {
|
||||
return nil, fmt.Errorf("dimensions for class method %#v not found", m.Text())
|
||||
}
|
||||
lineWidth := mdims.Width
|
||||
if maxWidth < lineWidth {
|
||||
maxWidth = lineWidth
|
||||
}
|
||||
}
|
||||
dims.Width = maxWidth
|
||||
|
||||
// All rows should be the same height
|
||||
var anyRowText *d2target.MText
|
||||
if len(obj.Class.Fields) > 0 {
|
||||
anyRowText = obj.Class.Fields[0].Text()
|
||||
} else if len(obj.Class.Methods) > 0 {
|
||||
anyRowText = obj.Class.Methods[0].Text()
|
||||
}
|
||||
if anyRowText != nil {
|
||||
// 10px of padding top and bottom so text doesn't look squished
|
||||
rowHeight := GetTextDimensions(mtexts, ruler, anyRowText, go2.Pointer(d2fonts.SourceCodePro)).Height + 20
|
||||
dims.Height = rowHeight * (len(obj.Class.Fields) + len(obj.Class.Methods) + 2)
|
||||
} else {
|
||||
dims.Height = labelDims.Height
|
||||
}
|
||||
|
||||
case d2target.ShapeSQLTable:
|
||||
maxNameWidth := 0
|
||||
maxTypeWidth := 0
|
||||
constraintWidth := 0
|
||||
|
||||
for i := range obj.SQLTable.Columns {
|
||||
// Note: we want to set dimensions of actual column not the for loop copy of the struct
|
||||
c := &obj.SQLTable.Columns[i]
|
||||
ctexts := c.Texts()
|
||||
|
||||
nameDims := GetTextDimensions(mtexts, ruler, ctexts[0], fontFamily)
|
||||
if nameDims == nil {
|
||||
return nil, fmt.Errorf("dimensions for sql_table name %#v not found", ctexts[0].Text)
|
||||
}
|
||||
c.Name.LabelWidth = nameDims.Width
|
||||
c.Name.LabelHeight = nameDims.Height
|
||||
if maxNameWidth < nameDims.Width {
|
||||
maxNameWidth = nameDims.Width
|
||||
}
|
||||
|
||||
typeDims := GetTextDimensions(mtexts, ruler, ctexts[1], fontFamily)
|
||||
if typeDims == nil {
|
||||
return nil, fmt.Errorf("dimensions for sql_table type %#v not found", ctexts[1].Text)
|
||||
}
|
||||
c.Type.LabelWidth = typeDims.Width
|
||||
c.Type.LabelHeight = typeDims.Height
|
||||
if maxTypeWidth < typeDims.Width {
|
||||
maxTypeWidth = typeDims.Width
|
||||
}
|
||||
|
||||
if c.Constraint != "" {
|
||||
// covers UNQ constraint with padding
|
||||
constraintWidth = 60
|
||||
}
|
||||
}
|
||||
|
||||
// The rows get padded a little due to header font being larger than row font
|
||||
dims.Height = labelDims.Height * (len(obj.SQLTable.Columns) + 1)
|
||||
dims.Width = d2target.NamePadding + maxNameWidth + d2target.TypePadding + maxTypeWidth + d2target.TypePadding + constraintWidth
|
||||
}
|
||||
return &dims, nil
|
||||
}
|
||||
|
||||
func (obj *Object) GetPadding() (x, y float64) {
|
||||
|
||||
switch strings.ToLower(obj.Attributes.Shape.Value) {
|
||||
case d2target.ShapeImage,
|
||||
d2target.ShapeSQLTable,
|
||||
d2target.ShapeText,
|
||||
d2target.ShapeCode:
|
||||
return 0., 0.
|
||||
case d2target.ShapeClass:
|
||||
// TODO fix class row width measurements (see SQL table)
|
||||
return 100., 0.
|
||||
default:
|
||||
return DEFAULT_SHAPE_PADDING, DEFAULT_SHAPE_PADDING
|
||||
}
|
||||
}
|
||||
|
||||
type Edge struct {
|
||||
Index int `json:"index"`
|
||||
|
||||
|
|
@ -909,62 +1069,24 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
|
|||
}
|
||||
shapeType := strings.ToLower(obj.Attributes.Shape.Value)
|
||||
|
||||
switch shapeType {
|
||||
case d2target.ShapeClass,
|
||||
d2target.ShapeSQLTable,
|
||||
d2target.ShapeCode,
|
||||
d2target.ShapeImage,
|
||||
d2target.ShapeText:
|
||||
// edge cases for unnamed class, etc
|
||||
default:
|
||||
if obj.Attributes.Label.Value == "" && desiredWidth == 0 && desiredHeight == 0 {
|
||||
obj.Width = 100
|
||||
obj.Height = 100
|
||||
continue
|
||||
}
|
||||
}
|
||||
// switch shapeType {
|
||||
// case d2target.ShapeClass,
|
||||
// d2target.ShapeSQLTable,
|
||||
// d2target.ShapeCode,
|
||||
// d2target.ShapeImage,
|
||||
// d2target.ShapeText:
|
||||
// // edge cases for unnamed class, etc
|
||||
// default:
|
||||
// if obj.Attributes.Label.Value == "" && desiredWidth == 0 && desiredHeight == 0 {
|
||||
// obj.Width = 100
|
||||
// obj.Height = 100
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
|
||||
var dims *d2target.TextDimensions
|
||||
var innerLabelPadding = INNER_LABEL_PADDING
|
||||
switch shapeType {
|
||||
case d2target.ShapeText:
|
||||
if obj.Attributes.Language == "latex" {
|
||||
width, height, err := d2latex.Measure(obj.Text().Text)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dims = d2target.NewTextDimensions(width, height)
|
||||
} else if obj.Attributes.Language != "" {
|
||||
var err error
|
||||
dims, err = getMarkdownDimensions(mtexts, ruler, obj.Text(), fontFamily)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
dims = GetTextDimensions(mtexts, ruler, obj.Text(), fontFamily)
|
||||
}
|
||||
innerLabelPadding = 0
|
||||
|
||||
case d2target.ShapeClass:
|
||||
dims = GetTextDimensions(mtexts, ruler, obj.Text(), go2.Pointer(d2fonts.SourceCodePro))
|
||||
|
||||
default:
|
||||
dims = GetTextDimensions(mtexts, ruler, obj.Text(), fontFamily)
|
||||
}
|
||||
|
||||
if shapeType == d2target.ShapeSQLTable && obj.Attributes.Label.Value == "" {
|
||||
// measure with placeholder text to determine height
|
||||
placeholder := *obj.Text()
|
||||
placeholder.Text = "Table"
|
||||
dims = GetTextDimensions(mtexts, ruler, &placeholder, fontFamily)
|
||||
}
|
||||
|
||||
if dims == nil {
|
||||
if shapeType == d2target.ShapeImage {
|
||||
dims = d2target.NewTextDimensions(0, 0)
|
||||
} else {
|
||||
return fmt.Errorf("dimensions for object label %#v not found", obj.Text())
|
||||
}
|
||||
labelDims, err := obj.GetLabelSize(mtexts, ruler, fontFamily)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch shapeType {
|
||||
|
|
@ -972,139 +1094,46 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
|
|||
// no labels
|
||||
default:
|
||||
if obj.Attributes.Label.Value != "" {
|
||||
obj.LabelWidth = go2.Pointer(dims.Width)
|
||||
obj.LabelHeight = go2.Pointer(dims.Height)
|
||||
obj.LabelWidth = go2.Pointer(labelDims.Width)
|
||||
obj.LabelHeight = go2.Pointer(labelDims.Height)
|
||||
}
|
||||
}
|
||||
|
||||
dims.Width += innerLabelPadding
|
||||
dims.Height += innerLabelPadding
|
||||
obj.LabelDimensions = *dims
|
||||
// the desired dimensions must be at least as large as the text
|
||||
obj.Width = float64(go2.Max(dims.Width, desiredWidth))
|
||||
obj.Height = float64(go2.Max(dims.Height, desiredHeight))
|
||||
if shapeType != d2target.ShapeText && obj.Attributes.Label.Value != "" {
|
||||
labelDims.Width += INNER_LABEL_PADDING
|
||||
labelDims.Height += INNER_LABEL_PADDING
|
||||
}
|
||||
obj.LabelDimensions = *labelDims
|
||||
|
||||
defaultDims, err := obj.GetDefaultSize(mtexts, ruler, fontFamily, *labelDims)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj.Width = float64(go2.Max(defaultDims.Width, desiredWidth))
|
||||
obj.Height = float64(go2.Max(defaultDims.Height, desiredHeight))
|
||||
|
||||
paddingX, paddingY := obj.GetPadding()
|
||||
|
||||
switch shapeType {
|
||||
case d2target.ShapeSquare, d2target.ShapeCircle:
|
||||
if desiredWidth != 0 || desiredHeight != 0 {
|
||||
paddingX = 0.
|
||||
paddingY = 0.
|
||||
}
|
||||
|
||||
sideLength := math.Max(obj.Width+paddingX, obj.Height+paddingY)
|
||||
obj.Width = sideLength
|
||||
obj.Height = sideLength
|
||||
|
||||
default:
|
||||
if desiredWidth == 0 {
|
||||
obj.Width += 100
|
||||
obj.Width += float64(paddingX)
|
||||
}
|
||||
if desiredHeight == 0 {
|
||||
obj.Height += 100
|
||||
}
|
||||
|
||||
case d2target.ShapeImage:
|
||||
if desiredWidth == 0 {
|
||||
obj.Width = 128
|
||||
}
|
||||
if desiredHeight == 0 {
|
||||
obj.Height = 128
|
||||
}
|
||||
|
||||
case d2target.ShapeSquare, d2target.ShapeCircle:
|
||||
sideLength := go2.Max(obj.Width, obj.Height)
|
||||
padding := 0.
|
||||
if desiredWidth == 0 && desiredHeight == 0 {
|
||||
padding = 100.
|
||||
}
|
||||
obj.Width = sideLength + padding
|
||||
obj.Height = sideLength + padding
|
||||
|
||||
case d2target.ShapeClass:
|
||||
maxWidth := dims.Width
|
||||
|
||||
for _, f := range obj.Class.Fields {
|
||||
fdims := GetTextDimensions(mtexts, ruler, f.Text(), go2.Pointer(d2fonts.SourceCodePro))
|
||||
if fdims == nil {
|
||||
return fmt.Errorf("dimensions for class field %#v not found", f.Text())
|
||||
}
|
||||
lineWidth := fdims.Width
|
||||
if maxWidth < lineWidth {
|
||||
maxWidth = lineWidth
|
||||
}
|
||||
}
|
||||
for _, m := range obj.Class.Methods {
|
||||
mdims := GetTextDimensions(mtexts, ruler, m.Text(), go2.Pointer(d2fonts.SourceCodePro))
|
||||
if mdims == nil {
|
||||
return fmt.Errorf("dimensions for class method %#v not found", m.Text())
|
||||
}
|
||||
lineWidth := mdims.Width
|
||||
if maxWidth < lineWidth {
|
||||
maxWidth = lineWidth
|
||||
}
|
||||
}
|
||||
|
||||
// All rows should be the same height
|
||||
var anyRowText *d2target.MText
|
||||
if len(obj.Class.Fields) > 0 {
|
||||
anyRowText = obj.Class.Fields[0].Text()
|
||||
} else if len(obj.Class.Methods) > 0 {
|
||||
anyRowText = obj.Class.Methods[0].Text()
|
||||
}
|
||||
if anyRowText != nil {
|
||||
// 10px of padding top and bottom so text doesn't look squished
|
||||
rowHeight := GetTextDimensions(mtexts, ruler, anyRowText, go2.Pointer(d2fonts.SourceCodePro)).Height + 20
|
||||
obj.Height = float64(rowHeight * (len(obj.Class.Fields) + len(obj.Class.Methods) + 2))
|
||||
}
|
||||
// Leave room for padding
|
||||
obj.Width = float64(maxWidth + 100)
|
||||
|
||||
case d2target.ShapeSQLTable:
|
||||
maxNameWidth := 0
|
||||
maxTypeWidth := 0
|
||||
constraintWidth := 0
|
||||
|
||||
for i := range obj.SQLTable.Columns {
|
||||
// Note: we want to set dimensions of actual column not the for loop copy of the struct
|
||||
c := &obj.SQLTable.Columns[i]
|
||||
ctexts := c.Texts()
|
||||
|
||||
nameDims := GetTextDimensions(mtexts, ruler, ctexts[0], fontFamily)
|
||||
if nameDims == nil {
|
||||
return fmt.Errorf("dimensions for sql_table name %#v not found", ctexts[0].Text)
|
||||
}
|
||||
c.Name.LabelWidth = nameDims.Width
|
||||
c.Name.LabelHeight = nameDims.Height
|
||||
if maxNameWidth < nameDims.Width {
|
||||
maxNameWidth = nameDims.Width
|
||||
}
|
||||
|
||||
typeDims := GetTextDimensions(mtexts, ruler, ctexts[1], fontFamily)
|
||||
if typeDims == nil {
|
||||
return fmt.Errorf("dimensions for sql_table type %#v not found", ctexts[1].Text)
|
||||
}
|
||||
c.Type.LabelWidth = typeDims.Width
|
||||
c.Type.LabelHeight = typeDims.Height
|
||||
if maxTypeWidth < typeDims.Width {
|
||||
maxTypeWidth = typeDims.Width
|
||||
}
|
||||
|
||||
if c.Constraint != "" {
|
||||
// covers UNQ constraint with padding
|
||||
constraintWidth = 60
|
||||
}
|
||||
}
|
||||
|
||||
// The rows get padded a little due to header font being larger than row font
|
||||
obj.Height = float64(dims.Height * (len(obj.SQLTable.Columns) + 1))
|
||||
obj.Width = float64(d2target.NamePadding + maxNameWidth + d2target.TypePadding + maxTypeWidth + d2target.TypePadding + constraintWidth)
|
||||
|
||||
case d2target.ShapeText, d2target.ShapeCode:
|
||||
}
|
||||
|
||||
switch shapeType {
|
||||
case d2target.ShapeClass,
|
||||
d2target.ShapeSQLTable,
|
||||
d2target.ShapeCode,
|
||||
d2target.ShapeText:
|
||||
if float64(desiredWidth) > obj.Width {
|
||||
obj.Width = float64(desiredWidth)
|
||||
}
|
||||
if float64(desiredHeight) > obj.Height {
|
||||
obj.Height = float64(desiredHeight)
|
||||
obj.Height += float64(paddingY)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
for _, edge := range g.Edges {
|
||||
endpointLabels := []string{}
|
||||
|
|
|
|||
Loading…
Reference in a new issue