UNPKG

d3plus-network

Version:

Javascript network visualizations built upon d3 modules.

629 lines (612 loc) 29.4 kB
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); } function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get.bind(); } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); } function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } /** @external Viz @see https://github.com/d3plus/d3plus-viz#Viz */ import { extent, max, mean, min, merge } from "d3-array"; import { nest } from "d3-collection"; import { forceLink, forceManyBody, forceSimulation } from "d3-force"; import { polygonHull } from "d3-polygon"; import * as scales from "d3-scale"; import { zoomTransform } from "d3-zoom"; import { accessor, assign, configPrep, constant, elem } from "d3plus-common"; import * as shapes from "d3plus-shape"; import { addToQueue, Viz } from "d3plus-viz"; /** * Fetches the unique ID for a data point, whether it's defined by data or nodes. * @private */ function getNodeId(d, i) { return "".concat(this._id(d, i) || this._nodeGroupBy[min([this._drawDepth, this._nodeGroupBy.length - 1])](d, i)); } /** @class Network @extends external:Viz @desc Creates a network visualization based on a defined set of nodes and edges. [Click here](http://d3plus.org/examples/d3plus-network/getting-started/) for help getting started using the Network class. */ var Network = /*#__PURE__*/function (_Viz) { _inherits(Network, _Viz); var _super = _createSuper(Network); /** @memberof Network @desc Invoked when creating a new class instance, and sets any default parameters. @private */ function Network() { var _this; _classCallCheck(this, Network); _this = _super.call(this); _this._links = []; _this._linkSize = constant(1); _this._linkSizeMin = 1; _this._linkSizeScale = "sqrt"; _this._noDataMessage = false; _this._nodeGroupBy = [accessor("id")]; _this._nodes = []; _this._on["click.shape"] = function (d, i, x, event) { _this._tooltipClass.data([]).render(); if (_this._hover && _this._drawDepth >= _this._groupBy.length - 1) { var id = getNodeId.bind(_assertThisInitialized(_this))(d, i); if (_this._focus && _this._focus === id) { _this.active(false); _this._on.mouseenter.bind(_assertThisInitialized(_this))(d, i, x, event); _this._focus = undefined; _this._zoomToBounds(null); } else { _this.hover(false); var links = _this._linkLookup[id], node = _this._nodeLookup[id]; var filterIds = [id]; var xDomain = [node.x - node.r, node.x + node.r], yDomain = [node.y - node.r, node.y + node.r]; links.forEach(function (l) { filterIds.push(l.id); if (l.x - l.r < xDomain[0]) xDomain[0] = l.x - l.r; if (l.x + l.r > xDomain[1]) xDomain[1] = l.x + l.r; if (l.y - l.r < yDomain[0]) yDomain[0] = l.y - l.r; if (l.y + l.r > yDomain[1]) yDomain[1] = l.y + l.r; }); _this.active(function (h, x) { if (h.source && h.target) return h.source.id === id || h.target.id === id;else return filterIds.includes(getNodeId.bind(_assertThisInitialized(_this))(h, x)); }); _this._focus = id; var t = zoomTransform(_this._container.node()); xDomain = xDomain.map(function (d) { return d * t.k + t.x; }); yDomain = yDomain.map(function (d) { return d * t.k + t.y; }); _this._zoomToBounds([[xDomain[0], yDomain[0]], [xDomain[1], yDomain[1]]]); } } }; _this._on["click.legend"] = function (d, i, x, event) { var ids = _this._id(d); var id = _this._ids(d); id = id[id.length - 1]; if (_this._hover && _this._drawDepth >= _this._groupBy.length - 1) { if (_this._focus && _this._focus === ids) { _this.active(false); _this._focus = undefined; _this._zoomToBounds(null); } else { _this.hover(false); var nodes = ids.map(function (id) { return _this._nodeLookup[id]; }); var filterIds = ["".concat(id)]; var xDomain = [nodes[0].x - nodes[0].r, nodes[0].x + nodes[0].r], yDomain = [nodes[0].y - nodes[0].r, nodes[0].y + nodes[0].r]; nodes.forEach(function (l) { filterIds.push(l.id); if (l.x - l.r < xDomain[0]) xDomain[0] = l.x - l.r; if (l.x + l.r > xDomain[1]) xDomain[1] = l.x + l.r; if (l.y - l.r < yDomain[0]) yDomain[0] = l.y - l.r; if (l.y + l.r > yDomain[1]) yDomain[1] = l.y + l.r; }); _this.active(function (h, x) { if (h.source && h.target) return filterIds.includes(h.source.id) && filterIds.includes(h.target.id);else { var myIds = _this._ids(h, x); return filterIds.includes("".concat(myIds[myIds.length - 1])); } }); _this._focus = ids; var t = zoomTransform(_this._container.node()); xDomain = xDomain.map(function (d) { return d * t.k + t.x; }); yDomain = yDomain.map(function (d) { return d * t.k + t.y; }); _this._zoomToBounds([[xDomain[0], yDomain[0]], [xDomain[1], yDomain[1]]]); } _this._on.mouseenter.bind(_assertThisInitialized(_this))(d, i, x, event); _this._on["mousemove.legend"].bind(_assertThisInitialized(_this))(d, i, x, event); } }; _this._on.mouseenter = function () {}; _this._on["mouseleave.shape"] = function () { _this.hover(false); }; var defaultMouseMove = _this._on["mousemove.shape"]; _this._on["mousemove.shape"] = function (d, i, x, event) { defaultMouseMove(d, i, x, event); var id = getNodeId.bind(_assertThisInitialized(_this))(d, i), links = _this._linkLookup[id] || [], node = _this._nodeLookup[id]; var filterIds = [id]; var xDomain = [node.x - node.r, node.x + node.r], yDomain = [node.y - node.r, node.y + node.r]; links.forEach(function (l) { filterIds.push(l.id); if (l.x - l.r < xDomain[0]) xDomain[0] = l.x - l.r; if (l.x + l.r > xDomain[1]) xDomain[1] = l.x + l.r; if (l.y - l.r < yDomain[0]) yDomain[0] = l.y - l.r; if (l.y + l.r > yDomain[1]) yDomain[1] = l.y + l.r; }); _this.hover(function (h, x) { if (h.source && h.target) return h.source.id === id || h.target.id === id;else return filterIds.includes("".concat(_this._ids(h, x)[_this._drawDepth])); }); }; _this._sizeMin = 5; _this._sizeScale = "sqrt"; _this._shape = constant("Circle"); _this._shapeConfig = assign(_this._shapeConfig, { ariaLabel: function ariaLabel(d, i) { var validSize = _this._size ? ", ".concat(_this._size(d, i)) : ""; return "".concat(_this._drawLabel(d, i)).concat(validSize, "."); }, labelConfig: { duration: 0, fontMin: 1, fontResize: true, labelPadding: 0, textAnchor: "middle", verticalAlign: "middle" }, Path: { fill: "none", label: false, stroke: "#eee" } }); _this._x = accessor("x"); _this._y = accessor("y"); _this._zoom = true; return _this; } /** Extends the draw behavior of the abstract Viz class. @private */ _createClass(Network, [{ key: "_draw", value: function _draw(callback) { var _this2 = this; _get(_getPrototypeOf(Network.prototype), "_draw", this).call(this, callback); var duration = this._duration, height = this._height - this._margin.top - this._margin.bottom, transform = "translate(".concat(this._margin.left, ", ").concat(this._margin.top, ")"), width = this._width - this._margin.left - this._margin.right; var data = this._filteredData.reduce(function (obj, d, i) { obj[_this2._id(d, i)] = d; return obj; }, {}); var nodes = this._nodes.reduce(function (obj, d, i) { obj[getNodeId.bind(_this2)(d, i)] = d; return obj; }, {}); nodes = Array.from(new Set(Object.keys(data).concat(Object.keys(nodes)))).map(function (id, i) { var d = data[id], n = nodes[id]; if (n === undefined) return false; return { __d3plus__: true, data: d || n, i: i, id: id, fx: d !== undefined && !isNaN(_this2._x(d)) ? _this2._x(d) : _this2._x(n), fy: d !== undefined && !isNaN(_this2._y(d)) ? _this2._y(d) : _this2._y(n), node: n, r: _this2._size ? d !== undefined && _this2._size(d) !== undefined ? _this2._size(d) : _this2._size(n) : _this2._sizeMin, shape: d !== undefined && _this2._shape(d) !== undefined ? _this2._shape(d) : _this2._shape(n) }; }).filter(function (n) { return n; }); var nodeLookup = this._nodeLookup = nodes.reduce(function (obj, d) { obj[d.id] = d; return obj; }, {}); var nodeIndices = nodes.map(function (n) { return n.node; }); var links = this._links.map(function (l) { var referenceType = _typeof(l.source); return { size: _this2._linkSize(l), source: referenceType === "number" ? nodes[nodeIndices.indexOf(_this2._nodes[l.source])] : referenceType === "string" ? nodeLookup[l.source] : nodeLookup[l.source.id], target: referenceType === "number" ? nodes[nodeIndices.indexOf(_this2._nodes[l.target])] : referenceType === "string" ? nodeLookup[l.target] : nodeLookup[l.target.id] }; }); this._linkLookup = links.reduce(function (obj, d) { if (!obj[d.source.id]) obj[d.source.id] = []; obj[d.source.id].push(d.target); if (!obj[d.target.id]) obj[d.target.id] = []; obj[d.target.id].push(d.source); return obj; }, {}); var missingCoords = nodes.some(function (n) { return n.fx === undefined || n.fy === undefined; }); if (missingCoords) { var linkStrength = scales.scaleLinear().domain(extent(links, function (d) { return d.size; })).range([0.1, 0.5]); var simulation = forceSimulation().force("link", forceLink(links).id(function (d) { return d.id; }).distance(1).strength(function (d) { return linkStrength(d.size); }).iterations(4)).force("charge", forceManyBody().strength(-1)).stop(); var iterations = 100; var alphaMin = 0.001; var alphaDecay = 1 - Math.pow(alphaMin, 1 / iterations); simulation.velocityDecay(0); simulation.alphaMin(alphaMin); simulation.alphaDecay(alphaDecay); simulation.alphaDecay(0); simulation.nodes(nodes); simulation.tick(iterations).stop(); var nodePositions = nodes.map(function (n) { return [n.vx, n.vy]; }); var angle = 0, cx = 0, cy = 0; if (nodePositions.length === 2) { angle = 100; } else if (nodePositions.length > 2) { var hull = polygonHull(nodePositions); var rect = shapes.largestRect(hull, { verbose: true }); angle = rect.angle; cx = rect.cx; cy = rect.cy; } nodes.forEach(function (n) { var p = shapes.pointRotate([n.vx, n.vy], -1 * (Math.PI / 180 * angle), [cx, cy]); n.fx = p[0]; n.fy = p[1]; }); } var xExtent = extent(nodes.map(function (n) { return n.fx; })), yExtent = extent(nodes.map(function (n) { return n.fy; })); var x = scales.scaleLinear().domain(xExtent).range([0, width]), y = scales.scaleLinear().domain(yExtent).range([0, height]); var nodeRatio = (xExtent[1] - xExtent[0]) / (yExtent[1] - yExtent[0]) || 1, screenRatio = width / height; if (nodeRatio > screenRatio) { var h = height * screenRatio / nodeRatio; y.range([(height - h) / 2, height - (height - h) / 2]); } else { var w = width * nodeRatio / screenRatio; x.range([(width - w) / 2, width - (width - w) / 2]); } nodes.forEach(function (n) { n.x = x(n.fx); n.y = y(n.fy); }); var rExtent = extent(nodes.map(function (n) { return n.r; })); var rMax = this._sizeMax || max([1, min(merge(nodes.map(function (n1) { return nodes.map(function (n2) { return n1 === n2 ? null : shapes.pointDistance([n1.x, n1.y], [n2.x, n2.y]); }); }))) / 2]); var r = scales["scale".concat(this._sizeScale.charAt(0).toUpperCase()).concat(this._sizeScale.slice(1))]().domain(rExtent).range([rExtent[0] === rExtent[1] ? rMax : min([rMax / 2, this._sizeMin]), rMax]), xDomain = x.domain(), yDomain = y.domain(); var xOldSize = xDomain[1] - xDomain[0], yOldSize = yDomain[1] - yDomain[0]; nodes.forEach(function (n) { var size = r(n.r); if (xDomain[0] > x.invert(n.x - size)) xDomain[0] = x.invert(n.x - size); if (xDomain[1] < x.invert(n.x + size)) xDomain[1] = x.invert(n.x + size); if (yDomain[0] > y.invert(n.y - size)) yDomain[0] = y.invert(n.y - size); if (yDomain[1] < y.invert(n.y + size)) yDomain[1] = y.invert(n.y + size); }); var xNewSize = xDomain[1] - xDomain[0], yNewSize = yDomain[1] - yDomain[0]; rMax *= min([xOldSize / xNewSize, yOldSize / yNewSize]); r.range([rExtent[0] === rExtent[1] ? rMax : min([rMax / 2, this._sizeMin]), rMax]); x.domain(xDomain); y.domain(yDomain); var fallbackRadius = (nodeRatio > screenRatio ? width : height) / 2; nodes.forEach(function (n) { n.x = x(n.fx); n.fx = n.x; n.y = y(n.fy); n.fy = n.y; n.r = r(n.r) || fallbackRadius; n.width = n.r * 2; n.height = n.r * 2; }); this._container = this._select.selectAll("svg.d3plus-network").data([0]); this._container = this._container.enter().append("svg").attr("class", "d3plus-network").attr("opacity", 0).attr("width", width).attr("height", height).attr("x", this._margin.left).attr("y", this._margin.top).style("background-color", "transparent").merge(this._container); this._container.transition().duration(duration).attr("opacity", 1).attr("width", width).attr("height", height).attr("x", this._margin.left).attr("y", this._margin.top); var hitArea = this._container.selectAll("rect.d3plus-network-hitArea").data([0]); hitArea.enter().append("rect").attr("class", "d3plus-network-hitArea").merge(hitArea).attr("width", width).attr("height", height).attr("fill", "transparent").on("click", function () { if (_this2._focus) { _this2.active(false); _this2._focus = undefined; _this2._zoomToBounds(null); } }); this._zoomGroup = this._container.selectAll("g.d3plus-network-zoomGroup").data([0]); var parent = this._zoomGroup = this._zoomGroup.enter().append("g").attr("class", "d3plus-network-zoomGroup").merge(this._zoomGroup); var strokeExtent = extent(links, function (d) { return d.size; }); if (strokeExtent[0] !== strokeExtent[1]) { var strokeScale = scales["scale".concat(this._linkSizeScale.charAt(0).toUpperCase()).concat(this._linkSizeScale.slice(1))]().domain(strokeExtent).range([this._linkSizeMin, r.range()[0]]); links.forEach(function (link) { link.size = strokeScale(link.size); }); } var linkConfig = configPrep.bind(this)(this._shapeConfig, "edge", "Path"); delete linkConfig.on; this._shapes.push(new shapes.Path().config(linkConfig).strokeWidth(function (d) { return d.size; }).activeStyle({ "stroke-width": function strokeWidth(d) { return d.size; } }).d(function (d) { return "M".concat(d.source.x, ",").concat(d.source.y, " ").concat(d.target.x, ",").concat(d.target.y); }).data(links).select(elem("g.d3plus-network-links", { parent: parent, duration: duration, enter: { transform: transform }, update: { transform: transform } }).node()).render()); var shapeConfig = { label: function label(d) { return nodes.length <= _this2._dataCutoff || _this2._hover && _this2._hover(d) || _this2._active && _this2._active(d) ? _this2._drawLabel(d.data || d.node, d.i) : false; }, select: elem("g.d3plus-network-nodes", { parent: parent, duration: duration, enter: { transform: transform }, update: { transform: transform } }).node() }; nest().key(function (d) { return d.shape; }).entries(nodes).forEach(function (d) { _this2._shapes.push(new shapes[d.key]().config(configPrep.bind(_this2)(_this2._shapeConfig, "shape", d.key)).config(shapeConfig).config(shapeConfig[d.key] || {}).data(d.values).render()); }); return this; } /** @memberof Network @desc If *value* is specified, sets the hover method to the specified function and returns the current class instance. @param {Function} [*value*] @chainable */ }, { key: "hover", value: function hover(_) { this._hover = _; if (this._nodes.length < this._dataCutoff) { this._shapes.forEach(function (s) { return s.hover(_); }); if (this._legend) this._legendClass.hover(_); } return this; } /** @memberof Network @desc A predefined *Array* of edges that connect each object passed to the [node](#Network.node) method. The `source` and `target` keys in each link need to map to the nodes in one of three ways: 1. The index of the node in the nodes array (as in [this](http://d3plus.org/examples/d3plus-network/getting-started/) example). 2. The actual node *Object* itself. 3. A *String* value matching the `id` of the node. The value passed should either be an *Array* of data or a *String* representing a filepath or URL to be loaded. An optional formatting function can be passed as a second argument to this method. This custom function will be passed the data that has been loaded, as long as there are no errors. This function should return the final links *Array*. @param {Array|String} *links* = [] @param {Function} [*formatter*] @chainable */ }, { key: "links", value: function links(_, f) { if (arguments.length) { addToQueue.bind(this)(_, f, "links"); return this; } return this._links; } /** @memberof Network @desc Defines the thickness of the links connecting each node. The value provided can be either a pixel Number to be used for all links, or an accessor function that returns a specific data value to be used in an automatically calculated linear scale. @param {Function|Name} [*value* = 1] @chainable */ }, { key: "linkSize", value: function linkSize(_) { return arguments.length ? (this._linkSize = typeof _ === "function" ? _ : constant(_), this) : this._linkSize; } /** @memberof Network @desc Defines the minimum pixel stroke width used in link sizing. @param {Number} [*value* = 2] @chainable */ }, { key: "linkSizeMin", value: function linkSizeMin(_) { return arguments.length ? (this._linkSizeMin = _, this) : this._linkSizeMin; } /** @memberof Network @desc Sets the specific type of [continuous d3-scale](https://github.com/d3/d3-scale#continuous-scales) used when calculating the pixel size of links in the network. @param {String} [*value* = "sqrt"] @chainable */ }, { key: "linkSizeScale", value: function linkSizeScale(_) { return arguments.length ? (this._linkSizeScale = _, this) : this._linkSizeScale; } /** @memberof Network @desc If *value* is specified, sets the node group accessor(s) to the specified string, function, or array of values and returns the current class instance. This method overrides the default .groupBy() function from being used with the data passed to .nodes(). If *value* is not specified, returns the current node group accessor. @param {String|Function|Array} [*value* = "id"] @chainable */ }, { key: "nodeGroupBy", value: function nodeGroupBy(_) { var _this3 = this; if (!arguments.length) return this._nodeGroupBy; if (!(_ instanceof Array)) _ = [_]; return this._nodeGroupBy = _.map(function (k) { if (typeof k === "function") return k;else { if (!_this3._aggs[k]) { _this3._aggs[k] = function (a, c) { var v = Array.from(new Set(a.map(c))); return v.length === 1 ? v[0] : v; }; } return accessor(k); } }), this; } /** @memberof Network @desc The list of nodes to be used for drawing the network. The value passed should either be an *Array* of data or a *String* representing a filepath or URL to be loaded. Additionally, a custom formatting function can be passed as a second argument to this method. This custom function will be passed the data that has been loaded, as long as there are no errors. This function should return the final node *Array*. @param {Array|String} *nodes* = [] @param {Function} [*formatter*] @chainable */ }, { key: "nodes", value: function nodes(_, f) { if (arguments.length) { addToQueue.bind(this)(_, f, "nodes"); return this; } return this._nodes; } /** @memberof Network @desc If *value* is specified, sets the size accessor to the specified function or data key and returns the current class instance. If *value* is not specified, returns the current size accessor. @param {Function|String} [*value*] @chainable */ }, { key: "size", value: function size(_) { return arguments.length ? (this._size = typeof _ === "function" || !_ ? _ : accessor(_), this) : this._size; } /** @memberof Network @desc Defines the maximum pixel radius used in size scaling. By default, the maximum size is determined by half the distance of the two closest nodes. @param {Number} [*value*] @chainable */ }, { key: "sizeMax", value: function sizeMax(_) { return arguments.length ? (this._sizeMax = _, this) : this._sizeMax; } /** @memberof Network @desc Defines the minimum pixel radius used in size scaling. @param {Number} [*value* = 5] @chainable */ }, { key: "sizeMin", value: function sizeMin(_) { return arguments.length ? (this._sizeMin = _, this) : this._sizeMin; } /** @memberof Network @desc Sets the specific type of [continuous d3-scale](https://github.com/d3/d3-scale#continuous-scales) used when calculating the pixel size of nodes in the network. @param {String} [*value* = "sqrt"] @chainable */ }, { key: "sizeScale", value: function sizeScale(_) { return arguments.length ? (this._sizeScale = _, this) : this._sizeScale; } /** @memberof Network @desc If *value* is specified, sets the x accessor to the specified function or string matching a key in the data and returns the current class instance. The data passed to .data() takes priority over the .nodes() data array. If *value* is not specified, returns the current x accessor. By default, the x and y positions are determined dynamically based on default force layout properties. @param {Function|String} [*value*] @chainable */ }, { key: "x", value: function x(_) { if (arguments.length) { if (typeof _ === "function") this._x = _;else { this._x = accessor(_); if (!this._aggs[_]) this._aggs[_] = mean; } return this; } else return this._x; } /** @memberof Network @desc If *value* is specified, sets the y accessor to the specified function or string matching a key in the data and returns the current class instance. The data passed to .data() takes priority over the .nodes() data array. If *value* is not specified, returns the current y accessor. By default, the x and y positions are determined dynamically based on default force layout properties. @param {Function|String} [*value*] @chainable */ }, { key: "y", value: function y(_) { if (arguments.length) { if (typeof _ === "function") this._y = _;else { this._y = accessor(_); if (!this._aggs[_]) this._aggs[_] = mean; } return this; } else return this._y; } }]); return Network; }(Viz); export { Network as default };