From aa8ce9463ee5de826c51a3e60bb68d3563f717d6 Mon Sep 17 00:00:00 2001
From: Daniel Suh <23denial@gmail.com>
Date: Mon, 20 May 2024 12:43:47 -0400
Subject: [PATCH] pass e2e tests
---
d2renderers/d2sketch/sketch.go | 98 ++-
.../d2sketch/testdata/animated/sketch.exp.svg | 2 +-
.../testdata/animated_dark/sketch.exp.svg | 2 +-
.../TestCLI_E2E/internal_linked_pdf.exp.pdf | Bin 66068 -> 66068 bytes
.../testdata/TestCLI_E2E/no-nav-pdf.exp.pdf | Bin 96034 -> 96034 bytes
.../testdata/TestCLI_E2E/no-nav-pptx.exp.pptx | Bin 122127 -> 122131 bytes
.../TestCLI_E2E/renamed-board.exp.pdf | Bin 109975 -> 109975 bytes
.../testdata/TestCLI_E2E/theme-pdf.exp.pdf | Bin 17306 -> 17306 bytes
e2etests/testdata/txtar.txt | 22 +-
.../dagre/board.exp.json | 695 +++++++++++++++++
.../dagre/sketch.exp.svg | 108 +++
.../elk/board.exp.json | 645 ++++++++++++++++
.../elk/sketch.exp.svg | 108 +++
.../dagre/sketch.exp.svg | 2 +-
.../elk/sketch.exp.svg | 2 +-
.../dagre/board.exp.json | 703 ++++++++++++++++++
.../dagre/sketch.exp.svg | 116 +++
.../elk/board.exp.json | 653 ++++++++++++++++
.../elk/sketch.exp.svg | 116 +++
lib/svg/path.go | 2 -
20 files changed, 3208 insertions(+), 66 deletions(-)
create mode 100644 e2etests/testdata/txtar/bidirectional-connection-animation/dagre/board.exp.json
create mode 100644 e2etests/testdata/txtar/bidirectional-connection-animation/dagre/sketch.exp.svg
create mode 100644 e2etests/testdata/txtar/bidirectional-connection-animation/elk/board.exp.json
create mode 100644 e2etests/testdata/txtar/bidirectional-connection-animation/elk/sketch.exp.svg
create mode 100644 e2etests/testdata/txtar/sketch-bidirectional-connection-animation/dagre/board.exp.json
create mode 100644 e2etests/testdata/txtar/sketch-bidirectional-connection-animation/dagre/sketch.exp.svg
create mode 100644 e2etests/testdata/txtar/sketch-bidirectional-connection-animation/elk/board.exp.json
create mode 100644 e2etests/testdata/txtar/sketch-bidirectional-connection-animation/elk/sketch.exp.svg
diff --git a/d2renderers/d2sketch/sketch.go b/d2renderers/d2sketch/sketch.go
index 8c2ec056e..ea768d948 100644
--- a/d2renderers/d2sketch/sketch.go
+++ b/d2renderers/d2sketch/sketch.go
@@ -6,7 +6,6 @@ import (
"fmt"
"regexp"
"strings"
- "unicode"
_ "embed"
@@ -322,76 +321,69 @@ func Paths(r *Runner, shape d2target.Shape, paths []string) (string, error) {
}
func Connection(r *Runner, connection d2target.Connection, path, attrs string) (string, error) {
- roughness := 0.5
- js := fmt.Sprintf(`node = rc.path("%s", {roughness: %f, seed: 1});`, path, roughness)
- paths, err := computeRoughPathData(r, js)
- if err != nil {
- return "", err
- }
-
- finalPath := ""
-
- insertSpacesAfterLetters := func(s string) {
- output := ""
- for i := 0; i < len(s); i++ {
- if s[i] != ',' {
- output += string(s[i])
- }
- if unicode.IsLetter(rune(s[i])) {
- output += " "
- }
- }
- fmt.Println("PATH", output)
- finalPath += output
- }
- fmt.Println()
-
- for i := 0; i < len(paths); i++ {
- insertSpacesAfterLetters(paths[i])
- }
-
animatedClass := ""
if connection.Animated {
animatedClass = " animated-connection"
}
// If connection is animated and bidirectional
- if connection.Animated && ((connection.DstArrow == d2target.NoArrowhead && connection.SrcArrow == d2target.NoArrowhead) || (connection.DstArrow != d2target.NoArrowhead && connection.SrcArrow != d2target.NoArrowhead)) {
- // There is no pure CSS way to animate bidirectional connections in two directions, so we split it up
- path1, path2, err := svg.SplitPath(path, 0.5)
+ if connection.Animated {
+ if (connection.DstArrow == d2target.NoArrowhead && connection.SrcArrow == d2target.NoArrowhead) || (connection.DstArrow != d2target.NoArrowhead && connection.SrcArrow != d2target.NoArrowhead) {
+ // There is no pure CSS way to animate bidirectional connections in two directions, so we split it up
+ path1, path2, err := svg.SplitPath(path, 0.5)
+ if err != nil {
+ return "", err
+ }
+
+ pathEl1 := d2themes.NewThemableElement("path")
+ pathEl1.D = path1
+ pathEl1.Fill = color.None
+ pathEl1.Stroke = connection.Stroke
+ pathEl1.ClassName = fmt.Sprintf("connection%s", animatedClass)
+ pathEl1.Style = connection.CSSStyle()
+ pathEl1.Style += "animation-direction: reverse;"
+ pathEl1.Attributes = attrs
+
+ pathEl2 := d2themes.NewThemableElement("path")
+ pathEl2.D = path2
+ pathEl2.Fill = color.None
+ pathEl2.Stroke = connection.Stroke
+ pathEl2.ClassName = fmt.Sprintf("connection%s", animatedClass)
+ pathEl2.Style = connection.CSSStyle()
+ pathEl2.Attributes = attrs
+ return pathEl1.Render() + " " + pathEl2.Render(), nil
+ } else {
+ pathEl := d2themes.NewThemableElement("path")
+ pathEl.D = path
+ pathEl.Fill = color.None
+ pathEl.Stroke = connection.Stroke
+ pathEl.ClassName = fmt.Sprintf("connection%s", animatedClass)
+ pathEl.Style = connection.CSSStyle()
+ pathEl.Attributes = attrs
+ return pathEl.Render(), nil
+ }
+ } else {
+ roughness := 0.5
+ js := fmt.Sprintf(`node = rc.path("%s", {roughness: %f, seed: 1});`, path, roughness)
+ paths, err := computeRoughPathData(r, js)
if err != nil {
return "", err
}
- pathEl1 := d2themes.NewThemableElement("path")
- pathEl1.D = path1
- pathEl1.Fill = color.None
- pathEl1.Stroke = connection.Stroke
- pathEl1.ClassName = fmt.Sprintf("connection%s", animatedClass)
- pathEl1.Style = connection.CSSStyle()
- pathEl1.Style += "animation-direction: reverse;"
- pathEl1.Attributes = attrs
+ output := ""
- pathEl2 := d2themes.NewThemableElement("path")
- pathEl2.D = path2
- pathEl2.Fill = color.None
- pathEl2.Stroke = connection.Stroke
- pathEl2.ClassName = fmt.Sprintf("connection%s", animatedClass)
- pathEl2.Style = connection.CSSStyle()
- pathEl2.Attributes = attrs
- // return pathEl1.Render(), nil
- return pathEl2.Render(), nil
- // return pathEl1.Render() + " " + pathEl2.Render(), nil
- } else {
pathEl := d2themes.NewThemableElement("path")
- pathEl.D = finalPath
pathEl.Fill = color.None
pathEl.Stroke = connection.Stroke
pathEl.ClassName = fmt.Sprintf("connection%s", animatedClass)
pathEl.Style = connection.CSSStyle()
pathEl.Attributes = attrs
- return pathEl.Render(), nil
+ for _, p := range paths {
+ pathEl.D = p
+ output += pathEl.Render()
+ }
+ return output, nil
}
}
diff --git a/d2renderers/d2sketch/testdata/animated/sketch.exp.svg b/d2renderers/d2sketch/testdata/animated/sketch.exp.svg
index fecf1bfd8..819d95394 100644
--- a/d2renderers/d2sketch/testdata/animated/sketch.exp.svg
+++ b/d2renderers/d2sketch/testdata/animated/sketch.exp.svg
@@ -110,7 +110,7 @@
-wintersummertreessnowsun
+wintersummertreessnowsun
diff --git a/d2renderers/d2sketch/testdata/animated_dark/sketch.exp.svg b/d2renderers/d2sketch/testdata/animated_dark/sketch.exp.svg
index 1178e671f..4b24c1970 100644
--- a/d2renderers/d2sketch/testdata/animated_dark/sketch.exp.svg
+++ b/d2renderers/d2sketch/testdata/animated_dark/sketch.exp.svg
@@ -108,7 +108,7 @@
-wintersummertreessnowsun
+wintersummertreessnowsun
diff --git a/e2etests-cli/testdata/TestCLI_E2E/internal_linked_pdf.exp.pdf b/e2etests-cli/testdata/TestCLI_E2E/internal_linked_pdf.exp.pdf
index 7c1a8847dd2ac8f9e0ca68c8b64454c881ad6aa4..0cdc62313ebfc74ccc5bfd27165fa0be382d4342 100644
GIT binary patch
delta 185
zcmbQz!ZM|WWy1%F$?LioF&b?S?0M=x*)z=m$W@*^C#`7n?>mbWCf9WvO#UFTh|y&8
zWXWwllg;jcxXPR7q&-rZJom5jWWj$1lkfdCob33|n$dW2?LR|C!^!LarK*}585kOw
z7@8Pqa_Rf#r?@1Rq$+5*SQ!}@nHa$3He3ETXVSK`v{Wzv0fjsTE-=H`)YQ}fP0ZBP
M*wS+PY(~aP061zv!~g&Q
delta 181
zcmbQz!ZM|WWy1%F$^6}m7>zb>>fYuvxgyg5$W`9F`^F=c$@jYqCV!Av#Avd4vgA|$
z$r(35T;S?HhsXOCOiJKW;C2!`_GWkc=Gyxsj4Q1=7yFgmWF1U
zT>8HGDK3d6sR|k{Rz?O!CI)c1&6fYonY0ZpOcV@2Kp{_o3(PP!H8-$C6Eih4HaDI=
In~|{+0AqbW8~^|S
diff --git a/e2etests-cli/testdata/TestCLI_E2E/no-nav-pdf.exp.pdf b/e2etests-cli/testdata/TestCLI_E2E/no-nav-pdf.exp.pdf
index 73d6e3754de3b03f34ccfa42dc02e1106a3ad970..f577774c3e1de114d89311eeb80e01d27d33e8f8 100644
GIT binary patch
delta 55
zcmZ4Vj&;#H)`l&NN+qhMMh1pPCWgkAnq2z6`6(`mC8-J;E>=bcMkWSux$W*HjJKHp
D-OUfb
delta 55
zcmZ4Vj&;#H)`l&NN+qf$hUSKrCYFXKnq2z6`6(`mC8-J;E>=bcMkWSux$W*HjJKHp
D;CT<(
diff --git a/e2etests-cli/testdata/TestCLI_E2E/no-nav-pptx.exp.pptx b/e2etests-cli/testdata/TestCLI_E2E/no-nav-pptx.exp.pptx
index cf1579ac5ebe9fbd46d2730a5db52a1a9aeeb295..2adb5e28d5956d553b099a2ea147da14e037039a 100644
GIT binary patch
delta 482
zcmV<80UiF2x(AcG2Y|Ez7RP_jZreN%#P4|uf_I6O;_pL2*b2}FIW<6lx@fN~xsloM
zAINoNy?r6NV(d1z9@jJT;|^(U?@rE=S4i0ezoT8tDFLs7Hh$dEultAQ10@-y*U|~ryA>`z&&qBo=Js?KGSXKvcGPfauKRJK<9Fmim+mJ>U
zrFxMgu%7e(nFEwsqGSuSiIqw(uv%4YJSJNpS~0NTz++}z+c8?b2+8Gl14UuiWM^=W
z@UDFouM($hs(G5GcDgP0p7V}4$9_oN|9vn&|Xy_J4!RdD5?OGZ7+%8ZXen`Z<1<9xZ=-(n3Vcs0vkZs>}wj_bPR
zjn2hDlT$Y0{64Gl?e^3C{(%lX?>7zquj%=|>&2!M8{Y7Z;CwFG_X_d{+WlVY-`FeV
zt(cMsV@UII^`+YNS2Fwx00960P)i30qNc06paB2?ahIRF0Ufv6$N{nw38JQ}yPyF8
Y0CAU<+yPJl#FysW0V4(^%K-oY0Qd&(fdBvi
delta 473
zcmV;~0Ve*Fx(AQC2Y|Ez7RP_hZrd;rgztF@f_I6OVl)k5D?l6M)BpkMqP?=@MrI@a
zfLuq?+ZUoMCT@M}aXhmhcSvh{H`#%_LQKZH9c^k(2{`4oas7^d+dotvDM=`umVD+U8taF|$AH;k4nLbU1PKz7(U*&3V!
zT#T>g*~FAg36JAgkJmZga^A2Xk6*s#+Ldw3)hVD|9^Gms(BJ@_KjM}ZRm^n!`3n>l
zd(|bNB1rVHb4upGs!x9b6~qh7qmMcf_BNy;sb~Tk@4BZ52^`8rkRBXFiJ-}7@IdZc
zQjw)o5{&6~kgl(lxA~y=jB?l@6Fz!vo(Hxh|UDfJS!TGZ38RRN2wEeTx|FKufTQMaO
zdLQTI(sQ-*FQfY%00960P)i30s#Lz`oB;p;Ww*h|0nZZ&s#Lz`oB;p;WtW-U0Z;+J
Pm+IUBBL*PL0RR91`&i$R
diff --git a/e2etests-cli/testdata/TestCLI_E2E/renamed-board.exp.pdf b/e2etests-cli/testdata/TestCLI_E2E/renamed-board.exp.pdf
index 581e72d2a77bca16b0b2e4c6277e2d32a38184d9..8628441e39da91d8d9cb4e692eaace5947a46ef7 100644
GIT binary patch
delta 70
zcmbP!n{E1SwuUW?Nh_yMUdd=MJz*uI;q=2R8854v8W|WGnHU;dYI5oO=BKzMmZU0Z
UxL6q(7?~Kr<+kr$#dwDa0Fjm#QUCw|
delta 70
zcmbP!n{E1SwuUW?Nh_yMS;?qBJz*uI(e%SB8854v7@8Yenphf|XmaWM=BKzMmZU0Z
UxL6q(7?~Kr<+kr$#dwDa0F-wZZ2$lO
diff --git a/e2etests-cli/testdata/TestCLI_E2E/theme-pdf.exp.pdf b/e2etests-cli/testdata/TestCLI_E2E/theme-pdf.exp.pdf
index 01c7166e15d8e436c7ba1a17db06e0b1c1cb8e93..ffd681ab13e139e54fb0dfd923bc8e4b3cfd4071 100644
GIT binary patch
delta 52
zcmbQ$&N!=`al dog3
}
--- bidirectional_connection_animation --
-vars: {
- d2-config: {
- sketch: true
- }
-}
-
+-- bidirectional-connection-animation --
a <-> b: {style.animated: true}
a <-> c: {style.animated: true}
a <-> d: {style.animated: true}
@@ -272,3 +266,17 @@ x <-> y <-> z: {
}
direction: right
}
+
+-- sketch-bidirectional-connection-animation --
+vars: {
+ d2-config: {
+ sketch: true
+ }
+}
+
+a <-> b: {style.animated: true}
+a <-> c: {style.animated: true}
+a <-> d: {style.animated: true}
+a <-> e
+f <-> g: {style.animated: true}
+x -- x: {style.animated: true}
\ No newline at end of file
diff --git a/e2etests/testdata/txtar/bidirectional-connection-animation/dagre/board.exp.json b/e2etests/testdata/txtar/bidirectional-connection-animation/dagre/board.exp.json
new file mode 100644
index 000000000..16c3d88b9
--- /dev/null
+++ b/e2etests/testdata/txtar/bidirectional-connection-animation/dagre/board.exp.json
@@ -0,0 +1,695 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "a",
+ "type": "rectangle",
+ "pos": {
+ "x": 170,
+ "y": 0
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "b",
+ "type": "rectangle",
+ "pos": {
+ "x": 0,
+ "y": 166
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "c",
+ "type": "rectangle",
+ "pos": {
+ "x": 113,
+ "y": 166
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "d",
+ "type": "rectangle",
+ "pos": {
+ "x": 226,
+ "y": 166
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "e",
+ "type": "rectangle",
+ "pos": {
+ "x": 340,
+ "y": 166
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "e",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "f",
+ "type": "rectangle",
+ "pos": {
+ "x": 455,
+ "y": 0
+ },
+ "width": 51,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "f",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 6,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "g",
+ "type": "rectangle",
+ "pos": {
+ "x": 453,
+ "y": 166
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "g",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "x",
+ "type": "rectangle",
+ "pos": {
+ "x": 566,
+ "y": 0
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "x",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [
+ {
+ "id": "(a <-> b)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "b",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 169.75,
+ "y": 45.957000732421875
+ },
+ {
+ "x": 55.14899826049805,
+ "y": 101.99099731445312
+ },
+ {
+ "x": 26.5,
+ "y": 126
+ },
+ {
+ "x": 26.5,
+ "y": 166
+ }
+ ],
+ "isCurve": true,
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(a <-> c)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "c",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 173.5,
+ "y": 66
+ },
+ {
+ "x": 146.3000030517578,
+ "y": 106
+ },
+ {
+ "x": 139.5,
+ "y": 126
+ },
+ {
+ "x": 139.5,
+ "y": 166
+ }
+ ],
+ "isCurve": true,
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(a <-> d)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "d",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 219,
+ "y": 66
+ },
+ {
+ "x": 246.1999969482422,
+ "y": 106
+ },
+ {
+ "x": 253,
+ "y": 126
+ },
+ {
+ "x": 253,
+ "y": 166
+ }
+ ],
+ "isCurve": true,
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(a <-> e)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "e",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 222.5,
+ "y": 46
+ },
+ {
+ "x": 337.70001220703125,
+ "y": 102
+ },
+ {
+ "x": 366.5,
+ "y": 126
+ },
+ {
+ "x": 366.5,
+ "y": 166
+ }
+ ],
+ "isCurve": true,
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(f <-> g)[0]",
+ "src": "f",
+ "srcArrow": "triangle",
+ "dst": "g",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 480,
+ "y": 66
+ },
+ {
+ "x": 480,
+ "y": 106
+ },
+ {
+ "x": 480,
+ "y": 126
+ },
+ {
+ "x": 480,
+ "y": 166
+ }
+ ],
+ "isCurve": true,
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(x -- x)[0]",
+ "src": "x",
+ "srcArrow": "none",
+ "dst": "x",
+ "dstArrow": "none",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 619.166015625,
+ "y": 16
+ },
+ {
+ "x": 639.9660034179688,
+ "y": 3.1989998817443848
+ },
+ {
+ "x": 646.5,
+ "y": 0
+ },
+ {
+ "x": 648.5,
+ "y": 0
+ },
+ {
+ "x": 650.5,
+ "y": 0
+ },
+ {
+ "x": 653.166015625,
+ "y": 6.599999904632568
+ },
+ {
+ "x": 655.166015625,
+ "y": 16.5
+ },
+ {
+ "x": 657.166015625,
+ "y": 26.399999618530273
+ },
+ {
+ "x": 657.166015625,
+ "y": 39.599998474121094
+ },
+ {
+ "x": 655.166015625,
+ "y": 49.5
+ },
+ {
+ "x": 653.166015625,
+ "y": 59.400001525878906
+ },
+ {
+ "x": 639.9660034179688,
+ "y": 62.79999923706055
+ },
+ {
+ "x": 619.166015625,
+ "y": 50
+ }
+ ],
+ "isCurve": true,
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/txtar/bidirectional-connection-animation/dagre/sketch.exp.svg b/e2etests/testdata/txtar/bidirectional-connection-animation/dagre/sketch.exp.svg
new file mode 100644
index 000000000..030120280
--- /dev/null
+++ b/e2etests/testdata/txtar/bidirectional-connection-animation/dagre/sketch.exp.svg
@@ -0,0 +1,108 @@
+
\ No newline at end of file
diff --git a/e2etests/testdata/txtar/bidirectional-connection-animation/elk/board.exp.json b/e2etests/testdata/txtar/bidirectional-connection-animation/elk/board.exp.json
new file mode 100644
index 000000000..85d12fc27
--- /dev/null
+++ b/e2etests/testdata/txtar/bidirectional-connection-animation/elk/board.exp.json
@@ -0,0 +1,645 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "a",
+ "type": "rectangle",
+ "pos": {
+ "x": 68,
+ "y": 12
+ },
+ "width": 160,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "b",
+ "type": "rectangle",
+ "pos": {
+ "x": 12,
+ "y": 208
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "c",
+ "type": "rectangle",
+ "pos": {
+ "x": 85,
+ "y": 208
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "d",
+ "type": "rectangle",
+ "pos": {
+ "x": 158,
+ "y": 208
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "e",
+ "type": "rectangle",
+ "pos": {
+ "x": 232,
+ "y": 208
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "e",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "f",
+ "type": "rectangle",
+ "pos": {
+ "x": 306,
+ "y": 12
+ },
+ "width": 51,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "f",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 6,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "g",
+ "type": "rectangle",
+ "pos": {
+ "x": 305,
+ "y": 208
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "g",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "x",
+ "type": "rectangle",
+ "pos": {
+ "x": 427,
+ "y": 12
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "x",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [
+ {
+ "id": "(a <-> b)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "b",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 100.25,
+ "y": 78
+ },
+ {
+ "x": 100.25,
+ "y": 118
+ },
+ {
+ "x": 38.5,
+ "y": 118
+ },
+ {
+ "x": 38.5,
+ "y": 208
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(a <-> c)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "c",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 132.25,
+ "y": 78
+ },
+ {
+ "x": 132.25,
+ "y": 168
+ },
+ {
+ "x": 111.5,
+ "y": 168
+ },
+ {
+ "x": 111.5,
+ "y": 208
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(a <-> d)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "d",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 164.25,
+ "y": 78
+ },
+ {
+ "x": 164.25,
+ "y": 168
+ },
+ {
+ "x": 185,
+ "y": 168
+ },
+ {
+ "x": 185,
+ "y": 208
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(a <-> e)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "e",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 196.25,
+ "y": 78
+ },
+ {
+ "x": 196.25,
+ "y": 118
+ },
+ {
+ "x": 258.5,
+ "y": 118
+ },
+ {
+ "x": 258.5,
+ "y": 208
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(f <-> g)[0]",
+ "src": "f",
+ "srcArrow": "triangle",
+ "dst": "g",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 332,
+ "y": 78
+ },
+ {
+ "x": 332,
+ "y": 208
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(x -- x)[0]",
+ "src": "x",
+ "srcArrow": "none",
+ "dst": "x",
+ "dstArrow": "none",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 427.5,
+ "y": 34
+ },
+ {
+ "x": 377.5,
+ "y": 34
+ },
+ {
+ "x": 377.5,
+ "y": 56
+ },
+ {
+ "x": 427.5,
+ "y": 56
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/txtar/bidirectional-connection-animation/elk/sketch.exp.svg b/e2etests/testdata/txtar/bidirectional-connection-animation/elk/sketch.exp.svg
new file mode 100644
index 000000000..5bdfdd2c5
--- /dev/null
+++ b/e2etests/testdata/txtar/bidirectional-connection-animation/elk/sketch.exp.svg
@@ -0,0 +1,108 @@
+abcdefgx
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2etests/testdata/txtar/bidirectional_connection_animation/dagre/sketch.exp.svg b/e2etests/testdata/txtar/bidirectional_connection_animation/dagre/sketch.exp.svg
index 02f9b9fa1..8feb1399c 100644
--- a/e2etests/testdata/txtar/bidirectional_connection_animation/dagre/sketch.exp.svg
+++ b/e2etests/testdata/txtar/bidirectional_connection_animation/dagre/sketch.exp.svg
@@ -103,7 +103,7 @@
-abcdefgx
+abcdefgx
diff --git a/e2etests/testdata/txtar/bidirectional_connection_animation/elk/sketch.exp.svg b/e2etests/testdata/txtar/bidirectional_connection_animation/elk/sketch.exp.svg
index 4c6c71efb..85a25830d 100644
--- a/e2etests/testdata/txtar/bidirectional_connection_animation/elk/sketch.exp.svg
+++ b/e2etests/testdata/txtar/bidirectional_connection_animation/elk/sketch.exp.svg
@@ -103,7 +103,7 @@
-abcdefgx
+abcdefgx
diff --git a/e2etests/testdata/txtar/sketch-bidirectional-connection-animation/dagre/board.exp.json b/e2etests/testdata/txtar/sketch-bidirectional-connection-animation/dagre/board.exp.json
new file mode 100644
index 000000000..d4be34b71
--- /dev/null
+++ b/e2etests/testdata/txtar/sketch-bidirectional-connection-animation/dagre/board.exp.json
@@ -0,0 +1,703 @@
+{
+ "name": "",
+ "config": {
+ "sketch": true,
+ "themeID": null,
+ "darkThemeID": null,
+ "pad": null,
+ "center": null,
+ "layoutEngine": null
+ },
+ "isFolderOnly": false,
+ "fontFamily": "HandDrawn",
+ "shapes": [
+ {
+ "id": "a",
+ "type": "rectangle",
+ "pos": {
+ "x": 172,
+ "y": 0
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "b",
+ "type": "rectangle",
+ "pos": {
+ "x": 0,
+ "y": 166
+ },
+ "width": 55,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 10,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "c",
+ "type": "rectangle",
+ "pos": {
+ "x": 115,
+ "y": 166
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "d",
+ "type": "rectangle",
+ "pos": {
+ "x": 229,
+ "y": 166
+ },
+ "width": 55,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 10,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "e",
+ "type": "rectangle",
+ "pos": {
+ "x": 344,
+ "y": 166
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "e",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "f",
+ "type": "rectangle",
+ "pos": {
+ "x": 457,
+ "y": 0
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "f",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "g",
+ "type": "rectangle",
+ "pos": {
+ "x": 457,
+ "y": 166
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "g",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "x",
+ "type": "rectangle",
+ "pos": {
+ "x": 571,
+ "y": 0
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "x",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [
+ {
+ "id": "(a <-> b)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "b",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 172.5,
+ "y": 46
+ },
+ {
+ "x": 56.5,
+ "y": 102
+ },
+ {
+ "x": 27.5,
+ "y": 126
+ },
+ {
+ "x": 27.5,
+ "y": 166
+ }
+ ],
+ "isCurve": true,
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(a <-> c)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "c",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 176,
+ "y": 66
+ },
+ {
+ "x": 148.8000030517578,
+ "y": 106
+ },
+ {
+ "x": 142,
+ "y": 126
+ },
+ {
+ "x": 142,
+ "y": 166
+ }
+ ],
+ "isCurve": true,
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(a <-> d)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "d",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 222.5,
+ "y": 66
+ },
+ {
+ "x": 249.6999969482422,
+ "y": 106
+ },
+ {
+ "x": 256.5,
+ "y": 126
+ },
+ {
+ "x": 256.5,
+ "y": 166
+ }
+ ],
+ "isCurve": true,
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(a <-> e)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "e",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 226.25,
+ "y": 46.08599853515625
+ },
+ {
+ "x": 341.6499938964844,
+ "y": 102.01699829101562
+ },
+ {
+ "x": 370.5,
+ "y": 126
+ },
+ {
+ "x": 370.5,
+ "y": 166
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(f <-> g)[0]",
+ "src": "f",
+ "srcArrow": "triangle",
+ "dst": "g",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 484,
+ "y": 66
+ },
+ {
+ "x": 484,
+ "y": 106
+ },
+ {
+ "x": 484,
+ "y": 126
+ },
+ {
+ "x": 484,
+ "y": 166
+ }
+ ],
+ "isCurve": true,
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(x -- x)[0]",
+ "src": "x",
+ "srcArrow": "none",
+ "dst": "x",
+ "dstArrow": "none",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 624.666015625,
+ "y": 16
+ },
+ {
+ "x": 646.2659912109375,
+ "y": 3.1989998817443848
+ },
+ {
+ "x": 653,
+ "y": 0
+ },
+ {
+ "x": 655,
+ "y": 0
+ },
+ {
+ "x": 657,
+ "y": 0
+ },
+ {
+ "x": 659.666015625,
+ "y": 6.599999904632568
+ },
+ {
+ "x": 661.666015625,
+ "y": 16.5
+ },
+ {
+ "x": 663.666015625,
+ "y": 26.399999618530273
+ },
+ {
+ "x": 663.666015625,
+ "y": 39.599998474121094
+ },
+ {
+ "x": 661.666015625,
+ "y": 49.5
+ },
+ {
+ "x": 659.666015625,
+ "y": 59.400001525878906
+ },
+ {
+ "x": 646.2659912109375,
+ "y": 62.79999923706055
+ },
+ {
+ "x": 624.666015625,
+ "y": 50
+ }
+ ],
+ "isCurve": true,
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/txtar/sketch-bidirectional-connection-animation/dagre/sketch.exp.svg b/e2etests/testdata/txtar/sketch-bidirectional-connection-animation/dagre/sketch.exp.svg
new file mode 100644
index 000000000..01ae7f387
--- /dev/null
+++ b/e2etests/testdata/txtar/sketch-bidirectional-connection-animation/dagre/sketch.exp.svg
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+abcdefgx
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2etests/testdata/txtar/sketch-bidirectional-connection-animation/elk/board.exp.json b/e2etests/testdata/txtar/sketch-bidirectional-connection-animation/elk/board.exp.json
new file mode 100644
index 000000000..5abaaf205
--- /dev/null
+++ b/e2etests/testdata/txtar/sketch-bidirectional-connection-animation/elk/board.exp.json
@@ -0,0 +1,653 @@
+{
+ "name": "",
+ "config": {
+ "sketch": true,
+ "themeID": null,
+ "darkThemeID": null,
+ "pad": null,
+ "center": null,
+ "layoutEngine": null
+ },
+ "isFolderOnly": false,
+ "fontFamily": "HandDrawn",
+ "shapes": [
+ {
+ "id": "a",
+ "type": "rectangle",
+ "pos": {
+ "x": 71,
+ "y": 12
+ },
+ "width": 160,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "b",
+ "type": "rectangle",
+ "pos": {
+ "x": 12,
+ "y": 208
+ },
+ "width": 55,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 10,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "c",
+ "type": "rectangle",
+ "pos": {
+ "x": 87,
+ "y": 208
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "d",
+ "type": "rectangle",
+ "pos": {
+ "x": 161,
+ "y": 208
+ },
+ "width": 55,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 10,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "e",
+ "type": "rectangle",
+ "pos": {
+ "x": 236,
+ "y": 208
+ },
+ "width": 53,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "e",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "f",
+ "type": "rectangle",
+ "pos": {
+ "x": 309,
+ "y": 12
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "f",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "g",
+ "type": "rectangle",
+ "pos": {
+ "x": 309,
+ "y": 208
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "g",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "x",
+ "type": "rectangle",
+ "pos": {
+ "x": 433,
+ "y": 12
+ },
+ "width": 54,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "x",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 9,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [
+ {
+ "id": "(a <-> b)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "b",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 103.25,
+ "y": 78
+ },
+ {
+ "x": 103.25,
+ "y": 118
+ },
+ {
+ "x": 39.5,
+ "y": 118
+ },
+ {
+ "x": 39.5,
+ "y": 208
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(a <-> c)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "c",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 135.25,
+ "y": 78
+ },
+ {
+ "x": 135.25,
+ "y": 168
+ },
+ {
+ "x": 114,
+ "y": 168
+ },
+ {
+ "x": 114,
+ "y": 208
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(a <-> d)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "d",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 167.25,
+ "y": 78
+ },
+ {
+ "x": 167.25,
+ "y": 168
+ },
+ {
+ "x": 188.5,
+ "y": 168
+ },
+ {
+ "x": 188.5,
+ "y": 208
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(a <-> e)[0]",
+ "src": "a",
+ "srcArrow": "triangle",
+ "dst": "e",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 199.25,
+ "y": 78
+ },
+ {
+ "x": 199.25,
+ "y": 118
+ },
+ {
+ "x": 262.5,
+ "y": 118
+ },
+ {
+ "x": 262.5,
+ "y": 208
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(f <-> g)[0]",
+ "src": "f",
+ "srcArrow": "triangle",
+ "dst": "g",
+ "dstArrow": "triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 336,
+ "y": 78
+ },
+ {
+ "x": 336,
+ "y": 208
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(x -- x)[0]",
+ "src": "x",
+ "srcArrow": "none",
+ "dst": "x",
+ "dstArrow": "none",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 433,
+ "y": 34
+ },
+ {
+ "x": 383,
+ "y": 34
+ },
+ {
+ "x": 383,
+ "y": 56
+ },
+ {
+ "x": 433,
+ "y": 56
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/txtar/sketch-bidirectional-connection-animation/elk/sketch.exp.svg b/e2etests/testdata/txtar/sketch-bidirectional-connection-animation/elk/sketch.exp.svg
new file mode 100644
index 000000000..9603d3358
--- /dev/null
+++ b/e2etests/testdata/txtar/sketch-bidirectional-connection-animation/elk/sketch.exp.svg
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+abcdefgx
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/svg/path.go b/lib/svg/path.go
index f3d2d6606..56201c8ce 100644
--- a/lib/svg/path.go
+++ b/lib/svg/path.go
@@ -200,7 +200,6 @@ func pathLength(pathData []string) (float64, error) {
pathLength += geo.EuclideanDistance(prevPosition.X, prevPosition.Y, x, y)
default:
- fmt.Println("hello", pathData[i])
return 0, fmt.Errorf("unknown svg path command \"%s\"", pathData[i])
}
@@ -228,7 +227,6 @@ func SplitPath(path string, percentage float64) (string, string, error) {
pastHalf := false
pathData := strings.Split(path, " ")
pathLen, err := pathLength(pathData)
- fmt.Println("pathLen:", pathLen)
if err != nil {
return "", "", err