diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md
index 372655bca0..e10f25c482 100644
--- a/ci/release/changelogs/next.md
+++ b/ci/release/changelogs/next.md
@@ -1,6 +1,7 @@
#### Features ๐
- ELK now routes `sql_table` edges to the exact columns (ty @landmaj) [#1681](https://github.com/terrastruct/d2/pull/1681)
+- Adds new unfilled triangle arrowhead. [#1711](https://github.com/terrastruct/d2/pull/1711)
#### Improvements ๐งน
diff --git a/d2exporter/export.go b/d2exporter/export.go
index f79e98af66..bf923c6531 100644
--- a/d2exporter/export.go
+++ b/d2exporter/export.go
@@ -202,7 +202,7 @@ func toConnection(edge *d2graph.Edge, theme *d2themes.Theme) d2target.Connection
if edge.SrcArrow {
connection.SrcArrow = d2target.DefaultArrowhead
- if edge.SrcArrowhead != nil && edge.SrcArrowhead.Shape.Value != "" {
+ if edge.SrcArrowhead != nil {
connection.SrcArrow = edge.SrcArrowhead.ToArrowhead()
}
}
@@ -220,7 +220,7 @@ func toConnection(edge *d2graph.Edge, theme *d2themes.Theme) d2target.Connection
}
if edge.DstArrow {
connection.DstArrow = d2target.DefaultArrowhead
- if edge.DstArrowhead != nil && edge.DstArrowhead.Shape.Value != "" {
+ if edge.DstArrowhead != nil {
connection.DstArrow = edge.DstArrowhead.ToArrowhead()
}
}
diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go
index 16345ed6f6..1dc0942e08 100644
--- a/d2graph/d2graph.go
+++ b/d2graph/d2graph.go
@@ -177,13 +177,10 @@ func (a *Attributes) ApplyTextTransform() {
}
func (a *Attributes) ToArrowhead() d2target.Arrowhead {
- if a.Shape.Value == "" {
- return d2target.NoArrowhead
- }
-
- filled := false
+ var filled *bool
if a.Style.Filled != nil {
- filled, _ = strconv.ParseBool(a.Style.Filled.Value)
+ v, _ := strconv.ParseBool(a.Style.Filled.Value)
+ filled = go2.Pointer(v)
}
return d2target.ToArrowhead(a.Shape.Value, filled)
}
diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go
index 2e910d6586..67db074c99 100644
--- a/d2renderers/d2svg/d2svg.go
+++ b/d2renderers/d2svg/d2svg.go
@@ -138,6 +138,29 @@ func arrowheadMarker(isTarget bool, id string, connection d2target.Connection) s
)
}
path = polygonEl.Render()
+ case d2target.UnfilledTriangleArrowhead:
+ polygonEl := d2themes.NewThemableElement("polygon")
+ polygonEl.Fill = d2target.BG_COLOR
+ polygonEl.Stroke = connection.Stroke
+ polygonEl.ClassName = "connection"
+ polygonEl.Attributes = fmt.Sprintf(`stroke-width="%d"`, connection.StrokeWidth)
+
+ inset := strokeWidth / 2
+ if isTarget {
+ polygonEl.Points = fmt.Sprintf("%f,%f %f,%f %f,%f",
+ inset, inset,
+ width-inset, height/2.0,
+ inset, height-inset,
+ )
+ } else {
+ polygonEl.Points = fmt.Sprintf("%f,%f %f,%f %f,%f",
+ width-inset, inset,
+ inset, height/2.0,
+ width-inset, height-inset,
+ )
+ }
+ path = polygonEl.Render()
+
case d2target.TriangleArrowhead:
polygonEl := d2themes.NewThemableElement("polygon")
polygonEl.Fill = connection.Stroke
diff --git a/d2target/d2target.go b/d2target/d2target.go
index 9b69ff472e..3c468b81da 100644
--- a/d2target/d2target.go
+++ b/d2target/d2target.go
@@ -720,13 +720,14 @@ func (c Connection) GetID() string {
type Arrowhead string
const (
- NoArrowhead Arrowhead = "none"
- ArrowArrowhead Arrowhead = "arrow"
- TriangleArrowhead Arrowhead = "triangle"
- DiamondArrowhead Arrowhead = "diamond"
- FilledDiamondArrowhead Arrowhead = "filled-diamond"
- CircleArrowhead Arrowhead = "circle"
- FilledCircleArrowhead Arrowhead = "filled-circle"
+ NoArrowhead Arrowhead = "none"
+ ArrowArrowhead Arrowhead = "arrow"
+ UnfilledTriangleArrowhead Arrowhead = "unfilled-triangle"
+ TriangleArrowhead Arrowhead = "triangle"
+ DiamondArrowhead Arrowhead = "diamond"
+ FilledDiamondArrowhead Arrowhead = "filled-diamond"
+ CircleArrowhead Arrowhead = "circle"
+ FilledCircleArrowhead Arrowhead = "filled-circle"
// For fat arrows
LineArrowhead Arrowhead = "line"
@@ -740,29 +741,28 @@ const (
DefaultArrowhead Arrowhead = TriangleArrowhead
)
+// valid values for arrowhead.shape
var Arrowheads = map[string]struct{}{
- string(NoArrowhead): {},
- string(ArrowArrowhead): {},
- string(TriangleArrowhead): {},
- string(DiamondArrowhead): {},
- string(FilledDiamondArrowhead): {},
- string(CircleArrowhead): {},
- string(FilledCircleArrowhead): {},
- string(CfOne): {},
- string(CfMany): {},
- string(CfOneRequired): {},
- string(CfManyRequired): {},
-}
-
-func ToArrowhead(arrowheadType string, filled bool) Arrowhead {
+ string(NoArrowhead): {},
+ string(ArrowArrowhead): {},
+ string(TriangleArrowhead): {},
+ string(DiamondArrowhead): {},
+ string(CircleArrowhead): {},
+ string(CfOne): {},
+ string(CfMany): {},
+ string(CfOneRequired): {},
+ string(CfManyRequired): {},
+}
+
+func ToArrowhead(arrowheadType string, filled *bool) Arrowhead {
switch arrowheadType {
case string(DiamondArrowhead):
- if filled {
+ if filled != nil && *filled {
return FilledDiamondArrowhead
}
return DiamondArrowhead
case string(CircleArrowhead):
- if filled {
+ if filled != nil && *filled {
return FilledCircleArrowhead
}
return CircleArrowhead
@@ -771,6 +771,9 @@ func ToArrowhead(arrowheadType string, filled bool) Arrowhead {
case string(ArrowArrowhead):
return ArrowArrowhead
case string(TriangleArrowhead):
+ if filled != nil && !(*filled) {
+ return UnfilledTriangleArrowhead
+ }
return TriangleArrowhead
case string(CfOne):
return CfOne
@@ -781,6 +784,10 @@ func ToArrowhead(arrowheadType string, filled bool) Arrowhead {
case string(CfManyRequired):
return CfManyRequired
default:
+ if DefaultArrowhead == TriangleArrowhead &&
+ filled != nil && !(*filled) {
+ return UnfilledTriangleArrowhead
+ }
return DefaultArrowhead
}
}
@@ -799,6 +806,11 @@ func (arrowhead Arrowhead) Dimensions(strokeWidth float64) (width, height float6
baseHeight = 4
widthMultiplier = 3
heightMultiplier = 4
+ case UnfilledTriangleArrowhead:
+ baseWidth = 7
+ baseHeight = 7
+ widthMultiplier = 3
+ heightMultiplier = 4
case LineArrowhead:
widthMultiplier = 5
heightMultiplier = 8
diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go
index c62d1d6254..e1f12172a5 100644
--- a/e2etests/stable_test.go
+++ b/e2etests/stable_test.go
@@ -2868,6 +2868,7 @@ y: profits {
loadFromFile(t, "grid_outside_labels"),
loadFromFile(t, "grid_edge_across_cell"),
loadFromFile(t, "nesting_power"),
+ loadFromFile(t, "unfilled_triangle"),
}
runa(t, tcs)
diff --git a/e2etests/testdata/files/unfilled_triangle.d2 b/e2etests/testdata/files/unfilled_triangle.d2
new file mode 100644
index 0000000000..3993c0743b
--- /dev/null
+++ b/e2etests/testdata/files/unfilled_triangle.d2
@@ -0,0 +1,16 @@
+direction: right
+
+A <-> B: default {
+ source-arrowhead.style.filled: false
+ target-arrowhead.style.filled: false
+}
+C <-> D: triangle {
+ source-arrowhead: {
+ shape: triangle
+ style.filled: false
+ }
+ target-arrowhead: {
+ shape: triangle
+ style.filled: false
+ }
+}
diff --git a/e2etests/testdata/stable/unfilled_triangle/dagre/board.exp.json b/e2etests/testdata/stable/unfilled_triangle/dagre/board.exp.json
new file mode 100644
index 0000000000..52742a5c94
--- /dev/null
+++ b/e2etests/testdata/stable/unfilled_triangle/dagre/board.exp.json
@@ -0,0 +1,307 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "A",
+ "type": "rectangle",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 56,
+ "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": 11,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "B",
+ "type": "rectangle",
+ "pos": {
+ "x": 208,
+ "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": "B",
+ "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": "C",
+ "type": "rectangle",
+ "pos": {
+ "x": 1,
+ "y": 126
+ },
+ "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": 208,
+ "y": 126
+ },
+ "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
+ }
+ ],
+ "connections": [
+ {
+ "id": "(A <-> B)[0]",
+ "src": "A",
+ "srcArrow": "unfilled-triangle",
+ "dst": "B",
+ "dstArrow": "unfilled-triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "default",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 48,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 56,
+ "y": 33
+ },
+ {
+ "x": 116.80000305175781,
+ "y": 33
+ },
+ {
+ "x": 147.1999969482422,
+ "y": 33
+ },
+ {
+ "x": 208,
+ "y": 33
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(C <-> D)[0]",
+ "src": "C",
+ "srcArrow": "unfilled-triangle",
+ "dst": "D",
+ "dstArrow": "unfilled-triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "triangle",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 52,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 55,
+ "y": 159
+ },
+ {
+ "x": 116.5999984741211,
+ "y": 159
+ },
+ {
+ "x": 147.1999969482422,
+ "y": 159
+ },
+ {
+ "x": 208,
+ "y": 159
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "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/stable/unfilled_triangle/dagre/sketch.exp.svg b/e2etests/testdata/stable/unfilled_triangle/dagre/sketch.exp.svg
new file mode 100644
index 0000000000..d74d465995
--- /dev/null
+++ b/e2etests/testdata/stable/unfilled_triangle/dagre/sketch.exp.svg
@@ -0,0 +1,107 @@
+
\ No newline at end of file
diff --git a/e2etests/testdata/stable/unfilled_triangle/elk/board.exp.json b/e2etests/testdata/stable/unfilled_triangle/elk/board.exp.json
new file mode 100644
index 0000000000..d9d7fbb481
--- /dev/null
+++ b/e2etests/testdata/stable/unfilled_triangle/elk/board.exp.json
@@ -0,0 +1,289 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "A",
+ "type": "rectangle",
+ "pos": {
+ "x": 12,
+ "y": 12
+ },
+ "width": 56,
+ "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": 11,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "B",
+ "type": "rectangle",
+ "pos": {
+ "x": 260,
+ "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": "B",
+ "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": "C",
+ "type": "rectangle",
+ "pos": {
+ "x": 14,
+ "y": 98
+ },
+ "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": 260,
+ "y": 98
+ },
+ "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
+ }
+ ],
+ "connections": [
+ {
+ "id": "(A <-> B)[0]",
+ "src": "A",
+ "srcArrow": "unfilled-triangle",
+ "dst": "B",
+ "dstArrow": "unfilled-triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "default",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 48,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 68,
+ "y": 45
+ },
+ {
+ "x": 260,
+ "y": 45
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(C <-> D)[0]",
+ "src": "C",
+ "srcArrow": "unfilled-triangle",
+ "dst": "D",
+ "dstArrow": "unfilled-triangle",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "triangle",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 52,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 68,
+ "y": 131
+ },
+ {
+ "x": 260,
+ "y": 131
+ }
+ ],
+ "animated": false,
+ "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/stable/unfilled_triangle/elk/sketch.exp.svg b/e2etests/testdata/stable/unfilled_triangle/elk/sketch.exp.svg
new file mode 100644
index 0000000000..2c80e0f5c6
--- /dev/null
+++ b/e2etests/testdata/stable/unfilled_triangle/elk/sketch.exp.svg
@@ -0,0 +1,107 @@
+ABCD defaulttriangle
+
+
+
+
+
+
+
+
\ No newline at end of file