Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mouse interaction for dragging data points in Vega

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:

  • the signal changes only when a mouse move occurs during mouse down
  • the signal values are in the right coordinate system, the one used by the data, i.e. coordinates between 0 and 1

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?

like image 490
MightyCurious Avatar asked Oct 24 '25 00:10

MightyCurious


1 Answers

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"}]
        }
      ]
    }
  ]
}
like image 132
MightyCurious Avatar answered Oct 25 '25 20:10

MightyCurious



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!