Skip to content

Commit

Permalink
Merge pull request #1207 from gavin-ts/arrowhead-label-dimensions
Browse files Browse the repository at this point in the history
Update arrowhead label positioning and use label dimensions
  • Loading branch information
gavin-ts authored Apr 17, 2023
2 parents ee93929 + 8588aa3 commit eae415a
Show file tree
Hide file tree
Showing 762 changed files with 48,675 additions and 44,405 deletions.
1 change: 1 addition & 0 deletions ci/release/changelogs/next.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

- Markdown text can now be adjusted with `font-size` [#1191](https://github.com/terrastruct/d2/issues/1191)
- SVG outputs for internal links use relative paths instead of absolute [#1197](https://github.com/terrastruct/d2/pull/1197)
- Improves arrowhead label positioning [#1207](https://github.com/terrastruct/d2/pull/1207)

#### Bugfixes ⛑️

Expand Down
12 changes: 10 additions & 2 deletions d2exporter/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,11 @@ func toConnection(edge *d2graph.Edge, theme *d2themes.Theme) d2target.Connection
}
if edge.SrcArrowhead != nil {
if edge.SrcArrowhead.Label.Value != "" {
connection.SrcLabel = edge.SrcArrowhead.Label.Value
connection.SrcLabel = &d2target.Text{
Label: edge.SrcArrowhead.Label.Value,
LabelWidth: edge.SrcArrowhead.LabelDimensions.Width,
LabelHeight: edge.SrcArrowhead.LabelDimensions.Height,
}
}
}
if edge.DstArrow {
Expand All @@ -230,7 +234,11 @@ func toConnection(edge *d2graph.Edge, theme *d2themes.Theme) d2target.Connection
}
if edge.DstArrowhead != nil {
if edge.DstArrowhead.Label.Value != "" {
connection.DstLabel = edge.DstArrowhead.Label.Value
connection.DstLabel = &d2target.Text{
Label: edge.DstArrowhead.Label.Value,
LabelWidth: edge.DstArrowhead.LabelDimensions.Width,
LabelHeight: edge.DstArrowhead.LabelDimensions.Height,
}
}
}
if theme != nil && theme.SpecialRules.NoCornerRadius {
Expand Down
35 changes: 13 additions & 22 deletions d2graph/d2graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -1042,9 +1042,6 @@ func (obj *Object) OuterNearContainer() *Object {
type Edge struct {
Index int `json:"index"`

MinWidth int `json:"minWidth"`
MinHeight int `json:"minHeight"`

SrcTableColumnIndex *int `json:"srcTableColumnIndex,omitempty"`
DstTableColumnIndex *int `json:"dstTableColumnIndex,omitempty"`

Expand Down Expand Up @@ -1471,21 +1468,23 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
}
}
for _, edge := range g.Edges {
endpointLabels := []string{}
usedFont := fontFamily
if edge.Style.Font != nil {
f := d2fonts.D2_FONT_TO_FAMILY[edge.Style.Font.Value]
usedFont = &f
}

if edge.SrcArrowhead != nil && edge.SrcArrowhead.Label.Value != "" {
endpointLabels = append(endpointLabels, edge.SrcArrowhead.Label.Value)
t := edge.Text()
t.Text = edge.SrcArrowhead.Label.Value
dims := GetTextDimensions(mtexts, ruler, t, usedFont)
edge.SrcArrowhead.LabelDimensions = *dims
}
if edge.DstArrowhead != nil && edge.DstArrowhead.Label.Value != "" {
endpointLabels = append(endpointLabels, edge.DstArrowhead.Label.Value)
}

for _, label := range endpointLabels {
t := edge.Text()
t.Text = label
dims := GetTextDimensions(mtexts, ruler, t, fontFamily)
edge.MinWidth += dims.Width
// Some padding as it's not totally near the end
edge.MinHeight += dims.Height + 5
t.Text = edge.DstArrowhead.Label.Value
dims := GetTextDimensions(mtexts, ruler, t, usedFont)
edge.DstArrowhead.LabelDimensions = *dims
}

if edge.Label.Value == "" {
Expand All @@ -1497,20 +1496,12 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
}
edge.ApplyTextTransform()

usedFont := fontFamily
if edge.Style.Font != nil {
f := d2fonts.D2_FONT_TO_FAMILY[edge.Style.Font.Value]
usedFont = &f
}

dims := GetTextDimensions(mtexts, ruler, edge.Text(), usedFont)
if dims == nil {
return fmt.Errorf("dimensions for edge label %#v not found", edge.Text())
}

edge.LabelDimensions = *dims
edge.MinWidth += dims.Width
edge.MinHeight += dims.Height
}
return nil
}
Expand Down
16 changes: 0 additions & 16 deletions d2graph/serde.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,22 +388,6 @@ func CompareSerializedEdge(edge, other *Edge) error {
)
}

if edge.MinWidth != other.MinWidth {
return fmt.Errorf(
"min width differs: edge=%d, other=%d",
edge.MinWidth,
other.MinWidth,
)
}

if edge.MinHeight != other.MinHeight {
return fmt.Errorf(
"min height differs: edge=%d, other=%d",
edge.MinHeight,
other.MinHeight,
)
}

if edge.Label.Value != other.Label.Value {
return fmt.Errorf(
"labels differ: edge=%s, other=%s",
Expand Down
8 changes: 8 additions & 0 deletions d2renderers/d2sketch/sketch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,14 @@ queue -> package -> step
callout -> stored_data -> person
diamond -> oval -> circle
hexagon -> cloud
`,
},
{
name: "long_arrowhead_label",
script: `
a -> b: {
target-arrowhead: "a to b with unexpectedly long target arrowhead label"
}
`,
},
}
Expand Down
154 changes: 77 additions & 77 deletions d2renderers/d2sketch/testdata/all_shapes/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 77 additions & 77 deletions d2renderers/d2sketch/testdata/all_shapes_dark/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 80 additions & 80 deletions d2renderers/d2sketch/testdata/animated/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 80 additions & 80 deletions d2renderers/d2sketch/testdata/animated_dark/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 80 additions & 80 deletions d2renderers/d2sketch/testdata/arrowheads/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 80 additions & 80 deletions d2renderers/d2sketch/testdata/arrowheads_dark/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 77 additions & 77 deletions d2renderers/d2sketch/testdata/basic/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 77 additions & 77 deletions d2renderers/d2sketch/testdata/basic_dark/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 80 additions & 80 deletions d2renderers/d2sketch/testdata/child_to_child/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 80 additions & 80 deletions d2renderers/d2sketch/testdata/child_to_child_dark/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 80 additions & 80 deletions d2renderers/d2sketch/testdata/connection_label/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 80 additions & 80 deletions d2renderers/d2sketch/testdata/connection_label_dark/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 77 additions & 77 deletions d2renderers/d2sketch/testdata/crows_feet/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 77 additions & 77 deletions d2renderers/d2sketch/testdata/crows_feet_dark/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 77 additions & 77 deletions d2renderers/d2sketch/testdata/dots-all/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 77 additions & 77 deletions d2renderers/d2sketch/testdata/dots-multiple/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 80 additions & 80 deletions d2renderers/d2sketch/testdata/dots-real/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 77 additions & 77 deletions d2renderers/d2sketch/testdata/elk_corners/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
110 changes: 110 additions & 0 deletions d2renderers/d2sketch/testdata/long_arrowhead_label/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
568 changes: 284 additions & 284 deletions d2renderers/d2sketch/testdata/opacity/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
568 changes: 284 additions & 284 deletions d2renderers/d2sketch/testdata/opacity_dark/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 80 additions & 80 deletions d2renderers/d2sketch/testdata/paper-real/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
562 changes: 281 additions & 281 deletions d2renderers/d2sketch/testdata/root-fill/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 77 additions & 77 deletions d2renderers/d2sketch/testdata/sql_tables/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 77 additions & 77 deletions d2renderers/d2sketch/testdata/sql_tables_dark/sketch.exp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 80 additions & 80 deletions d2renderers/d2sketch/testdata/terminal/sketch.exp.svg

Large diffs are not rendered by default.

568 changes: 284 additions & 284 deletions d2renderers/d2sketch/testdata/twitter/sketch.exp.svg

Large diffs are not rendered by default.

568 changes: 284 additions & 284 deletions d2renderers/d2sketch/testdata/twitter_dark/sketch.exp.svg

Large diffs are not rendered by default.

Large diffs are not rendered by default.

152 changes: 76 additions & 76 deletions d2renderers/d2svg/appendix/testdata/links/sketch.exp.svg

Large diffs are not rendered by default.

152 changes: 76 additions & 76 deletions d2renderers/d2svg/appendix/testdata/links_dark/sketch.exp.svg

Large diffs are not rendered by default.

152 changes: 76 additions & 76 deletions d2renderers/d2svg/appendix/testdata/tooltip_fill/sketch.exp.svg

Large diffs are not rendered by default.

Large diffs are not rendered by default.

98 changes: 27 additions & 71 deletions d2renderers/d2svg/d2svg.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import (
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"

"oss.terrastruct.com/util-go/go2"

"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2renderers/d2fonts"
"oss.terrastruct.com/d2/d2renderers/d2latex"
Expand All @@ -39,8 +37,7 @@ import (
)

const (
DEFAULT_PADDING = 100
MIN_ARROWHEAD_STROKE_WIDTH = 2
DEFAULT_PADDING = 100

appendixIconRadius = 16
)
Expand Down Expand Up @@ -109,56 +106,13 @@ func arrowheadMarkerID(isTarget bool, connection d2target.Connection) string {
)))
}

func arrowheadDimensions(arrowhead d2target.Arrowhead, strokeWidth float64) (width, height float64) {
var baseWidth, baseHeight float64
var widthMultiplier, heightMultiplier float64
switch arrowhead {
case d2target.ArrowArrowhead:
baseWidth = 4
baseHeight = 4
widthMultiplier = 4
heightMultiplier = 4
case d2target.TriangleArrowhead:
baseWidth = 4
baseHeight = 4
widthMultiplier = 3
heightMultiplier = 4
case d2target.LineArrowhead:
widthMultiplier = 5
heightMultiplier = 8
case d2target.FilledDiamondArrowhead:
baseWidth = 11
baseHeight = 7
widthMultiplier = 5.5
heightMultiplier = 3.5
case d2target.DiamondArrowhead:
baseWidth = 11
baseHeight = 9
widthMultiplier = 5.5
heightMultiplier = 4.5
case d2target.FilledCircleArrowhead, d2target.CircleArrowhead:
baseWidth = 8
baseHeight = 8
widthMultiplier = 5
heightMultiplier = 5
case d2target.CfOne, d2target.CfMany, d2target.CfOneRequired, d2target.CfManyRequired:
baseWidth = 9
baseHeight = 9
widthMultiplier = 4.5
heightMultiplier = 4.5
}

clippedStrokeWidth := go2.Max(MIN_ARROWHEAD_STROKE_WIDTH, strokeWidth)
return baseWidth + clippedStrokeWidth*widthMultiplier, baseHeight + clippedStrokeWidth*heightMultiplier
}

func arrowheadMarker(isTarget bool, id string, connection d2target.Connection) string {
arrowhead := connection.DstArrow
if !isTarget {
arrowhead = connection.SrcArrow
}
strokeWidth := float64(connection.StrokeWidth)
width, height := arrowheadDimensions(arrowhead, strokeWidth)
width, height := arrowhead.Dimensions(strokeWidth)

var path string
switch arrowhead {
Expand Down Expand Up @@ -620,38 +574,40 @@ func drawConnection(writer io.Writer, labelMaskID string, connection d2target.Co
fmt.Fprint(writer, textEl.Render())
}

length := geo.Route(connection.Route).Length()
if connection.SrcLabel != "" {
// TODO use arrowhead label dimensions https://github.com/terrastruct/d2/issues/183
size := float64(connection.FontSize)
position := 0.
if length > 0 {
position = size / length
}
fmt.Fprint(writer, renderArrowheadLabel(connection, connection.SrcLabel, position, size, size))
}
if connection.DstLabel != "" {
// TODO use arrowhead label dimensions https://github.com/terrastruct/d2/issues/183
size := float64(connection.FontSize)
position := 1.
if length > 0 {
position -= size / length
}
fmt.Fprint(writer, renderArrowheadLabel(connection, connection.DstLabel, position, size, size))
if connection.SrcLabel != nil && connection.SrcLabel.Label != "" {
fmt.Fprint(writer, renderArrowheadLabel(connection, connection.SrcLabel.Label, false))
}
if connection.DstLabel != nil && connection.DstLabel.Label != "" {
fmt.Fprint(writer, renderArrowheadLabel(connection, connection.DstLabel.Label, true))
}
fmt.Fprintf(writer, `</g>`)
return
}

func renderArrowheadLabel(connection d2target.Connection, text string, position, width, height float64) string {
labelTL := label.UnlockedTop.GetPointOnRoute(connection.Route, float64(connection.StrokeWidth), position, width, height)
func renderArrowheadLabel(connection d2target.Connection, text string, isDst bool) string {
var width, height float64
if isDst {
width = float64(connection.DstLabel.LabelWidth)
height = float64(connection.DstLabel.LabelHeight)
} else {
width = float64(connection.SrcLabel.LabelWidth)
height = float64(connection.SrcLabel.LabelHeight)
}

labelTL := connection.GetArrowheadLabelPosition(isDst)

// svg text is positioned with the center of its baseline
baselineCenter := geo.Point{
X: labelTL.X + width/2.,
Y: labelTL.Y + float64(connection.FontSize),
}

textEl := d2themes.NewThemableElement("text")
textEl.X = labelTL.X + width/2
textEl.Y = labelTL.Y + float64(connection.FontSize)
textEl.X = baselineCenter.X
textEl.Y = baselineCenter.Y
textEl.Fill = d2target.FG_COLOR
textEl.ClassName = "text-italic"
textEl.Style = fmt.Sprintf("text-anchor:%s;font-size:%vpx", "middle", connection.FontSize)
textEl.Style = fmt.Sprintf("text-anchor:middle;font-size:%vpx", connection.FontSize)
textEl.Content = RenderText(text, textEl.X, height)
return textEl.Render()
}
Expand Down
Loading

0 comments on commit eae415a

Please sign in to comment.