|
var networkFile = 'https://vega.github.io/new-editor/app/data/miserables.json'; |
|
var spec = { |
|
"$schema": "https://vega.github.io/schema/vega/v3.0.json", |
|
"width": 500, |
|
"height": 500, |
|
"autosize": {"type": "fit", "resize": true}, |
|
|
|
"signals": [ |
|
// center force |
|
{ "name": "cx", "value": "0.5", |
|
"bind": {"input": "range", "element": "#centerX", "min": 0, "max": 1, "step": 0.01} }, |
|
{ "name": "cy", "value": "0.5", |
|
"bind": {"input": "range", "element": "#centerY", "min": 0, "max": 1, "step": 0.01} }, |
|
|
|
// charge force |
|
{ "name": "chargeEnabled", "value": true, |
|
"bind": {"input": "checkbox", "element": "#chargeEnabled"} }, |
|
{ "name": "chargeStrength", "value": -30, |
|
"bind": {"input": "range", "element": "#chargeStrength", "min": -100, "max": 10, "step": 1} }, |
|
{ "name": "chargeDistanceMin", "value": 1, |
|
"bind": {"input": "range", "element": "#chargeDistanceMin", "min":0, "max": 50, "step": 0.1} }, |
|
{ "name": "chargeDistanceMax", "value": 2000, |
|
"bind": {"input": "range", "element": "#chargeDistanceMax", "min":0, "max": 2000, "step": 0.1} }, |
|
|
|
// collide force |
|
{ "name": "collideEnabled", "value": true, |
|
"bind": {"input": "checkbox", "element": "#collideEnabled"} }, |
|
{ "name": "collideStrength", "value": 0.7, |
|
"bind": {"input": "range", "element": "#collideStrength", "min":0, "max": 2, "step": 0.1} }, |
|
{ "name": "collideRadius", "value": 5, |
|
"bind": {"input": "range", "element": "#collideRadius", "min":0, "max": 100, "step": 1} }, |
|
{ "name": "collideIterations", "value": 1, |
|
"bind": {"input": "range", "element": "#collideIterations", "min":1, "max": 10, "step": 1} }, |
|
|
|
// X force |
|
{ "name": "forceXEnabled", "value": false, |
|
"bind": {"input": "checkbox", "element": "#forceXEnabled"} }, |
|
{ "name": "forceX_Strength", "value": 0, |
|
"bind": {"input": "range", "element": "#forceX_Strength", "min":0, "max": 1, "step": 0.01} }, |
|
{ "name": "forceX_X", "value": .5, |
|
"bind": {"input": "range", "element": "#forceX_X", "min":0, "max": 1, "step": .01} }, |
|
|
|
// Y force |
|
{ "name": "forceYEnabled", "value": false, |
|
"bind": {"input": "checkbox", "element": "#forceYEnabled"} }, |
|
{ "name": "forceY_Strength", "value": .1, |
|
"bind": {"input": "range", "element": "#forceY_Strength", "min":0, "max": 1, "step": 0.01} }, |
|
{ "name": "forceY_Y", "value": .5, |
|
"bind": {"input": "range", "element": "#forceY_Y", "min":0, "max": 1, "step": 0.01} }, |
|
|
|
// link force |
|
{ "name": "linkEnabled", "value": true, |
|
"bind": {"input": "checkbox", "element": "#linkEnabled"} }, |
|
{ "name": "linkDistance", "value": 30, |
|
"bind": {"input": "range", "element": "#linkDistance", "min": 5, "max": 100, "step": 1} }, |
|
{ "name": "linkIterations", "value": 1, |
|
"bind": {"input": "range", "element": "#linkIterations", "min":1, "max": 10, "step": 1} }, |
|
|
|
// other parameters |
|
{ "name": "static", "value": false }, |
|
|
|
{ |
|
"description": "State variable for active node fix status.", |
|
"name": "fix", "value": 0, |
|
"on": [ |
|
{ |
|
"events": "symbol:mouseout[!event.buttons], window:mouseup", |
|
"update": "0" |
|
}, |
|
{ |
|
"events": "symbol:mouseover", |
|
"update": "fix || 1" |
|
}, |
|
{ |
|
"events": "[symbol:mousedown, window:mouseup] > window:mousemove!", |
|
"update": "2", "force": true |
|
} |
|
] |
|
}, |
|
{ |
|
"description": "Graph node most recently interacted with.", |
|
"name": "node", "value": null, |
|
"on": [ |
|
{ |
|
"events": "symbol:mouseover", |
|
"update": "fix === 1 ? item() : node" |
|
} |
|
] |
|
}, |
|
{ |
|
"description": "Flag to restart Force simulation upon data changes.", |
|
"name": "restart", "value": false, |
|
"on": [ |
|
{"events": {"signal": "fix"}, "update": "fix > 1"} |
|
] |
|
} |
|
], |
|
|
|
"data": [ |
|
{ |
|
"name": "node-data", |
|
"url": networkFile, |
|
"format": {"type": "json", "property": "nodes"} |
|
}, |
|
{ |
|
"name": "linkData", |
|
"url": networkFile, |
|
"format": {"type": "json", "property": "links"} |
|
} |
|
], |
|
|
|
"marks": [ |
|
{ |
|
"name": "nodes", |
|
"type": "symbol", |
|
"zindex": 1, |
|
|
|
"from": {"data": "node-data"}, |
|
"on": [ |
|
{ |
|
"trigger": "fix", |
|
"modify": "node", |
|
"values": "fix === 1 ? {fx:node.x, fy:node.y} : {fx:x(), fy:y()}" |
|
}, |
|
{ |
|
"trigger": "!fix", |
|
"modify": "node", "values": "{fx: null, fy: null}" |
|
} |
|
], |
|
|
|
"encode": { |
|
"enter": { |
|
"fill": {"value": "black"} |
|
}, |
|
"update": { |
|
"cursor": {"value": "pointer"}, |
|
"size": {"signal": "collideRadius * collideRadius * 4"}, |
|
"stroke": {"signal": "chargeStrength > 0 ? 'blue' : 'red'"}, |
|
"strokeWidth": {"signal": "abs(chargeStrength) / 15"} |
|
} |
|
}, |
|
|
|
"transform": [ |
|
{ |
|
"type": "force", |
|
"iterations": 300, |
|
"restart": {"signal": "restart"}, |
|
"static": {"signal": "static"}, |
|
"forces": [ |
|
{"force": "center", "x": {"signal": "width * cx"}, "y": {"signal": "height * cy"}}, |
|
{"force": "nbody", "strength": {"signal": "chargeStrength * chargeEnabled"}, "distanceMin": {"signal": "chargeDistanceMin"}, "distanceMax": {"signal": "chargeDistanceMax"}}, |
|
{"force": "collide", "strength": {"signal": "collideStrength * collideEnabled"}, "radius": {"signal": "collideRadius"}, "iterations": {"signal": "collideIterations"}}, |
|
// {"force": "x", "strength": {"expr": "forceX_Strength * forceXEnabled"}, "x": {"expr": "width * forceX_X"}}, // blanks screen if enabled |
|
{"force": "link", "links": "linkData", "distance": {"signal": "linkDistance"}, "iterations": {"signal": "linkIterations"}} |
|
// {"force": "link", "links": {"signal": "linkEnabled ? 'linkData' : []"}, "distance": {"signal": "linkDistance"}, "iterations": {"signal": "linkIterations"}} |
|
] |
|
} |
|
] |
|
}, |
|
{ |
|
"type": "path", |
|
"from": {"data": "linkData"}, |
|
"interactive": false, |
|
"encode": { |
|
"update": { |
|
"stroke": {"value": "#ccc"}, |
|
"strokeWidth": {"value": 0.5} |
|
} |
|
}, |
|
"transform": [ |
|
{ |
|
"type": "linkpath", "shape": "line", |
|
"sourceX": "datum.source.x", "sourceY": "datum.source.y", |
|
"targetX": "datum.target.x", "targetY": "datum.target.y" |
|
} |
|
] |
|
} |
|
] |
|
}; |