I'm working to understand interaction in Vega diagrams in more depth. Currently, I would like to modify the Voronoi example from the documentation so that you're not adding and removing points, but dragging the existing points around. My code looks like this:
{
"$schema": "https://vega.github.io/schema/vega/v4.json",
"width": 500,
"height": 200,
"autosize": "none",
"signals": [
{
"name": "movePoint",
"on": [
{
"events": "[mousedown, mouseup] > mousemove",
"update": "{u: round(100*invert('xscale', x()))/100, v: round(100*invert('yscale', y()))/100}"
}
]
}
],
"data": [
{
"name": "table",
"values": [
{"u": 0.1, "v": 0.1},
{"u": 0.9, "v": 0.1},
{"u": 0.1, "v": 0.9},
{"u": 0.9, "v": 0.9},
{"u": 0.5, "v": 0.5}
],
"on": [
{"trigger": "movePoint", "modify": "movePoint", "values": "{u: movePoint.u, v: movePoint.v}"}
]
}
],
"scales": [
{
"name": "xscale",
"domain": [0, 1],
"range": "width"
},
{
"name": "yscale",
"domain": [0, 1],
"range": "height"
}
],
"marks": [
{
"name": "points",
"type": "symbol",
"zindex": 1,
"from": {"data": "table"},
"interactive": false,
"encode": {
"enter": {
"fill": {"value": "black"},
"size": {"value": 36},
"x": {"scale": "xscale", "field": "u"},
"y": {"scale": "yscale", "field": "v"}
}
}
},
{
"type": "path",
"from": {"data": "points"},
"encode": {
"enter": {
"stroke": {"value": "firebrick"},
"fill": {"value": "transparent"}
}
},
"transform": [
{
"type": "voronoi",
"x": "datum.x", "y": "datum.y",
"size": [{"signal": "width"}, {"signal": "height"}]
}
]
}
]
}
It seems that the signal already works well. I tested it using the Vega debug view, and it seems to work as it should:
What doesn't work is the interaction: I can't drag the points around, and the data doesn't change on mouse interaction. I tried to move the trigger from the data to the marks, but also that didn't work. What do I have to do to be able to drag the points around?
I found out how to do this for myself. The problem was that the signal I used calculates the right new coordinates, but it doesn't contain the ID of the object being dragged around. I found it easiest to add a separate signal for that. So instead of one "MovePoint"-signal, we now have "NewPointPosition" (same signal, new name) and "WhichPoint", which contains the ID of the object being dragged. Probably this can be combined into one signal, but maybe it's easiert to understand like this.
Another important point that was missing from my first version was that the "points" and "path" marks need an "update" clause in "encode", not just an "enter" one. Otherwise, the data will change, but the visualization will not react to that.
The working code looks like this:
{
"$schema": "https://vega.github.io/schema/vega/v4.json",
"width": 500,
"height": 200,
"autosize": "none",
"signals": [
{
"name": "whichPoint",
"on": [
{
"events": "path:click, path:mousemove[event.buttons]{20}",
"update": "datum.datum"
}
]
},
{
"name": "newPointPosition",
"on": [
{
"events": "path:click, path:mousemove[event.buttons]{20}",
"update": "{u: invert('xscale', x()), v: invert('yscale', y())}"
}
]
}
],
"data": [
{
"name": "table",
"values": [
{"u": 0.1, "v": 0.1},
{"u": 0.9, "v": 0.1},
{"u": 0.1, "v": 0.9},
{"u": 0.9, "v": 0.9},
{"u": 0.5, "v": 0.5}
],
"on": [
{"trigger": "whichPoint", "modify": "whichPoint", "values": "{u: newPointPosition.u, v:newPointPosition.v}"}
]
}
],
"scales": [
{
"name": "xscale",
"domain": [0, 1],
"range": "width"
},
{
"name": "yscale",
"domain": [0, 1],
"range": "height"
}
],
"marks": [
{
"name": "points",
"type": "symbol",
"zindex": 1,
"from": {"data": "table"},
"encode": {
"enter": {
"fill": {"value": "black"},
"size": {"value": 36},
"x": {"scale": "xscale", "field": "u"},
"y": {"scale": "yscale", "field": "v"}
},
"update": {
"fill": {"value": "black"},
"size": {"value": 36},
"x": {"scale": "xscale", "field": "u"},
"y": {"scale": "yscale", "field": "v"}
}
}
},
{
"type": "path",
"from": {"data": "points"},
"encode": {
"enter": {
"stroke": {"value": "firebrick"},
"fill": {"value": "transparent"}
},
"update": {
"stroke": {"value": "firebrick"},
"fill": {"value": "transparent"}
}
},
"transform": [
{
"type": "voronoi",
"x": "datum.x", "y": "datum.y",
"size": [{"signal": "width"}, {"signal": "height"}]
}
]
}
]
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With