diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index 37cfd7529..4409e6045 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -207,9 +207,9 @@ func arrowheadMarker(isTarget bool, id string, connection d2target.Connection) s width *= 1.1 default: if isTarget { - refX = width - 3/2*strokeWidth + refX = width - 1.5*strokeWidth } else { - refX = 3 / 2 * strokeWidth + refX = 1.5 * strokeWidth } } @@ -225,10 +225,10 @@ func arrowheadMarker(isTarget bool, id string, connection d2target.Connection) s } // compute the (dx, dy) adjustment to apply to get the arrowhead-adjusted end point -func arrowheadAdjustment(start, end *geo.Point, arrowhead d2target.Arrowhead, strokeWidth int) *geo.Point { - distance := float64(strokeWidth) / 2.0 +func arrowheadAdjustment(start, end *geo.Point, arrowhead d2target.Arrowhead, edgeStrokeWidth, shapeStrokeWidth int) *geo.Point { + distance := (float64(edgeStrokeWidth) + float64(shapeStrokeWidth)) / 2.0 if arrowhead != d2target.NoArrowhead { - distance += float64(strokeWidth) + distance += float64(edgeStrokeWidth) } v := geo.NewVector(end.X-start.X, end.Y-start.Y) @@ -236,11 +236,13 @@ func arrowheadAdjustment(start, end *geo.Point, arrowhead d2target.Arrowhead, st } // returns the path's d attribute for the given connection -func pathData(connection d2target.Connection) string { +func pathData(connection d2target.Connection, idToShape map[string]d2target.Shape) string { var path []string route := connection.Route + srcShape := idToShape[connection.Src] + dstShape := idToShape[connection.Dst] - sourceAdjustment := arrowheadAdjustment(route[0], route[1], connection.SrcArrow, connection.StrokeWidth) + sourceAdjustment := arrowheadAdjustment(route[0], route[1], connection.SrcArrow, connection.StrokeWidth, srcShape.StrokeWidth) path = append(path, fmt.Sprintf("M %f %f", route[0].X-sourceAdjustment.X, route[0].Y-sourceAdjustment.Y, @@ -256,7 +258,7 @@ func pathData(connection d2target.Connection) string { )) } // final curve target adjustment - targetAdjustment := arrowheadAdjustment(route[i+1], route[i+2], connection.DstArrow, connection.StrokeWidth) + targetAdjustment := arrowheadAdjustment(route[i+1], route[i+2], connection.DstArrow, connection.StrokeWidth, dstShape.StrokeWidth) path = append(path, fmt.Sprintf("C %f %f %f %f %f %f", route[i].X, route[i].Y, route[i+1].X, route[i+1].Y, @@ -315,7 +317,7 @@ func pathData(connection d2target.Connection) string { lastPoint := route[len(route)-1] secondToLastPoint := route[len(route)-2] - targetAdjustment := arrowheadAdjustment(secondToLastPoint, lastPoint, connection.DstArrow, connection.StrokeWidth) + targetAdjustment := arrowheadAdjustment(secondToLastPoint, lastPoint, connection.DstArrow, connection.StrokeWidth, dstShape.StrokeWidth) path = append(path, fmt.Sprintf("L %f %f", lastPoint.X+targetAdjustment.X, lastPoint.Y+targetAdjustment.Y, @@ -344,7 +346,7 @@ func labelMask(id string, connection d2target.Connection, labelTL, tl, br *geo.P }, "\n") } -func drawConnection(writer io.Writer, connection d2target.Connection, markers map[string]struct{}) { +func drawConnection(writer io.Writer, connection d2target.Connection, markers map[string]struct{}, idToShape map[string]d2target.Shape) { var markerStart string if connection.SrcArrow != d2target.NoArrowhead { id := arrowheadMarkerID(false, connection) @@ -414,7 +416,7 @@ func drawConnection(writer io.Writer, connection d2target.Connection, markers ma } fmt.Fprintf(writer, ``, - pathData(connection), + pathData(connection, idToShape), connectionStyle(connection), markerStart, markerEnd, @@ -772,9 +774,11 @@ func Render(diagram *d2target.Diagram) ([]byte, error) { // SVG has no notion of z-index. The z-index is effectively the order it's drawn. // So draw from the least nested to most nested + idToShape := make(map[string]d2target.Shape) highest := 1 for _, s := range diagram.Shapes { highest = go2.Max(highest, s.Level) + idToShape[s.ID] = s } for i := 1; i <= highest; i++ { for _, s := range diagram.Shapes { @@ -789,7 +793,7 @@ func Render(diagram *d2target.Diagram) ([]byte, error) { markers := map[string]struct{}{} for _, c := range diagram.Connections { - drawConnection(buf, c, markers) + drawConnection(buf, c, markers, idToShape) } embedFonts(buf) diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go index 5a5fc0e77..3aafba44d 100644 --- a/e2etests/stable_test.go +++ b/e2etests/stable_test.go @@ -637,6 +637,45 @@ func RegisterHash(h Hash, f func() hash.Hash) { } | x -> hey -> y`, + }, { + name: "arrowhead_adjustment", + script: `a <-> b: { + style.stroke-width: 6 + style.stroke-dash: 4 + source-arrowhead: { + shape: arrow + } + } + + c -> b: { + style.stroke-width: 7 + style.stroke: "#20222a" + } + c.style.stroke-width: 7 + c.style.stroke: "#b2350d" + c.shape: document + b.style.stroke-width: 8 + b.style.stroke: "#0db254" + a.style.border-radius: 10 + a.style.stroke-width: 8 + a.style.stroke: "#2bc3d8" + Oval: "" { + shape: oval + style.stroke-width: 6 + style.stroke: "#a1a4af" + } + a <-> Oval: { + style.stroke-width: 6 + source-arrowhead: { + shape: diamond + } + target-arrowhead: * { + shape: diamond + style.filled: true + } + } + c -- a: {style.stroke-width: 7} + Oval <-> c`, }, { name: "md_code_inline", diff --git a/e2etests/testdata/stable/all_shapes/sketch.exp.svg b/e2etests/testdata/stable/all_shapes/sketch.exp.svg index fcf7d2fdb..3c83a55c5 100644 --- a/e2etests/testdata/stable/all_shapes/sketch.exp.svg +++ b/e2etests/testdata/stable/all_shapes/sketch.exp.svg @@ -12,7 +12,7 @@ width="1539" height="824" viewBox="-100 -100 1539 824">rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud cba \ No newline at end of file diff --git a/e2etests/testdata/stable/binary_tree/sketch.exp.svg b/e2etests/testdata/stable/binary_tree/sketch.exp.svg index e627cc671..c7029d6f7 100644 --- a/e2etests/testdata/stable/binary_tree/sketch.exp.svg +++ b/e2etests/testdata/stable/binary_tree/sketch.exp.svg @@ -12,7 +12,7 @@ width="1518" height="1004" viewBox="-100 -100 1518 1004">abcdefghijklmno abcdefghijklmno aaadddeeebbbccc -111 +111 -222abcd abcd abc abc acfbdhg acfbdhg agdfbhec agdfbhec abcdefghijklmnopq abcdefghijklmnopq finallyatreeandnodessomemoremanythenhereyouhavehierarchyanotherofnestingtreesatreeinsidehierarchyroot finallyatreeandnodessomemoremanythenhereyouhavehierarchyanotherofnestingtreesatreeinsidehierarchyroot aabbccddllffwwyyadnniijjkkssuurmeemmmmgghhzzooppqqrrttvvxxabac +aabbccddllffwwyyadnniijjkkssuurmeemmmmgghhzzooppqqrrttvvxxabac -1 +1 -2 +2 -3 +3 -4 +4 -5 +5 -6abcdefghiqrjmnoszaabbeeffggklptuwxyccddv abcdefghiqrjmnoszaabbeeffggklptuwxyccddv

Markdown: Syntax

-
ab

code

-
ab abcdefghijklmnopqrstuvw abcdefghijklmnopqrstuvw abcdefghijklmnopqrstu abcdefghijklmnopqrstu acdefgbh acdefgbh topabcbottomstartend topabcbottomstartend acbl1l2c1l2c3l2c2l3c1l3c2l4bacacbabcc1c2c3abc acbl1l2c1l2c3l2c2l3c1l3c2l4bacacbabcc1c2c3abc AKHIALFLGAMSTNAZCANVNMUTARLAMOOKTXORCOKSNEWYCTMANYRIDEMDNJPANCSCIDMTWAILINIAMIKYWIOHMNSDVAWVMENHVTNDAKHIALFLGAMSTNAZCANVNMUTARLAMOOKTXORCOKSNEWYCTMANYRIDEMDNJPANCSCIDMTWAILINIAMIKYWIOHMNSDVAWVMENHVTND