Closed DW19904 closed 3 months ago
The triangles are at the end of the each path. The problem is that path ends are in the middle of your nodes (squares, diamonds, circles), not at the node edges. And taking into account that you have multiple shapes of the nodes (squares, diamonds, circles), it won't be easy to calculate a correct position of where the path intersects node edge.
As an easier workaround you can put the triangles into the middle of each node. That requires some trigonometry calculations to make sure the triangle looks into the right direction:
{
"type": "symbol",
"from": {"data": "links"},
"encode": {
"enter": {
"shape": {"value": "triangle"},
"size": {"value": 80},
"fill": {"value": "black"}
},
"update": {
"x": {"signal": "(datum.source.x+datum.target.x)/2"},
"y": {"signal": "(datum.source.y+datum.target.y)/2"},
"angle": {
"signal": "360*atan((datum.target.y-datum.source.y)/(datum.target.x-datum.source.x))/(2*PI)+if(datum.target.x>originX,180,0)-90"
}
}
}
},
And I've changed data source to "from": {"data": "links"} for this mark, because you need a triangle for each link
If you'll really want to put the triangles to the edges of the nodes, you need to calculate where the links intersect with the node edges. But there will be some extra trigonometry (less for the circles, more for the squares and diamonds). If you understand trigonometry - you can do that. Or just keep it as is.
Thank you so much, the triangle in the middle of the link works perfectly.
There is 1 other requirement that i am trying to work on, and its that some links need a triangle pointing in the other direction, but it can still sit in the middle, essentially just behind the existing one, if this is possible via another symbol mark which is a triangle, and it essentially looks like the below.
I think you can use either triangle or diamond within the existing mark. Just use signal with if() statement instead of value in "shape": {"value": "triangle"}.
Fantastic, thank you! working a treat.
Hi,
I am trying to place triangles at the end of a path mark to represent an arrow head, i require them at both ends of the path.
In the current code below i have a triangle symbol mark added but its not in the correct position and i am struggling in how to move it to the end of each path, any help in how i get triangles at each end of the path is very much appreciated, i have also attached some mock data the visual is based on.
{ "width": 1200, "height": 600, "padding": 5, "autosize": "none", "signals": [ { "name": "labels", "value": true, "bind": {"input": "checkbox"} }, { "name": "radius", "value": 250, "bind": { "input": "range", "min": 100, "max": 250 } }, { "name": "links", "value": "line", "bind": { "input": "select", "options": [ "line", "curve", "diagonal", "orthogonal" ] } }, { "name": "originX", "update": "width / 2" }, { "name": "originY", "update": "height / 2" } ], "data": [ { "name": "dataset", "transform": [ { "type": "stratify", "key": "ID", "parentKey": "Parent" }, { "type": "tree", "method": "cluster", "size": [ 1, {"signal": "radius"} ], "as": [ "alpha", "radius", "depth", "children" ] }, { "type": "formula", "expr": "(0 + 360 datum.alpha + 160) % 360", "as": "angle" }, { "type": "formula", "expr": "PI datum.angle / 180", "as": "radians" }, { "type": "formula", "expr": "inrange(datum.angle, [90, 270])", "as": "leftside" }, { "type": "formula", "expr": "originX + datum.radius cos(datum.radians)", "as": "x" }, { "type": "formula", "expr": "originY + datum.radius sin(datum.radians)", "as": "y" } ] }, { "name": "links", "source": "dataset", "transform": [ {"type": "treelinks"}, { "type": "linkpath", "shape": {"signal": "links"}, "orient": "radial", "sourceX": "source.radians", "sourceY": "source.radius", "targetX": "target.radians", "targetY": "target.radius" } ] } ], "scales": [ { "name": "color", "type": "linear", "range": {"scheme": "magma"}, "domain": { "data": "dataset", "field": "depth" }, "zero": true } ], "marks": [ { "type": "path", "from": {"data": "links"}, "encode": { "update": { "x": {"signal": "originX"}, "y": {"signal": "originY"}, "path": {"field": "path"}, "stroke": {"value": "#ccc"}, "strokeDash": { "signal": "datum.target.LinkStrength === 'Weak' ? [5,5] : null" } } } }, { "type": "symbol", "from": {"data": "dataset"}, "encode": { "enter": { "size": {"value": 5000}, "stroke": {"value": "#666666"} }, "update": { "x": {"field": "x"}, "y": {"field": "y"}, "shape": { "signal": "datum.NodeType === 'Animal' ? 'diamond' : datum.NodeType === 'Human' ? 'circle' : 'square'" }, "fill": {"field": "NodeCF"} } } }, { "type": "symbol", "from": {"data": "dataset"}, "encode": { "enter": { "shape": {"value": "triangle-down"}, "size": {"value": 500}, "fill": {"value": "black"} }, "update": { "x": {"field": "x"}, "y": {"signal": "datum.y - 15"} } } }, { "type": "text", "from": {"data": "dataset"}, "encode": { "enter": { "text": { "signal": "split(datum.Name, ' ')" }, "fontSize": {"value": 12}, "baseline": { "value": "middle" }, "align": {"value": "center"} }, "update": { "x": {"field": "x"}, "y": { "signal": "datum.y - 0" }, "opacity": { "signal": "labels ? 1 : 0" } } } } ] }
vegadata.csv