Assets/vega.force.directed.layout.json
{ "data": [ { "name": "link-data-raw", "values": [] }, {"name": "link-data", "source": "link-data-raw"}, { "name": "node-data", "transform": [ { "signal": "force", "type": "force", "forces": [ {"force": "center", "x": {"signal": "cx"}, "y": {"signal": "cy"}}, { "force": "collide", "iterations": 1, "radius": {"signal": "2 * nodeRadius"}, "strength": 0.7 }, {"force": "nbody", "strength": {"signal": "nodeCharge"}}, { "force": "link", "distance": {"signal": "linkDistance"}, "id": "index", "links": "link-data-raw" } ], "iterations": 300, "restart": {"signal": "restart"} }, { "type": "formula", "as": "fx", "expr": "fix[0]!=null && node==datum.index ?invert('xscale',fix[0]):null" }, { "type": "formula", "as": "fy", "expr": "fix[1]!=null && node==datum.index ?invert('yscale',fix[1]):null" } ], "values": [] }, { "name": "adj-nodes", "transform": [ { "type": "filter", "expr": "datum.src === hoverIndex || datum.tgt === hoverIndex" } ], "source": "link-data" }, { "name": "adjacentIndices", "transform": [ { "type": "formula", "as": "adj", "expr": "datum.src === hoverIndex ? datum.tgt : datum.src" }, {"fields": ["adj"], "type": "project", "as": ["adj"]} ], "source": "adj-nodes" } ], "marks": [ { "from": {"data": "link-data"}, "type": "path", "encode": { "update": { "stroke": {"value": "#ccc"}, "strokeWidth": { "signal": "datum.src === hoverIndex || datum.tgt === hoverIndex ? 2 : 0.5" } } }, "interactive": false, "name": "links", "transform": [ { "type": "linkpath", "require": {"signal": "force"}, "shape": "line", "sourceX": {"expr": "scale('xscale', datum.datum.source.x)"}, "sourceY": {"expr": "scale('yscale', datum.datum.source.y)"}, "targetX": {"expr": "scale('xscale', datum.datum.target.x)"}, "targetY": {"expr": "scale('yscale', datum.datum.target.y)"} } ] }, { "from": {"data": "node-data"}, "type": "symbol", "encode": { "enter": { "fill": {"signal": "scale('color', datum.nodeType)"}, "stroke": {"value": "white"} }, "update": { "cursor": {"value": "pointer"}, "fill": { "signal": "hoverIndex === datum.index || indata('adjacentIndices', 'adj', datum.index) ? 'red' : scale('color', datum.nodeType)" }, "size": { "signal": "(hoverIndex === datum.index || indata('adjacentIndices', 'adj', datum.index)) ? 2.5 * nodeRadius * nodeRadius : 2 * nodeRadius * nodeRadius" }, "tooltip": {"signal": "{ name: datum.name }"}, "x": { "signal": "fix[0]!=null && node===datum.index ?fix[0]:scale('xscale', datum.x)" }, "y": { "signal": "fix[1]!=null && node===datum.index ?fix[1]:scale('yscale', datum.y)" }, "zindex": { "signal": "(hoverIndex === datum.index || indata('adjacentIndices', 'adj', datum.index)) ? 10 : 1" } } }, "name": "nodes", "zindex": 1 } ], "scales": [ { "domain": {"data": "node-data", "field": "nodeType"}, "name": "color", "type": "ordinal", "range": {"scheme": "category10"} }, { "domain": {"signal": "xdom"}, "name": "xscale", "range": {"signal": "xrange"}, "zero": false }, { "domain": {"signal": "ydom"}, "name": "yscale", "range": {"signal": "yrange"}, "zero": false } ], "signals": [ {"name": "xrange", "update": "[0, width]"}, {"name": "yrange", "update": "[height, 0]"}, {"name": "xext", "update": "[0, width]"}, {"name": "yext", "update": "[height, 0]"}, { "name": "down", "on": [ {"events": "mouseup,touchend", "update": "null"}, {"events": "mousedown, touchstart", "update": "xy()"}, {"events": "symbol:mousedown, symbol:touchstart", "update": "null"} ] }, { "name": "xcur", "on": [{"events": "mousedown, touchstart, touchend", "update": "xdom"}] }, { "name": "ycur", "on": [{"events": "mousedown, touchstart, touchend", "update": "ydom"}] }, { "name": "delta", "on": [ { "events": [ { "between": [ {"type": "mousedown"}, {"source": "window", "type": "mouseup"} ], "consume": true, "source": "window", "type": "mousemove" }, { "consume": true, "filter": "event.touches.length === 1", "type": "touchmove" } ], "update": "down ? [down[0]-x(), y()-down[1]] : [0,0]" } ], "value": [0, 0] }, { "name": "anchor", "on": [ { "events": "wheel", "update": "[invert('xscale', x()), invert('yscale', y())]" }, { "events": { "filter": "event.touches.length===2", "type": "touchstart" }, "update": "[(xdom[0] + xdom[1]) / 2, (ydom[0] + ydom[1]) / 2]" } ], "value": [0, 0] }, { "name": "zoom", "on": [ { "events": "wheel!", "force": true, "update": "pow(1.001, event.deltaY * pow(16, event.deltaMode))" }, { "events": {"signal": "dist2"}, "force": true, "update": "dist1 / dist2" }, {"events": [{"source": "view", "type": "dblclick"}], "update": "1"} ], "value": 1 }, { "name": "dist1", "on": [ { "events": { "filter": "event.touches.length===2", "type": "touchstart" }, "update": "pinchDistance(event)" }, {"events": {"signal": "dist2"}, "update": "dist2"} ], "value": 0 }, { "name": "dist2", "on": [ { "events": { "consume": true, "filter": "event.touches.length===2", "type": "touchmove" }, "update": "pinchDistance(event)" } ], "value": 0 }, { "name": "xdom", "on": [ { "events": {"signal": "delta"}, "update": "[xcur[0] + span(xcur) * delta[0] / width, xcur[1] + span(xcur) * delta[0] / width]" }, { "events": {"signal": "zoom"}, "update": "[anchor[0] + (xdom[0] - anchor[0]) * zoom, anchor[0] + (xdom[1] - anchor[0]) * zoom]" }, {"events": [{"source": "view", "type": "dblclick"}], "update": "xrange"} ], "update": "xext" }, { "name": "ydom", "on": [ { "events": {"signal": "delta"}, "update": "[ycur[0] + span(ycur) * delta[1] / height, ycur[1] + span(ycur) * delta[1] / height]" }, { "events": {"signal": "zoom"}, "update": "[anchor[1] + (ydom[0] - anchor[1]) * zoom, anchor[1] + (ydom[1] - anchor[1]) * zoom]" }, {"events": [{"source": "view", "type": "dblclick"}], "update": "yrange"} ], "update": "yext" }, {"name": "size", "update": "clamp(20 / span(xdom), 1, 1000)"}, { "name": "cx", "on": [ { "events": "[symbol:mousedown, window:mouseup] > window:mousemove", "update": " cx==width/2?cx+0.001:width/2" } ], "update": "width / 2" }, {"name": "cy", "update": "height / 2"}, {"name": "w", "value": 1200}, {"name": "h", "value": 800}, { "name": "hoverIndex", "on": [ {"events": "symbol:mouseover", "update": "datum.index"}, {"events": "symbol:mouseout", "update": "-1"} ], "value": -1 }, { "name": "nodeRadius", "bind": {"input": "range", "max": 50, "min": 1, "step": 1}, "value": 8 }, { "name": "nodeCharge", "bind": {"input": "range", "max": 10, "min": -100, "step": 1}, "value": -30 }, { "name": "linkDistance", "bind": {"input": "range", "max": 200, "min": 5, "step": 1}, "value": 30 }, {"name": "static", "value": false}, { "description": "State variable for active node fix status.", "name": "fix", "on": [ { "events": "symbol:mouseout[!event.buttons], window:mouseup", "update": "false" }, {"events": "symbol:mouseover", "force": true, "update": "fix || true"}, { "events": "[symbol:mousedown, window:mouseup] > window:mousemove!", "force": true, "update": "xy()" } ], "value": false }, { "description": "Graph node most recently interacted with.", "name": "node", "on": [ { "events": "symbol:mouseover", "update": "fix === true ? datum.index : node" } ] }, { "description": "Flag to restart Force simulation upon data changes.", "name": "restart", "on": [{"events": {"signal": "fix"}, "update": "fix && fix.length"}], "value": false } ], "legends": [ { "fill": "color", "title": "Color codes", "direction": "vertical", "labelLimit": 0 } ], "$schema": "https://vega.github.io/schema/vega/v6.json", "autosize": {"type": "pad", "resize": true, "contains": "padding"}, "padding": {"right": 260}, "description": "A node-link diagram with force-directed layout", "height": {"signal": "h"}, "width": {"signal": "w"} } |