diff --git a/demos/gallery-case1.html b/demos/gallery-case1.html index e5822f05d7..d6966b6a91 100644 --- a/demos/gallery-case1.html +++ b/demos/gallery-case1.html @@ -58,7 +58,7 @@ graph = new G6.Graph({ id: 'mountNode', // dom id fitView: 'autoZoom', - plugins: [new Plugin({max_iteration: 1000, kg: 2, kr: 40, prev_overlapping: true}), + plugins: [new Plugin({maxIteration: 1000, kg: 2, kr: 40, prevOverlapping: true}), new FisheyePlugin({radius: 200}), nodeSizeMapper, nodeColorMapper ], diff --git a/demos/gallery-graphanalyzer.html b/demos/gallery-graphanalyzer.html index 2af038134c..2e56ba1435 100644 --- a/demos/gallery-graphanalyzer.html +++ b/demos/gallery-graphanalyzer.html @@ -25,11 +25,11 @@ const Mapper = G6.Plugins['tool.mapper']; const MaxSpanningForestPlugin = G6.Plugins['template.maxSpanningForest']; //the instances of plugins - const maxSpanningForest = new MaxSpanningForestPlugin({ + let maxSpanningForest = new MaxSpanningForestPlugin({ layoutCfg: { - max_iteration: 600, + maxIteration: 600, kg: 10, - prev_overlapping: true, + prevOverlapping: true, onLayoutComplete: function () { const minimap = document.getElementById('minimap'); const legend = document.getElementById('legend'); @@ -38,24 +38,34 @@ } } }); - const nodeSizeMapper = new Mapper('node', 'userview', 'size', [20, 50], { + let nodeSizeMapper = new Mapper('node', 'userview', 'size', [20, 50], { legendCfg: { containerId: 'legend', title: 'UV', - layout: 'horizontal' + layout: 'horizontal', + // formatter: function(num) { + // console.log(num); + // num = num * num; + // if (num >= 100000000) { + // return num / 100000000 + '亿'; + // } if (num >= 10000) { + // return num / 10000 + '万'; + // } + // return num; + // } } }); - const edgeSizeMapper = new Mapper('edge', 'userview', 'size', [1, 16], { + let edgeSizeMapper = new Mapper('edge', 'userview', 'size', [1, 16], { legendCfg: null }); - const nodeColorMapper = new Mapper('node', 'stayTime', 'color', ['#BAE7FF', '#1890FF', '#0050B3'], { + let nodeColorMapper = new Mapper('node', 'stayTime', 'color', ['#BAE7FF', '#1890FF', '#0050B3'], { legendCfg: { containerId: 'legend', title: 'Stay Time', layout: 'horizontal' } }); - const minimapPlugin = new G6.Plugins['tool.minimap']({ + let minimapPlugin = new G6.Plugins['tool.minimap']({ container: 'minimap', width: 180, height: 120 @@ -67,14 +77,14 @@ id: 'mountNode', // dom id fitView: 'autoZoom', plugins: [ - maxSpanningForest, nodeSizeMapper, nodeColorMapper, edgeSizeMapper, minimapPlugin + maxSpanningForest, nodeColorMapper, minimapPlugin, edgeSizeMapper, nodeSizeMapper, ], modes: { default: ['panCanvas', 'wheelZoom'] }, height: 500, }); - graph.read(data); + graph.read(Util.cloneDeep(data)); const minimap = document.getElementById('minimap'); const legend = document.getElementById('legend'); if (minimap !== undefined) minimap.style.display = 'none'; diff --git a/demos/plugin-fisheye.html b/demos/plugin-fisheye.html index 5454c6fba3..1928c23934 100644 --- a/demos/plugin-fisheye.html +++ b/demos/plugin-fisheye.html @@ -32,13 +32,13 @@ nodes.forEach((node, index) => { if (parseInt(index / col) % 2 === 0) { node.x = (col - index % col) * hgap; - node.ori_x = node.x; + node.oriX = node.x; } else { node.x = index % col * hgap + hgap; - node.ori_x = node.x; + node.oriX = node.x; } node.y = parseInt(index / col) * vgap + vgap / 2; - node.ori_y = node.y; + node.oriY = node.y; }); } }); diff --git a/demos/plugin-forceAtlas2.html b/demos/plugin-forceAtlas2.html index e92c445257..92d8c21ebe 100644 --- a/demos/plugin-forceAtlas2.html +++ b/demos/plugin-forceAtlas2.html @@ -15,19 +15,19 @@ diff --git a/demos/plugin-mapper.html b/demos/plugin-mapper.html index 8073e5c1b0..b1ad47397f 100644 --- a/demos/plugin-mapper.html +++ b/demos/plugin-mapper.html @@ -20,6 +20,7 @@ scale: 0.5 } }); + const nodeSizeMapper = new Mapper('node', 'weight', 'size', [20, 40]); const edgeSizeMapper = new Mapper('edge', 'weight', 'size', [2, 20], { legendCfg: null }); @@ -28,21 +29,25 @@ id: 'node1', x: 100, y: 200, + weight: 10, class: 'class_1' }, { id: 'node2', x: 300, y: 200, + weight: 2, class: 'class_1' }, { id: 'node3', x: 100, y: 100, + weight: 15, class: 'class_2' }, { id: 'node4', x: 300, y: 100, + weight: 5, class: 'class_2' }], edges: [{ @@ -61,7 +66,7 @@ }; const graph = new G6.Graph({ id: 'mountNode', // dom id - plugins: [nodeColorMapper, edgeSizeMapper], + plugins: [nodeColorMapper, edgeSizeMapper, nodeSizeMapper], height: 1000, }); graph.read(data); diff --git a/demos/quick-net-svg.html b/demos/quick-net-svg.html deleted file mode 100644 index b94da8cbaa..0000000000 --- a/demos/quick-net-svg.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - 快速上手-网图-SVG - - - -
- - - - diff --git a/package.json b/package.json index 89b81de6cb..1dc554741c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6", - "version": "2.1.0-beta", + "version": "2.1.0-beta.3", "description": "graph visualization frame work", "main": "build/g6.js", "homepage": "https://github.com/antvis/g6", @@ -97,7 +97,7 @@ "screenshot": "node ./bin/screenshot.js", "start": "npm run dev", "test": "torch --compile --renderer --recursive ./test/unit", - "test-live": "torch --compile --interactive --watch --recursive ./test/unit/plugins/tool.mapper-spec.js", + "test-live": "torch --compile --interactive --watch --recursive ./test/unit/plugins/layout.forceAtlas2-spec.js", "watch": "webpack --config webpack-dev.config.js", "win-dev": "node ./bin/win-dev.js" }, diff --git a/plugins/layout.forceAtlas2/BlobBuilder.js b/plugins/layout.forceAtlas2/BlobBuilder.js deleted file mode 100644 index 679cdb444b..0000000000 --- a/plugins/layout.forceAtlas2/BlobBuilder.js +++ /dev/null @@ -1,128 +0,0 @@ -/* BlobBuilder.js - * A complete BlobBuilder shim - * By Eli Grey - * License: MIT/X11 - */ - -self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || function(view) { - - let - get_class = function(object) { - return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1]; - }, - FakeBlobBuilder = view.BlobBuilder = function() {}, - FakeBlob = view.Blob = function(data, type) { - this.data = data; - this.size = data.length; - this.type = type; - }, - FBB_proto = FakeBlobBuilder.prototype = [], - FB_proto = FakeBlob.prototype, - FileReaderSync = view.FileReaderSync, - FileException = function(type) { - this.code = this[this.name = type]; - }, - file_ex_codes = ( - 'NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR ' + - 'NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR' - ).split(' '), - file_ex_code = file_ex_codes.length, - URL = view.URL = view.URL || view.webkitURL || view, - real_create_object_url, - real_revoke_object_url, - btoa = view.btoa, - ArrayBuffer = view.ArrayBuffer, - can_apply_typed_arrays = false, - can_apply_typed_arrays_test = function(pass) { - can_apply_typed_arrays = !pass; - }; - while (file_ex_code--) { - FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1; - } - try { - if (ArrayBuffer) { - can_apply_typed_arrays_test.apply(0, new Uint8Array(1)); - } - } catch (ex) {} - if (!URL.createObjectURL) { - URL = {}; - } - real_create_object_url = URL.createObjectURL; - real_revoke_object_url = URL.revokeObjectURL; - URL.createObjectURL = function(blob) { - let type = blob.type; - if (type === null) { - type = 'application/octet-stream'; - } - if (blob instanceof FakeBlob) { - if (btoa) { - return 'data:' + type + ';base64,' + btoa(blob.data); - } - return 'data:' + type + ',' + encodeURIComponent(blob.data); - - } else if (real_create_object_url) { - return real_create_object_url.call(URL, blob); - } - }; - URL.revokeObjectURL = function(object_url) { - if (object_url.substring(0, 5) !== 'data:' && real_revoke_object_url) { - real_revoke_object_url.call(URL, object_url); - } - }; - FBB_proto.append = function(data /* , endings*/) { - const bb = this; - // decode data to a binary string - if (ArrayBuffer && data instanceof ArrayBuffer) { - if (can_apply_typed_arrays) { - bb.push(String.fromCharCode.apply(String, new Uint8Array(data))); - } else { - let - str = '', - buf = new Uint8Array(data), - i = 0, - buf_len = buf.length; - for (; i < buf_len; i++) { - str += String.fromCharCode(buf[i]); - } - } - } else if (get_class(data) === 'Blob' || get_class(data) === 'File') { - if (FileReaderSync) { - const fr = new FileReaderSync(); - bb.push(fr.readAsBinaryString(data)); - } else { - // async FileReader won't work as BlobBuilder is sync - throw new FileException('NOT_READABLE_ERR'); - } - } else if (data instanceof FakeBlob) { - bb.push(data.data); - } else { - if (typeof data !== 'string') { - data += ''; // convert unsupported types to strings - } - // decode UTF-16 to binary string - bb.push(unescape(encodeURIComponent(data))); - } - }; - FBB_proto.getBlob = function(type) { - if (!arguments.length) { - type = null; - } - return new FakeBlob(this.join(''), type); - }; - FBB_proto.toString = function() { - return '[object BlobBuilder]'; - }; - FB_proto.slice = function(start, end, type) { - const args = arguments.length; - if (args < 3) { - type = null; - } - return new FakeBlob( - this.data.slice(start, args > 1 ? end : this.data.length), type - ); - }; - FB_proto.toString = function() { - return '[object Blob]'; - }; - return FakeBlobBuilder; -}(self); diff --git a/plugins/layout.forceAtlas2/README.md b/plugins/layout.forceAtlas2/README.md index e9d4b5d956..04c93dbada 100644 --- a/plugins/layout.forceAtlas2/README.md +++ b/plugins/layout.forceAtlas2/README.md @@ -7,17 +7,17 @@ ForceAtlas2(https://medialab.sciencespo.fr/publications/Jacomy_Heymann_Venturini - mode: 'normal'/'linlog' 'normal': normal layout 'linlog': the cluster will be more compact -- pre_overlapping: true/false. +- preOverlapping: true/false. true: prevents node overlapping, result in non-node-overlapping layout false: allows node overlapping. -- dissuade_hubs: true/false. whether active the dissuade hub mode +- dissuadeHubs: true/false. whether active the dissuade hub mode true: grant authorities(nodes with a high indegree) a more central position than hubs(nodes with a high outdegree) false: without dissuade hub mode -- barnes_hut: true/false. Whether active the barnes hut optimization on computing repulsive foces. We implemented the paper 'A hierarchical O(N log N) force-calculation algorithm strategy'(https://www.nature.com/articles/324446a0). +- barnesHut: true/false. Whether active the barnes hut optimization on computing repulsive foces. We implemented the paper 'A hierarchical O(N log N) force-calculation algorithm strategy'(https://www.nature.com/articles/324446a0). - ks: controls the global velocity. Default: 0.1 - ksmax: the max global velocity. Default: 10 - tao: the tolerance for the global swinging. Default: 0.1 -- max_iteration: the iteration to terminate the algorithm. We suggest 700 for the graph with less than 50 nodes; 1000 for 100 nodes; Upper than 1500 for more nodes. +- maxIteration: the iteration to terminate the algorithm. We suggest 700 for the graph with less than 50 nodes; 1000 for 100 nodes; Upper than 1500 for more nodes. ## use @@ -26,18 +26,18 @@ simple use. ```js $.getJSON('../../test/fixtures/viralMarketing.json', data => { const Plugin = G6.Plugins['layout.forceAtlas2']; - const layout_params = { - max_iteration: 1500, - prev_overlapping: true, + const layoutParams = { + maxIteration: 1500, + prevOverlapping: true, kr: 15, mode: 'normal', - barnes_hut: false, // may be counter-productive on small networks + barnesHut: false, // may be counter-productive on small networks ks: 0.1, - dissuade_hubs: false + dissuadeHubs: false }; graph = new G6.Graph({ id: 'mountNode', // dom id - plugins: [new Plugin(layout_params)], + plugins: [new Plugin(layoutParams)], height: 1000, }); graph.read(data); diff --git a/plugins/layout.forceAtlas2/body.js b/plugins/layout.forceAtlas2/body.js index 847d618fc9..d741d894cc 100644 --- a/plugins/layout.forceAtlas2/body.js +++ b/plugins/layout.forceAtlas2/body.js @@ -77,14 +77,14 @@ class Body { } // returns a new body add(bo) { - const nenw_mass = this.mass + bo.mass; - const x = (this.rx * this.mass + bo.rx * bo.mass) / nenw_mass; - const y = (this.ry * this.mass + bo.ry * bo.mass) / nenw_mass; + const nenwMass = this.mass + bo.mass; + const x = (this.rx * this.mass + bo.rx * bo.mass) / nenwMass; + const y = (this.ry * this.mass + bo.ry * bo.mass) / nenwMass; const dg = this.degree + bo.degree; const params = { rx: x, ry: y, - mass: nenw_mass, + mass: nenwMass, degree: dg }; return new Body(params); diff --git a/plugins/layout.forceAtlas2/layout.js b/plugins/layout.forceAtlas2/layout.js index b87f160dd8..b997886f87 100644 --- a/plugins/layout.forceAtlas2/layout.js +++ b/plugins/layout.forceAtlas2/layout.js @@ -54,7 +54,7 @@ class Layout { * whether preventing the node overlapping * @type {boolean} */ - prev_overlapping: false, + prevOverlapping: false, /** * whether active the dissuade hub mode @@ -62,19 +62,19 @@ class Layout { * a more central position than hubs (nodes with a high outdegree) * @type {boolean} */ - dissuade_hubs: false, + dissuadeHubs: false, /** * whether active the barnes hut optimization on computing repulsive forces * @type {boolean} */ - barnes_hut: false, + barnesHut: false, /** * the max iteration number * @type {number} */ - max_iteration: 1500, + maxIteration: 1500, /** * control the global velocity @@ -111,18 +111,18 @@ class Layout { kr, kg, mode, - prev_overlapping, - dissuade_hubs, - barnes_hut, - max_iteration, + prevOverlapping, + dissuadeHubs, + barnesHut, + maxIteration, ks, ksmax, tao, onLayoutComplete } = this; - if (!barnes_hut && nodes.length > 300) barnes_hut = true; - else if (barnes_hut && nodes.length <= 300) barnes_hut = false; + if (!barnesHut && nodes.length > 300) barnesHut = true; + else if (barnesHut && nodes.length <= 300) barnesHut = false; const width = this.width ? this.width : graph.getWidth(); const height = this.height ? this.height : graph.getHeight(); @@ -142,10 +142,10 @@ class Layout { kr, kg, mode, - prev_overlapping, - dissuade_hubs, - barnes_hut, - max_iteration, + prevOverlapping, + dissuadeHubs, + barnesHut, + maxIteration, ks, ksmax, tao, @@ -153,38 +153,39 @@ class Layout { widths }; - // a loading dom before worker + // a loading dom before worker const loading = document.createElement('div'); loading.id = 'loading'; loading.style.setProperty('background-color', '#fff'); loading.style.setProperty('position', 'absolute'); - const parent = graph.getGraphContainer(); - loading.style.setProperty('width', parent.width() + 'px'); - loading.style.setProperty('height', parent.height() + 'px'); - loading.style.setProperty('margin-top', (-parent.height()) + 'px'); + const parent = graph.getGraphContainer().parentNode; + loading.style.setProperty('width', parent.offsetWidth + 'px'); + loading.style.setProperty('height', parent.offsetHeight + 'px'); + loading.style.setProperty('margin-top', (-parent.offsetHeight) + 'px'); + loading.style.zIndex = 999; parent.appendChild(loading); // the loading image - const loading_img = document.createElement('img'); - loading_img.src = 'https://gw.alipayobjects.com/zos/rmsportal/mnEmjOmrHbghTsZNeTmI.gif'; - loading_img.style.setProperty('width', 120 + 'px'); - loading_img.style.setProperty('height', 120 + 'px'); - const Cw = 120; - const Pw = parent.width(); + const loadingImg = document.createElement('img'); + loadingImg.src = 'https://gw.alipayobjects.com/zos/rmsportal/mnEmjOmrHbghTsZNeTmI.gif'; + loadingImg.style.setProperty('width', 200 + 'px'); + loadingImg.style.setProperty('height', 200 + 'px'); + const Cw = loadingImg.offsetWidth; + const Pw = loading.offsetWidth; const left = (Pw - Cw) / 2; - loading_img.style.setProperty('margin-left', left + 'px'); - const Ch = 120; - const Ph = parent.height(); + loadingImg.style.setProperty('margin-left', left + 'px'); + const Ch = loadingImg.offsetHeight; + const Ph = loading.offsetHeight; const top = (Ph - Ch) / 2; - loading_img.style.setProperty('margin-top', top + 'px'); - loading.appendChild(loading_img); + loadingImg.style.setProperty('margin-top', top + 'px'); + loading.appendChild(loadingImg); const worker = new Worker();// { type: 'module' } worker.postMessage(obj); worker.onmessage = function(event) { this.nodes = event.data; - const graph_nodes = graph.getNodes(); + const graphNodes = graph.getNodes(); for (let i = 0; i < size; i += 1) { - const model = graph_nodes[i].getModel(); + const model = graphNodes[i].getModel(); model.x = this.nodes[i].x; model.y = this.nodes[i].y; } diff --git a/plugins/layout.forceAtlas2/layout.worker.js b/plugins/layout.forceAtlas2/layout.worker.js index eeb1619ad7..482cb0b68e 100644 --- a/plugins/layout.forceAtlas2/layout.worker.js +++ b/plugins/layout.forceAtlas2/layout.worker.js @@ -8,10 +8,10 @@ onmessage = function(event) { kr, kg, mode, - prev_overlapping, - dissuade_hubs, - barnes_hut, - max_iteration, + prevOverlapping, + dissuadeHubs, + barnesHut, + maxIteration, ks, ksmax, tao, @@ -30,8 +30,6 @@ onmessage = function(event) { for (let i = 0; i < size; i += 1) { idmap[nodes[i].id] = i; degrees[i] = 0; - // nodes[i].index = i; - // nodes[i].degree = 0; nodes[i].x = Math.random() * 1000; nodes[i].y = Math.random() * 1000; } @@ -45,24 +43,20 @@ onmessage = function(event) { node2 = nodes[j]; } } - // // const node1 = graph.find(edges[i].source).getModel(); - // // const node2 = graph.find(edges[i].target).getModel(); - // nodes[node1.index].degree += 1; - // nodes[node2.index].degree += 1; degrees[idmap[node1.id]] += 1; degrees[idmap[node2.id]] += 1; } - const kr_prime = 100; - let iter = max_iteration; - const prevo_iter = 50; + const krPrime = 100; + let iter = maxIteration; + const prevoIter = 50; let Forces = []; - const pre_Forces = []; + const preForces = []; for (let i = 0; i < size; i += 1) { Forces[2 * i] = 0; Forces[2 * i + 1] = 0; - if (barnes_hut) { + if (barnesHut) { let params = { id: i, rx: nodes[i].x, @@ -78,22 +72,22 @@ onmessage = function(event) { do { for (let i = 0; i < size; i += 1) { - pre_Forces[2 * i] = Forces[2 * i]; - pre_Forces[2 * i + 1] = Forces[2 * i + 1]; + preForces[2 * i] = Forces[2 * i]; + preForces[2 * i + 1] = Forces[2 * i + 1]; Forces[2 * i] = 0; Forces[2 * i + 1] = 0; } // // attractive forces, existing on every actual edge - Forces = getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths, idmap, degrees); + Forces = getAttrForces(nodes, edges, size, esize, prevOverlapping, dissuadeHubs, mode, iter, prevoIter, Forces, widths, idmap, degrees); // // // repulsive forces and Gravity, existing on every node pair - // // // if prev_overlapping, using the no-optimized method in the last prevo_iter instead. - if (barnes_hut && ((prev_overlapping && iter > prevo_iter) || !prev_overlapping)) { - Forces = getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, bodies, degrees); + // // // if prevOverlapping, using the no-optimized method in the last prevoIter instead. + if (barnesHut && ((prevOverlapping && iter > prevoIter) || !prevOverlapping)) { + Forces = getOptRepGraForces(nodes, edges, size, esize, prevOverlapping, dissuadeHubs, mode, iter, prevoIter, Forces, kr, krPrime, kg, center, bodies, degrees); } else { - Forces = getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths, degrees); + Forces = getRepGraForces(nodes, edges, size, esize, prevOverlapping, dissuadeHubs, mode, iter, prevoIter, Forces, kr, krPrime, kg, center, widths, degrees); } // // update the positions - const res = updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao, degrees); + const res = updatePos(size, nodes, Forces, preForces, SG, ks, ksmax, tao, degrees); nodes = res[0]; SG = res[1]; iter -= 1; @@ -101,81 +95,75 @@ onmessage = function(event) { self.postMessage(nodes); }; -function getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths, idmap, degrees) { +function getAttrForces(nodes, edges, size, esize, prevOverlapping, dissuadeHubs, mode, iter, prevoIter, Forces, widths, idmap, degrees) { for (let i = 0; i < esize; i += 1) { - // const source_node = graph.find(edges[i].source).getModel(); - // const target_node = graph.find(edges[i].target).getModel(); - let source_node; - let target_node; - let source_idx; - let target_idx; + // const sourceNode = graph.find(edges[i].source).getModel(); + // const targetNode = graph.find(edges[i].target).getModel(); + let sourceNode; + let targetNode; + let sourceIdx; + let targetIdx; for (let j = 0; j < size; j += 1) { if (nodes[j].id === edges[i].source) { - source_node = nodes[j]; - source_idx = j; + sourceNode = nodes[j]; + sourceIdx = j; } else if (nodes[j].id === edges[i].target) { - target_node = nodes[j]; - target_idx = j; + targetNode = nodes[j]; + targetIdx = j; } } - let dir = [ target_node.x - source_node.x, target_node.y - source_node.y ]; - let eucli_dis = Math.hypot(dir[0], dir[1]); - eucli_dis = eucli_dis < 0.0001 ? 0.0001 : eucli_dis; - dir[0] = dir[0] / eucli_dis; - dir[1] = dir[1] / eucli_dis; + let dir = [ targetNode.x - sourceNode.x, targetNode.y - sourceNode.y ]; + let eucliDis = Math.hypot(dir[0], dir[1]); + eucliDis = eucliDis < 0.0001 ? 0.0001 : eucliDis; + dir[0] = dir[0] / eucliDis; + dir[1] = dir[1] / eucliDis; // the force - if (prev_overlapping && iter < prevo_iter) eucli_dis = eucli_dis - widths[source_idx] - widths[target_idx]; - let Fa1 = eucli_dis; + if (prevOverlapping && iter < prevoIter) eucliDis = eucliDis - widths[sourceIdx] - widths[targetIdx]; + let Fa1 = eucliDis; let Fa2 = Fa1; if (mode === 'linlog') { - Fa1 = Math.log(1 + eucli_dis); + Fa1 = Math.log(1 + eucliDis); Fa2 = Fa1; } - if (dissuade_hubs) { - // Fa1 = eucli_dis / source_node.degree; - // Fa2 = eucli_dis / target_node.degree; - Fa1 = eucli_dis / degrees[source_idx]; - Fa2 = eucli_dis / degrees[target_idx]; + if (dissuadeHubs) { + Fa1 = eucliDis / degrees[sourceIdx]; + Fa2 = eucliDis / degrees[targetIdx]; } - if (prev_overlapping && iter < prevo_iter && eucli_dis <= 0) { + if (prevOverlapping && iter < prevoIter && eucliDis <= 0) { Fa1 = 0; Fa2 = 0; - } else if (prev_overlapping && iter < prevo_iter && eucli_dis > 0) { - Fa1 = eucli_dis; - Fa2 = eucli_dis; + } else if (prevOverlapping && iter < prevoIter && eucliDis > 0) { + Fa1 = eucliDis; + Fa2 = eucliDis; } - // Forces[2 * source_node.index] += Fa1 * dir[0]; - // Forces[2 * target_node.index] -= Fa2 * dir[0]; - // Forces[2 * source_node.index + 1] += Fa1 * dir[1]; - // Forces[2 * target_node.index + 1] -= Fa2 * dir[1]; - Forces[2 * idmap[source_node.id]] += Fa1 * dir[0]; - Forces[2 * idmap[target_node.id]] -= Fa2 * dir[0]; - Forces[2 * idmap[source_node.id] + 1] += Fa1 * dir[1]; - Forces[2 * idmap[target_node.id] + 1] -= Fa2 * dir[1]; + Forces[2 * idmap[sourceNode.id]] += Fa1 * dir[0]; + Forces[2 * idmap[targetNode.id]] -= Fa2 * dir[0]; + Forces[2 * idmap[sourceNode.id] + 1] += Fa1 * dir[1]; + Forces[2 * idmap[targetNode.id] + 1] -= Fa2 * dir[1]; dir = null; } return Forces; } -function getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths, degrees) { +function getRepGraForces(nodes, edges, size, esize, prevOverlapping, dissuadeHubs, mode, iter, prevoIter, Forces, kr, krPrime, kg, center, widths, degrees) { for (let i = 0; i < size; i += 1) { for (let j = i + 1; j < size; j += 1) { let dir = [ nodes[j].x - nodes[i].x, nodes[j].y - nodes[i].y ]; - let eucli_dis = Math.hypot(dir[0], dir[1]); - eucli_dis = eucli_dis < 0.0001 ? 0.0001 : eucli_dis; - dir[0] = dir[0] / eucli_dis; - dir[1] = dir[1] / eucli_dis; + let eucliDis = Math.hypot(dir[0], dir[1]); + eucliDis = eucliDis < 0.0001 ? 0.0001 : eucliDis; + dir[0] = dir[0] / eucliDis; + dir[1] = dir[1] / eucliDis; - if (prev_overlapping && iter < prevo_iter) eucli_dis = eucli_dis - widths[i] - widths[j]; + if (prevOverlapping && iter < prevoIter) eucliDis = eucliDis - widths[i] - widths[j]; - let Fr = kr * (degrees[i] + 1) * (degrees[j] + 1) / eucli_dis; + let Fr = kr * (degrees[i] + 1) * (degrees[j] + 1) / eucliDis; - if (prev_overlapping && iter < prevo_iter && eucli_dis < 0) { - Fr = kr_prime * (degrees[i] + 1) * (degrees[j] + 1); - } else if (prev_overlapping && iter < prevo_iter && eucli_dis === 0) { + if (prevOverlapping && iter < prevoIter && eucliDis < 0) { + Fr = krPrime * (degrees[i] + 1) * (degrees[j] + 1); + } else if (prevOverlapping && iter < prevoIter && eucliDis === 0) { Fr = 0; - } else if (prev_overlapping && iter < prevo_iter && eucli_dis > 0) { - Fr = kr * (degrees[i] + 1) * (degrees[j] + 1) / eucli_dis; + } else if (prevOverlapping && iter < prevoIter && eucliDis > 0) { + Fr = kr * (degrees[i] + 1) * (degrees[j] + 1) / eucliDis; } Forces[2 * i] -= Fr * dir[0]; Forces[2 * j] += Fr * dir[0]; @@ -186,9 +174,9 @@ function getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_h // gravity let dir = [ nodes[i].x - center.x, nodes[i].y - center.y ]; - const eucli_dis = Math.hypot(dir[0], dir[1]); - dir[0] = dir[0] / eucli_dis; - dir[1] = dir[1] / eucli_dis; + const eucliDis = Math.hypot(dir[0], dir[1]); + dir[0] = dir[0] / eucliDis; + dir[1] = dir[1] / eucliDis; const Fg = kg * (degrees[i] + 1); Forces[2 * i] -= Fg * dir[0]; Forces[2 * i + 1] -= Fg * dir[1]; @@ -197,7 +185,7 @@ function getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_h return Forces; } -function getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, ct, bodies, degrees) { +function getOptRepGraForces(nodes, edges, size, esize, prevOverlapping, dissuadeHubs, mode, iter, prevoIter, Forces, kr, krPrime, kg, ct, bodies, degrees) { let minx = 9e10, maxx = -9e10, miny = 9e10, @@ -212,75 +200,75 @@ function getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuad let width = Math.max(maxx - minx, maxy - miny); - let quad_params = { + let quadParams = { xmid: (maxx + minx) / 2, ymid: (maxy + miny) / 2, length: width, - mass_center: ct, + massCenter: ct, mass: size }; - let quad = new Quad(quad_params); - let quad_tree = new QuadTree(quad); + let quad = new Quad(quadParams); + let quadTree = new QuadTree(quad); // build the tree, insert the nodes(quads) into the tree for (let i = 0; i < size; i += 1) { - if (bodies[i].in(quad)) quad_tree.insert(bodies[i]); + if (bodies[i].in(quad)) quadTree.insert(bodies[i]); } // update the repulsive forces and the gravity. for (let i = 0; i < size; i += 1) { bodies[i].resetForce(); - quad_tree.updateForce(bodies[i]); + quadTree.updateForce(bodies[i]); Forces[2 * i] -= bodies[i].fx; Forces[2 * i + 1] -= bodies[i].fy; // gravity let dir = [ nodes[i].x - ct.x, nodes[i].y - ct.y ]; - let eucli_dis = Math.hypot(dir[0], dir[1]); - eucli_dis = eucli_dis < 0.0001 ? 0.0001 : eucli_dis; - dir[0] = dir[0] / eucli_dis; - dir[1] = dir[1] / eucli_dis; + let eucliDis = Math.hypot(dir[0], dir[1]); + eucliDis = eucliDis < 0.0001 ? 0.0001 : eucliDis; + dir[0] = dir[0] / eucliDis; + dir[1] = dir[1] / eucliDis; let Fg = kg * (degrees[i] + 1); Forces[2 * i] -= Fg * dir[0]; Forces[2 * i + 1] -= Fg * dir[1]; - eucli_dis = null; + eucliDis = null; Fg = null; dir = null; } - quad_params = null; + quadParams = null; quad = null; - quad_tree = null; + quadTree = null; width = null; return Forces; } -function updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao, degrees) { +function updatePos(size, nodes, Forces, preForces, SG, ks, ksmax, tao, degrees) { let swgns = []; let trans = []; // swg(G) and tra(G) let swgG = 0; let traG = 0; for (let i = 0; i < size; i += 1) { - const minus = [ Forces[2 * i] - pre_Forces[2 * i], - Forces[2 * i + 1] - pre_Forces[2 * i + 1] + const minus = [ Forces[2 * i] - preForces[2 * i], + Forces[2 * i + 1] - preForces[2 * i + 1] ]; - const minus_norm = Math.hypot(minus[0], minus[1]); - const add = [ Forces[2 * i] + pre_Forces[2 * i], - Forces[2 * i + 1] + pre_Forces[2 * i + 1] + const minusNorm = Math.hypot(minus[0], minus[1]); + const add = [ Forces[2 * i] + preForces[2 * i], + Forces[2 * i + 1] + preForces[2 * i + 1] ]; - const add_norm = Math.hypot(add[0], add[1]); + const addNorm = Math.hypot(add[0], add[1]); - swgns[i] = minus_norm; - trans[i] = add_norm / 2; + swgns[i] = minusNorm; + trans[i] = addNorm / 2; swgG += (degrees[i] + 1) * swgns[i]; traG += (degrees[i] + 1) * trans[i]; } - let pre_SG = SG; + let preSG = SG; SG = tao * traG / swgG; - if (pre_SG !== 0) { - SG = SG > (1.5 * pre_SG) ? (1.5 * pre_SG) : SG; + if (preSG !== 0) { + SG = SG > (1.5 * preSG) ? (1.5 * preSG) : SG; } // update the node positions for (let i = 0; i < size; i += 1) { @@ -289,13 +277,13 @@ function updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao, degrees) absForce = absForce < 0.0001 ? 0.0001 : absForce; const max = ksmax / absForce; Sn = Sn > max ? max : Sn; - const Dn_x = Sn * Forces[2 * i]; - const Dn_y = Sn * Forces[2 * i + 1]; - nodes[i].x += Dn_x; - nodes[i].y += Dn_y; + const Dnx = Sn * Forces[2 * i]; + const Dny = Sn * Forces[2 * i + 1]; + nodes[i].x += Dnx; + nodes[i].y += Dny; } swgns = null; trans = null; - pre_SG = null; + preSG = null; return [ nodes, SG ]; } diff --git a/plugins/layout.forceAtlas2/quad.js b/plugins/layout.forceAtlas2/quad.js index a8f1bd9756..fb468fd8ac 100644 --- a/plugins/layout.forceAtlas2/quad.js +++ b/plugins/layout.forceAtlas2/quad.js @@ -24,7 +24,7 @@ class Quad { * the mass center of this quad * @type {number} */ - this.mass_center = params.mass_center; + this.massCenter = params.massCenter; /** * the mass of this quad * @type {number} diff --git a/plugins/layout.forceAtlas2/quadTree/body.js b/plugins/layout.forceAtlas2/quadTree/body.js deleted file mode 100644 index 8cdf842fa5..0000000000 --- a/plugins/layout.forceAtlas2/quadTree/body.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * @fileOverview body - * @author shiwu.wyy@antfin.com - */ -const G6 = require('@antv/g6'); -const Util = G6.Util; - -// represents a body(a point mass) and its position -class Body { - constructor(options) { - Util.mix(this, { - /** - * the id of this body, the same with the node id - * @type {number} - */ - id: -1, - /** - * the position of this body - * @type {number} - */ - rx: null, - /** - * the position of this body - * @type {number} - */ - ry: null, - /** - * the force acting on this body - * @type {number} - */ - fx: 0, - /** - * the force acting on this body - * @type {number} - */ - fy: 0, - /** - * the mass of this body, =1 for a node - * @type {number} - */ - mass: 1, - /** - * the degree of the node represented by this body - * @type {number} - */ - degree: 1, - /** - * the parameter for repulsive force, = kr - * @type {number} - */ - G: 1 - }, options); - } - // returns the euclidean distance - distanceTo(bo) { - const dx = this.rx - bo.rx; - const dy = this.ry - bo.ry; - return Math.hypot(dx, dy); - } - setPos(x, y) { - this.rx = x; - this.ry = y; - } - // resets the forces - resetForce() { - this.fx = 0; - this.fy = 0; - } - addForce(b) { - const dx = b.rx - this.rx; - const dy = b.ry - this.ry; - let dist = Math.hypot(dx, dy); - dist = dist < 0.0001 ? 0.0001 : dist; - // the repulsive defined by force atlas 2 - const F = (this.G * (this.degree + 1) * (b.degree + 1)) / dist; - this.fx += F * dx / dist; - this.fy += F * dy / dist; - } - // if quad contains this body - in(quad) { - return quad.contains(this.rx, this.ry); - } - // returns a new body - add(bo) { - const nenw_mass = this.mass + bo.mass; - const x = (this.rx * this.mass + bo.rx * bo.mass) / nenw_mass; - const y = (this.ry * this.mass + bo.ry * bo.mass) / nenw_mass; - const dg = this.degree + bo.degree; - const params = { rx: x, ry: y, mass: nenw_mass, degree: dg }; - return new Body(params); - } -} - -module.exports = Body; diff --git a/plugins/layout.forceAtlas2/quadTree/quad.js b/plugins/layout.forceAtlas2/quadTree/quad.js deleted file mode 100644 index 5458cc8981..0000000000 --- a/plugins/layout.forceAtlas2/quadTree/quad.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * @fileOverview quad - * @author shiwu.wyy@antfin.com - */ -const G6 = require('@antv/g6'); -const Util = G6.Util; - -class Quad { - constructor(options) { - Util.mix(this, { - /** - * the center position of this quad - * @type {number} - */ - xmid: null, - /** - * the center position of this quad - * @type {number} - */ - ymid: null, - /** - * the length of this quad - * @type {number} - */ - length: 0, - /** - * the mass center of this quad - * @type {number} - */ - mass_center: [ 0, 0 ], - /** - * the mass of this quad - * @type {number} - */ - mass: 0 - }, options); - } - getLength() { - return this.length; - } - contains(x, y) { - const halfLen = this.length / 2; - return (x <= this.xmid + halfLen && - x >= this.xmid - halfLen && - y <= this.ymid + halfLen && - y >= this.ymid - halfLen); - } - // northwest quadrant - NW() { - const x = this.xmid - this.length / 4; - const y = this.ymid + this.length / 4; - const len = this.length / 2; - const params = { xmid: x, ymid: y, length: len }; - const NW = new Quad(params); - return NW; - } - // northeast - NE() { - const x = this.xmid + this.length / 4; - const y = this.ymid + this.length / 4; - const len = this.length / 2; - const params = { xmid: x, ymid: y, length: len }; - const NE = new Quad(params); - return NE; - } - // southwest - SW() { - const x = this.xmid - this.length / 4; - const y = this.ymid - this.length / 4; - const len = this.length / 2; - const params = { xmid: x, ymid: y, length: len }; - const SW = new Quad(params); - return SW; - } - // southeast - SE() { - const x = this.xmid + this.length / 4; - const y = this.ymid - this.length / 4; - const len = this.length / 2; - const params = { xmid: x, ymid: y, length: len }; - const SE = new Quad(params); - return SE; - } -} -module.exports = Quad; diff --git a/plugins/layout.forceAtlas2/quadTree/quadTree.js b/plugins/layout.forceAtlas2/quadTree/quadTree.js deleted file mode 100644 index 147221bdae..0000000000 --- a/plugins/layout.forceAtlas2/quadTree/quadTree.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * @fileOverview quadTree - * @author shiwu.wyy@antfin.com - */ -const G6 = require('@antv/g6'); -const Util = G6.Util; -class QuadTree { - // each quadtree represents a quadrant and an aggregate body - // that represents all bodies inside the quadrant - constructor(options) { - Util.mix(this, { - /** - * (aggregated) body in this quad - * @type {object} - */ - body: null, - /** - * tree representing the northwest quadrant - * @type {object} - */ - quad: null, - NW: null, - NE: null, - SW: null, - SE: null, - /** - * threshold - * @type {number} - */ - theta: 0.5 - }, options); - if (options != null) this.quad = options; - } - // insert a body(node) into the tree - insert(bo) { - // if this node does not contain a body, put the new body bo here - if (this.body == null) { - this.body = bo; - return; - } - // internal node - if (!this._isExternal()) { - // update mass info - this.body = this.body.add(bo); - // insert body into quadrant - this._putBody(bo); - } else { // external node - // divide this region into four children - this.NW = new QuadTree(this.quad.NW()); - this.NE = new QuadTree(this.quad.NE()); - this.SW = new QuadTree(this.quad.SW()); - this.SE = new QuadTree(this.quad.SE()); - - // insert this body and bo - this._putBody(this.body); - this._putBody(bo); - // update the mass info - this.body = this.body.add(bo); - - } - } - // inserts bo into a quad - _putBody(bo) { - if (bo.in(this.quad.NW())) this.NW.insert(bo); - else if (bo.in(this.quad.NE())) this.NE.insert(bo); - else if (bo.in(this.quad.SW())) this.SW.insert(bo); - else if (bo.in(this.quad.SE())) this.SE.insert(bo); - } - _isExternal() { - // four children are null - return (this.NW == null && this.NE == null && this.SW == null && this.SE == null); - } - // update the forces - updateForce(bo) { - if (this.body == null || bo === this.body) { - return; - } - // if the current node is external - if (this._isExternal()) bo.addForce(this.body); - // internal nodes - else { - const s = this.quad.getLength(); - const d = this.body.distanceTo(bo); - // b is far enough - if ((s / d) < this.theta) bo.addForce(this.body); - else { - this.NW.updateForce(bo); - this.NE.updateForce(bo); - this.SW.updateForce(bo); - this.SE.updateForce(bo); - } - } - } -} -module.exports = QuadTree; diff --git a/plugins/template.maxSpanningForest/README.md b/plugins/template.maxSpanningForest/README.md index 7f4ec71a60..c965bb0b94 100644 --- a/plugins/template.maxSpanningForest/README.md +++ b/plugins/template.maxSpanningForest/README.md @@ -13,9 +13,9 @@ template.maxSpanningForest for jiuselu graph analyzer, which is a plugin for gra parameter for this plugin: - layoutCfg: the configuration for layout. - - max_iteration: the number of iteration for terminating the layout algorithm + - maxIteration: the number of iteration for terminating the layout algorithm - kg: the gravity parameter for layout. The larger kg, more compact the graph, expecialy for the isolated subgraphs. - - prev_overlapping: whether preventing the node overlapping + - prevOverlapping: whether preventing the node overlapping - onLayoutComplete: a listener for layout completement. When the layout is complete, the loading div and img disappear. - menuCfg: the configuration for menu example: @@ -46,10 +46,10 @@ To navigate an item (a node or edge) by id or item, if this item is not in the v graph.navigate(item); // item or id To create the menu which follows the mouse click: - graph.createMenu(func, container_id); + graph.createMenu(func, containerId); params: - func is the onclick listener for the li '查看单页分析详情' - - container_id is the DOM id of the menu container. + - containerId is the DOM id of the menu container. ## use @@ -60,9 +60,9 @@ const MaxSpanningForestPlugin = G6.Plugins['template.maxSpanningForest']; //the instances of plugins const maxSpanningForest = new MaxSpanningForestPlugin({ layoutCfg: { - max_iteration: 600, + maxIteration: 600, kg: 10, - prev_overlapping: true, + prevOverlapping: true, onLayoutComplete: function () { const minimap = document.getElementById('minimap'); const legend = document.getElementById('legend'); diff --git a/plugins/template.maxSpanningForest/index.js b/plugins/template.maxSpanningForest/index.js index acf751a46f..4d9a728fd1 100644 --- a/plugins/template.maxSpanningForest/index.js +++ b/plugins/template.maxSpanningForest/index.js @@ -43,10 +43,10 @@ class Plugin { kr: 120, kg: 8.0, mode: 'common', - prev_overlapping: true, - dissuade_hubs: false, - max_iteration: 1000, - barnes_hut: true, + prevOverlapping: true, + dissuadeHubs: false, + maxIteration: 1000, + barnesHut: true, ks: 0.1, ksmax: 10, tao: 0.1, @@ -55,13 +55,13 @@ class Plugin { }, fisheye: true, menu: null, - pre_navi: {}, - edge_style: { + preNavi: {}, + edgeStyle: { endArrow: true, stroke: '#4F7DAB', strokeOpacity: 0.65 }, - node_style: { + nodeStyle: { stroke: '#696969', strokeOpacity: 0.4, lineWidth: 1 @@ -148,21 +148,21 @@ class Plugin { if (!model.isTreeEdge || typeof model.isTreeEdge === 'undefined') model.shape = 'quadraticCurve'; } graph.edge({ - style: this.edge_style + style: this.edgeStyle }); graph.node({ label: this.node_label, - style: this.node_style + style: this.nodeStyle }); } activeItem(item) { const graph = this.graph; - const pre_navi = this.pre_navi; + const preNavi = this.preNavi; if (Util.isString(item)) { item = graph.find(item); } let style = {}; - let pre_style = {}; + let preStyle = {}; if (item.type === 'node') { style = { stroke: '#fff', @@ -170,29 +170,29 @@ class Plugin { shadowColor: '#6a80aa', shadowBlur: 20 }; - pre_style = this.node_style; + preStyle = this.nodeStyle; } else if (item.type === 'edge') { style = { endArrow: true, stroke: '#4C7295', strokeOpacity: 1 }; - pre_style = this.edge_style; + preStyle = this.edgeStyle; } else return; // unactive the previous navigate node - if (pre_navi !== {} && pre_navi !== null && pre_navi !== undefined && pre_navi.item !== null) { - graph.update(pre_navi.item, { - style: pre_navi.style + if (preNavi !== {} && preNavi !== null && preNavi !== undefined && preNavi.item !== null) { + graph.update(preNavi.item, { + style: preNavi.style }); } graph.update(item, { style }); - this.pre_navi = { + this.preNavi = { item, - style: pre_style + style: preStyle }; } setListener() { @@ -207,7 +207,7 @@ class Plugin { }); graph.on('node:mouseleave', ev => { graph.update(ev.item, { - style: this.node_style + style: this.nodeStyle }); graph.css({ cursor: '-webkit-grab' @@ -221,7 +221,7 @@ class Plugin { }); graph.on('edge:mouseleave', ev => { graph.update(ev.item, { - style: this.edge_style + style: this.edgeStyle }); }); @@ -230,9 +230,9 @@ class Plugin { item }) => { if (shape && item.isNode) { - const menu_x = item.getModel().x * graph.getMatrix()[0] + graph.getMatrix()[6]; - const menu_y = item.getModel().y * graph.getMatrix()[0] + graph.getMatrix()[7]; - this.menu.show(item, menu_x, menu_y); + const menuX = item.getModel().x * graph.getMatrix()[0] + graph.getMatrix()[6]; + const menuY = item.getModel().y * graph.getMatrix()[0] + graph.getMatrix()[7]; + this.menu.show(item, menuX, menuY); graph.draw(); } else { this.menu.hide(); diff --git a/plugins/template.maxSpanningForest/menu.js b/plugins/template.maxSpanningForest/menu.js index 80e313a06a..c9c8385c8e 100644 --- a/plugins/template.maxSpanningForest/menu.js +++ b/plugins/template.maxSpanningForest/menu.js @@ -9,7 +9,7 @@ const Util = G6.Util; class Menu { constructor(options) { Util.mix(this, { - container_id: '', + containerId: '', clickType: 'in' }, options); this.createMenu(); @@ -26,15 +26,15 @@ class Menu { createMenu() { const graph = this.graph; const menuCfg = this.menuCfg; - const container_id = this.container_id; + const containerId = this.containerId; const menuHtml = ``; const menu = Util.createDOM(menuHtml); let parent = graph.getGraphContainer(); - if (container_id !== '' && container_id !== undefined) { - parent = document.getElementById(container_id); + if (containerId !== '' && containerId !== undefined) { + parent = document.getElementById(containerId); } parent.appendChild(menu); @@ -62,12 +62,12 @@ class Menu { showSource(node) { const graph = this.graph; const { - re_nodes, - re_edges + reNodes, + reEdges } = Util.extract(graph, 'in', 1, [ node ]); graph.highlightSubgraph({ - re_nodes, - re_edges + reNodes, + reEdges }); // show the hided edge, which is not tree edge and it is in the es // and the source and targert of the edge are both visible @@ -75,7 +75,7 @@ class Menu { Util.each(edges, edge => { if (!edge.isVisible() && !edge.getModel().isTreeEdge && edge.getSource().isVisible() && edge.getTarget().isVisible()) { - Util.each(re_edges, e => { + Util.each(reEdges, e => { if (edge.id === e.id) { edge.show(); } @@ -89,12 +89,12 @@ class Menu { showTargets(node) { const graph = this.graph; const { - re_nodes, - re_edges + reNodes, + reEdges } = Util.extract(graph, 'out', 1, [ node ]); graph.highlightSubgraph({ - re_nodes, - re_edges + reNodes, + reEdges }); // show the hided edge, which is not tree edge and it is in the es // and the source and targert of the edge are both visible @@ -102,7 +102,7 @@ class Menu { Util.each(edges, edge => { if (!edge.isVisible() && !edge.getModel().isTreeEdge && edge.getSource().isVisible() && edge.getTarget().isVisible()) { - Util.each(re_edges, e => { + Util.each(reEdges, e => { if (edge.id === e.id) { edge.show(); } @@ -115,12 +115,12 @@ class Menu { showAll(node) { const graph = this.graph; const { - re_nodes, - re_edges + reNodes, + reEdges } = Util.extract(graph, 'bi', 1, [ node ]); graph.highlightSubgraph({ - re_nodes, - re_edges + reNodes, + reEdges }); // show the hided edge, which is not tree edge and it is in the es // and the source and targert of the edge are both visible @@ -128,7 +128,7 @@ class Menu { Util.each(edges, edge => { if (!edge.isVisible() && !edge.getModel().isTreeEdge && edge.getSource().isVisible() && edge.getTarget().isVisible()) { - Util.each(re_edges, e => { + Util.each(reEdges, e => { if (edge.id === e.id) { edge.show(); } diff --git a/plugins/tool.fisheye/index.js b/plugins/tool.fisheye/index.js index 7f08df4053..c1025cfc12 100644 --- a/plugins/tool.fisheye/index.js +++ b/plugins/tool.fisheye/index.js @@ -9,8 +9,8 @@ const Fisheye = require('./tool'); class Plugin { constructor(options) { Util.mix(this, { - ori_xs: [], - ori_ys: [] + oriXs: [], + oriYs: [] }, options); } init() { @@ -23,10 +23,10 @@ class Plugin { graph.on('mousemove', Util.throttle(ev => { const nodes = graph.getNodes(); const size = nodes.length; - if (this.ori_xs.length !== size) return; + if (this.oriXs.length !== size) return; for (let i = 0; i < size; i += 1) { - nodes[i].getModel().x = this.ori_xs[i]; - nodes[i].getModel().y = this.ori_ys[i]; + nodes[i].getModel().x = this.oriXs[i]; + nodes[i].getModel().y = this.oriYs[i]; } fisheye.zoom(ev.x, ev.y); graph.updateNodePosition(); @@ -39,16 +39,16 @@ class Plugin { if (ev === undefined || ev.item === undefined) { for (let i = 0; i < size; i++) { if (nodes[i].getModel().x === undefined) return; - this.ori_xs[i] = nodes[i].getModel().x; - this.ori_ys[i] = nodes[i].getModel().y; + this.oriXs[i] = nodes[i].getModel().x; + this.oriYs[i] = nodes[i].getModel().y; } } else if (ev.item.type !== 'node' || (!ev.updateModel.x && !ev.updateModel.y)) return; else { const item = graph.find(ev.originModel.id); for (let i = 0; i < size; i++) { if (nodes[i].getModel().id === item.id) { - this.ori_x[i] = ev.updateModel.x; - this.ori_y[i] = ev.updateModel.y; + this.oriXs[i] = ev.updateModel.x; + this.oriYs[i] = ev.updateModel.y; } } } diff --git a/plugins/tool.fisheye/tool.js b/plugins/tool.fisheye/tool.js index cd9567e462..c5889cf19d 100644 --- a/plugins/tool.fisheye/tool.js +++ b/plugins/tool.fisheye/tool.js @@ -42,9 +42,9 @@ class Tool { const dist = Math.hypot(node.x - center[0], node.y - center[1]); if (dist < radius) { // take the center as the origin - const moved_coords = [ node.x - center[0], node.y - center[1] ]; + const movedCoords = [ node.x - center[0], node.y - center[1] ]; // transform to polar coordinates - const { p, theta } = Rect2Polar(moved_coords[0], moved_coords[1]); + const { p, theta } = Rect2Polar(movedCoords[0], movedCoords[1]); const pf = radius * (((d + 1) * (p / radius)) / (d * (p / radius) + 1)); // after fisheye zooming // transform to rect coordinates const { x, y } = Polar2Rect(pf, theta); diff --git a/plugins/tool.highlightSubgraph/README.md b/plugins/tool.highlightSubgraph/README.md index 7e3bca17d1..29e774187e 100644 --- a/plugins/tool.highlightSubgraph/README.md +++ b/plugins/tool.highlightSubgraph/README.md @@ -3,10 +3,10 @@ Highlight a subgraph and weaken the rest of the graph. interface: -- highlightSubgraph(hl_items) +- highlightSubgraph(hlItems) hightlight a subgraph params: - - hl_items: the items which will be highlighted + - hlItems: the items which will be highlighted - restoreGraph() restore the graph to the un-lighlighted style. @@ -58,7 +58,7 @@ const graph = new G6.Graph({ graph.read(data); const nodes = graph.getNodes(); const edges = graph.getEdges(); -const re_nodes = [nodes[0], nodes[1]]; -const re_edges = [edges[0]]; -graph.highlightSubgraph({re_nodes, re_edges}); +const reNodes = [nodes[0], nodes[1]]; +const reEdges = [edges[0]]; +graph.highlightSubgraph({reNodes, reEdges}); ``` \ No newline at end of file diff --git a/plugins/tool.highlightSubgraph/index.js b/plugins/tool.highlightSubgraph/index.js index b7764aa796..1457eed394 100644 --- a/plugins/tool.highlightSubgraph/index.js +++ b/plugins/tool.highlightSubgraph/index.js @@ -35,11 +35,11 @@ class Plugin { this.graph.restoreGraph = this.restoreGraph; }); } - highlightSubgraph(hl_items) { + highlightSubgraph(hlItems) { this.restoreGraph(); // sort the group items - const ns = hl_items.re_nodes; - const es = hl_items.re_edges; + const ns = hlItems.reNodes; + const es = hlItems.reEdges; const group = this.getItemGroup(); const items = this.getItems(); Util.each(items, i => { diff --git a/plugins/tool.mapper/index.js b/plugins/tool.mapper/index.js index ce20b2fe22..f99eb64891 100644 --- a/plugins/tool.mapper/index.js +++ b/plugins/tool.mapper/index.js @@ -58,7 +58,8 @@ class Plugin { * 是否数据对齐 * @type {boolean} */ - nice: true + nice: true, + curRange: [ 0, 100 ] }, { itemType, dim, @@ -145,16 +146,12 @@ class Plugin { domain = this._trainNumberScale(itemType, data); } } - const rangeLength = range.length; const domainLength = domain.length; - if (rangeLength !== domainLength) { - const domainStep = (domain[1] - domain[0]) / (rangeLength - 1); - Util.each(range, (v, i) => { - domain[i] = domain[0] + i * domainStep; - }); + if (rangeLength !== domainLength && scaleType === 'Category') { + throw new Error('please set the same length of range to the domain!'); } - if (domain[0] === domain[1]) { + if (domainLength === 2 && domain[0] === domain[1]) { if (domain[0] > 0) { domain[0] = 0; } else if (domain[0] < 0) { @@ -176,10 +173,18 @@ class Plugin { const scaleType = this._getScaleType(data); const channel = this.channel; const graph = this.graph; + const containerId = this.legendCfg.containerId; let legendContainer = this.legendCfg.container; - if (containerId === undefined && legendContainer === undefined) { - legendContainer = Util.createDOM('
'); + if (legendContainer === undefined) { + if (containerId === undefined) { + legendContainer = Util.createDOM('
'); + } else { + legendContainer = document.getElementById(containerId); + if (legendContainer === undefined || legendContainer === null) { + throw new Error('please set the container for the graph !'); + } + } const container = graph.getGraphContainer(); container.appendChild(legendContainer); } @@ -201,27 +206,30 @@ class Plugin { } // the listener to filter nodes and edges const slider = legend.get('slider'); - slider.on('sliderchange', Util.throttle(ev => { - const domain = this.scale.values; - const cur_range = ev.range; - const dim = this.dim; - graph.addFilter(item => { - if (item.isNode) { - const val = item.model[dim]; - const percent = 100 * (val - domain[0]) / (domain[domain.length - 1] - domain[0]); - if (percent > cur_range[1] || percent < cur_range[0]) return false; - return true; - } else if (item.isEdge) { - const source_val = item.source.model[dim]; - const source_percent = 100 * (source_val - domain[0]) / (domain[domain.length - 1] - domain[0]); - const source_visible = (source_percent <= cur_range[1] && source_percent >= cur_range[0]); - const target_val = item.target.model[dim]; - const target_percent = 100 * (target_val - domain[0]) / (domain[domain.length - 1] - domain[0]); - const target_visible = (target_percent <= cur_range[1] && target_percent >= cur_range[0]); - if (!source_visible || !target_visible) return false; - return true; + const domain = this.scale.values; + const dim = this.dim; + graph.addFilter(item => { + if (item.isNode) { + const val = item.model[dim]; + const percent = 100 * (val - domain[0]) / (domain[domain.length - 1] - domain[0]); + if (percent > this.curRange[1] || percent < this.curRange[0]) { + return false; } - }); + return true; + } else if (item.isEdge) { + const sourceVal = item.source.model[dim]; + const sourcePercent = 100 * (sourceVal - domain[0]) / (domain[domain.length - 1] - domain[0]); + const sourceVisible = (sourcePercent <= this.curRange[1] && sourcePercent >= this.curRange[0]); + const targetVal = item.target.model[dim]; + const targetPercent = 100 * (targetVal - domain[0]) / (domain[domain.length - 1] - domain[0]); + const targetVisible = (targetPercent <= this.curRange[1] && targetPercent >= this.curRange[0]); + if (!sourceVisible || !targetVisible) return false; + return true; + } + }); + + slider.on('sliderchange', Util.throttle(ev => { + this.curRange = ev.range; graph.filter(); }, 100)); } @@ -306,14 +314,14 @@ class Plugin { const items = []; Util.each(range, (val, i) => { const percent = (domain[i] - scale.min) / (scale.max - scale.min); - let item_text = domain[i]; - if (legendCfg.formatter !== undefined && legendCfg.formmater !== null) { - item_text = legendCfg.formatter(domain[i]); + let itemText = domain[i]; + if (legendCfg.formatter !== undefined && legendCfg.formatter !== null) { + itemText = legendCfg.formatter(domain[i]); } items.push({ text: domain[i], attrValue: val, - value: item_text, // the number label of the slider + value: itemText, // the number label of the slider scaleValue: percent }); }); @@ -361,14 +369,14 @@ class Plugin { const items = []; Util.each(range, (val, i) => { const dom = domain[0] + domainStep * i; - let item_text = dom; + let itemText = dom; if (legendCfg.formatter !== undefined && legendCfg.formmater !== null) { - item_text = legendCfg.formatter(dom); + itemText = legendCfg.formatter(dom); } items.push({ text: dom, attrValue: val, - value: item_text // the number label of the slider + value: itemText // the number label of the slider }); }); const cfg = { @@ -407,9 +415,8 @@ class Plugin { return scale.scale(model[dim]) * 2; } else if (channel === 'color') { return color.mapping(model[dim])[0]; - } else if (itemType === 'edge' && channel === 'size') { - return scale.scale(model[dim]); } + // itemType === 'edge' && channel === 'size' return scale.scale(model[dim]); }); } @@ -419,11 +426,6 @@ class Plugin { max: domain[domain.length - 1] }; switch (type) { - case 'Linear': - return Scale.linear({ - min: domain[0], - max: domain[domain.length - 1] - }); case 'Category': return Scale.cat({ values: domain diff --git a/plugins/tool.textDisplay/README.md b/plugins/tool.textDisplay/README.md index d537250e5f..a8cb7cd8b1 100644 --- a/plugins/tool.textDisplay/README.md +++ b/plugins/tool.textDisplay/README.md @@ -1,6 +1,6 @@ ## textDisplay -Hide the labels when the width of the text are 2 times bigger than the parent node. +Hide the labels when the width of the text is 2 times bigger than the parent node. ## use diff --git a/plugins/tool.textDisplay/index.js b/plugins/tool.textDisplay/index.js index 7c122fb381..84a69cec5a 100644 --- a/plugins/tool.textDisplay/index.js +++ b/plugins/tool.textDisplay/index.js @@ -29,10 +29,10 @@ class Plugin { const label = node.getLabel(); const model = node.getModel(); const labelBox = label.getBBox(); - const label_width = labelBox.maxX - labelBox.minX; - const node_width = model.size * scale; - if (label_width === 0) return; - const ratio = label_width / node_width; + const labelWidth = labelBox.maxX - labelBox.minX; + const nodeWidth = model.size * scale; + if (labelWidth === 0) return; + const ratio = labelWidth / nodeWidth; if (ratio > 2) { label.hide(); } else { diff --git a/plugins/util.extractSubgraph/index.js b/plugins/util.extractSubgraph/index.js index 3e3cfb7954..b1a8acf528 100644 --- a/plugins/util.extractSubgraph/index.js +++ b/plugins/util.extractSubgraph/index.js @@ -10,58 +10,58 @@ const Util = G6.Util; const extractSubgraph = { extract(graph, type, step, focusNodes) { - const re_edges = []; + const reEdges = []; Util.each(focusNodes, fn => { if (type === 'in') { - const in_edges = fn.getInEdges(); - Util.each(in_edges, ie => { - re_edges.push(ie); + const inEdges = fn.getInEdges(); + Util.each(inEdges, ie => { + reEdges.push(ie); }); } else if (type === 'out') { - const out_edges = fn.getOutEdges(); - Util.each(out_edges, oe => { - re_edges.push(oe); + const outEdges = fn.getOutEdges(); + Util.each(outEdges, oe => { + reEdges.push(oe); }); } else { - const in_edges = fn.getInEdges(); - Util.each(in_edges, ie => { - re_edges.push(ie); + const inEdges = fn.getInEdges(); + Util.each(inEdges, ie => { + reEdges.push(ie); }); - const out_edges = fn.getOutEdges(); - Util.each(out_edges, oe => { - re_edges.push(oe); + const outEdges = fn.getOutEdges(); + Util.each(outEdges, oe => { + reEdges.push(oe); }); } }); - const re_nodes = []; + const reNodes = []; Util.each(focusNodes, fn => { - re_nodes.push(fn); - Util.each(re_edges, e => { + reNodes.push(fn); + Util.each(reEdges, e => { const source = e.getSource(); const target = e.getTarget(); - if (source.id !== fn.id) re_nodes.push(source); - if (target.id !== fn.id) re_nodes.push(target); + if (source.id !== fn.id) reNodes.push(source); + if (target.id !== fn.id) reNodes.push(target); }); }); return { - re_nodes, - re_edges + reNodes, + reEdges }; }, - setZIndex(ns, es, min_z) { + setZIndex(ns, es, minZ) { const graph = this.graph; const nodes = graph.getNodes(); const edges = graph.getEdges(); Util.each(nodes, node => { Util.each(ns, n => { - if (node.id === n.id) node.getGraphicGroup().setSilent('zIndex', min_z + 2); + if (node.id === n.id) node.getGraphicGroup().setSilent('zIndex', minZ + 2); }); }); Util.each(edges, edge => { Util.each(es, e => { - if (edge.id === e.id) edge.getGraphicGroup().setSilent('zIndex', min_z + 1); + if (edge.id === e.id) edge.getGraphicGroup().setSilent('zIndex', minZ + 1); }); }); graph.getItemGroup().sort(); diff --git a/src/graph.js b/src/graph.js index ad95ca6374..772a42fb3e 100755 --- a/src/graph.js +++ b/src/graph.js @@ -106,7 +106,7 @@ class Graph extends Base { const cfg = {}; Mixins.forEach(Mixin => { - Util.mix(cfg, Mixin.CFG, inputCfg); + Util.mix(cfg, Util.cloneDeep(Mixin.CFG), inputCfg); }); super(cfg); // plugin should init before all diff --git a/src/version.js b/src/version.js index 4b75c7b43d..3f92dc4960 100755 --- a/src/version.js +++ b/src/version.js @@ -1 +1 @@ -module.exports = '2.1.0-beta'; +module.exports = '2.1.0-beta.3'; diff --git a/test/unit/plugins/layout.forceAtlas2-spec.js b/test/unit/plugins/layout.forceAtlas2-spec.js index 4d391b9e71..c03a281d23 100644 --- a/test/unit/plugins/layout.forceAtlas2-spec.js +++ b/test/unit/plugins/layout.forceAtlas2-spec.js @@ -3,130 +3,145 @@ const Layout = require('../../../build/plugin.layout.forceAtlas2'); const expect = require('chai').expect; const Util = G6.Util; -// document.body.appendChild(Util.createDOM(` -//
-// `)); +document.body.appendChild(Util.createDOM(` +
+`)); -// describe('custom layout test', () => { -// const layout = new Layout(); -// const data = { -// nodes: [{ -// id: 'node1' -// }, { -// id: 'node2' -// }, { -// id: 'node3' -// }], -// edges: [{ -// target: 'node2', -// source: 'node1' -// }, { -// target: 'node2', -// source: 'node3' -// }] -// }; -// const graph = new G6.Graph({ -// container: 'mountNode', -// width: 500, -// height: 500, -// plugins: [ layout ] -// }); +describe('custom layout test', () => { + const originInnerHTML = document.getElementById('mountNode').innerHTML; + const layout = new Layout(); + const data = { + nodes: [{ + id: 'node1' + }, { + id: 'node2' + }, { + id: 'node3' + }], + edges: [{ + target: 'node2', + source: 'node1' + }, { + target: 'node2', + source: 'node3' + }] + }; + const graph = new G6.Graph({ + container: 'mountNode', + width: 500, + height: 500, + plugins: [ layout ] + }); -// graph.read(data); -// it('layout node positions', () => { -// const node1Model = graph.find('node1').getModel(); -// expect(node1Model.x).not.eql(undefined); -// expect(node1Model.y).not.eql(undefined); -// }); -// it('graph destroy', () => { -// graph.destroy(); -// }); -// }); + graph.read(data); + it('graph render', () => { + expect(document.getElementById('mountNode').innerHTML).not.eql(originInnerHTML); + }); + it('layout node positions', () => { + graph.on('afterlayout', () => { + const node1Model = graph.find('node1').getModel(); + expect(node1Model.x).not.eql(undefined); + expect(node1Model.y).not.eql(undefined); + }); + }); + it('graph destroy', () => { + graph.on('afterlayout', () => { + graph.destroy(); + expect(document.getElementById('mountNode').innerHTML).eql(originInnerHTML); + }); + }); +}); -// describe('node nonoverlapping test', () => { -// const layout = new Layout({ prevOverlapping: true }); -// const data = { -// nodes: [{ -// id: 'node1' -// }, { -// id: 'node2' -// }, { -// id: 'node3' -// }], -// edges: [{ -// target: 'node2', -// source: 'node1' -// }, { -// target: 'node2', -// source: 'node3' -// }] -// }; -// const graph = new G6.Graph({ -// container: 'mountNode', -// width: 500, -// height: 500, -// plugins: [ layout ] -// }); -// graph.read(data); -// it('overlapping', () => { -// const node1Model = graph.find('node1').getModel(); -// const node2Model = graph.find('node2').getModel(); -// const node3Model = graph.find('node3').getModel(); -// const node1BBox = node1Model.getBBox(); -// const node2BBox = node2Model.getBBox(); -// const node3BBox = node3Model.getBBox(); -// const node1Radius = (node1BBox.maxX - node1BBox.minX) / 2; -// const node2Radius = (node2BBox.maxX - node2BBox.minX) / 2; -// const node3Radius = (node3BBox.maxX - node3BBox.minX) / 2; -// const dist12 = Math.plot(node1Model.x - node2Model.x, node1Model.y - node2Model.y); -// const dist23 = Math.plot(node2Model.x - node3Model.x, node2Model.y - node3Model.y); -// const dist13 = Math.plot(node1Model.x - node3Model.x, node1Model.y - node3Model.y); -// expect(node1Radius - node2Radius <= dist12).eql(true); -// expect(node2Radius - node3Radius <= dist23).eql(true); -// expect(node1Radius - node3Radius <= dist13).eql(true); -// }); -// it('layout node positions', () => { -// const node1Model = graph.find('node1').getModel(); -// expect(node1Model.x).not.eql(undefined); -// expect(node1Model.y).not.eql(undefined); -// }); -// it('graph destroy', () => { -// graph.destroy(); -// }); -// }); +describe('node nonoverlapping test', () => { + const layout = new Layout({ prevOverlapping: true }); + const data = { + nodes: [{ + id: 'node1' + }, { + id: 'node2' + }, { + id: 'node3' + }], + edges: [{ + target: 'node2', + source: 'node1' + }, { + target: 'node2', + source: 'node3' + }] + }; + const graph = new G6.Graph({ + container: 'mountNode', + width: 500, + height: 500, + plugins: [ layout ] + }); + graph.read(data); + it('overlapping', () => { + graph.on('afterlayout', () => { + const node1Model = graph.find('node1').getModel(); + const node2Model = graph.find('node2').getModel(); + const node3Model = graph.find('node3').getModel(); + const node1BBox = node1Model.getBBox(); + const node2BBox = node2Model.getBBox(); + const node3BBox = node3Model.getBBox(); + const node1Radius = (node1BBox.maxX - node1BBox.minX) / 2; + const node2Radius = (node2BBox.maxX - node2BBox.minX) / 2; + const node3Radius = (node3BBox.maxX - node3BBox.minX) / 2; + const dist12 = Math.plot(node1Model.x - node2Model.x, node1Model.y - node2Model.y); + const dist23 = Math.plot(node2Model.x - node3Model.x, node2Model.y - node3Model.y); + const dist13 = Math.plot(node1Model.x - node3Model.x, node1Model.y - node3Model.y); + expect(node1Radius - node2Radius <= dist12).eql(true); + expect(node2Radius - node3Radius <= dist23).eql(true); + expect(node1Radius - node3Radius <= dist13).eql(true); + }); + }); + it('layout node positions', () => { + graph.on('afterlayout', () => { + const node1Model = graph.find('node1').getModel(); + expect(node1Model.x).not.eql(undefined); + expect(node1Model.y).not.eql(undefined); + }); + }); + it('graph destroy', () => { + graph.destroy(); + }); +}); -// describe('barnes hut optimiazation test', () => { -// const layout = new Layout(); -// const data = { -// nodes: [{ -// id: 'node1' -// }, { -// id: 'node2' -// }, { -// id: 'node3' -// }], -// edges: [{ -// target: 'node2', -// source: 'node1' -// }, { -// target: 'node2', -// source: 'node3' -// }] -// }; -// const graph = new G6.Graph({ -// container: 'mountNode', -// width: 500, -// height: 500, -// plugins: [ layout ] -// }); +describe('barnes hut optimiazation test', () => { + const layout = new Layout(); + const data = { + nodes: [{ + id: 'node1' + }, { + id: 'node2' + }, { + id: 'node3' + }], + edges: [{ + target: 'node2', + source: 'node1' + }, { + target: 'node2', + source: 'node3' + }] + }; + const graph = new G6.Graph({ + container: 'mountNode', + width: 500, + height: 500, + plugins: [ layout ] + }); -// graph.read(data); -// it('layout node positions', () => { -// const node1Model = graph.find('node1').getModel(); -// expect(node1Model.x).not.eql(undefined); -// expect(node1Model.y).not.eql(undefined); -// }); -// it('graph destroy', () => { -// graph.destroy(); -// }); -// }); + graph.read(data); + it('layout node positions', () => { + graph.on('afterlayout', () => { + const node1Model = graph.find('node1').getModel(); + expect(node1Model.x).not.eql(undefined); + expect(node1Model.y).not.eql(undefined); + }); + }); + it('graph destroy', () => { + graph.destroy(); + }); +}); diff --git a/test/unit/plugins/tool.mapper-spec.js b/test/unit/plugins/tool.mapper-spec.js index da542ee26b..1589faa65e 100644 --- a/test/unit/plugins/tool.mapper-spec.js +++ b/test/unit/plugins/tool.mapper-spec.js @@ -2,15 +2,18 @@ const G6 = require('../../../src/index'); const Mapper = require('../../../plugins/tool.mapper/'); const expect = require('chai').expect; const Util = G6.Util; -const Simulate = require('event-simulate'); document.body.appendChild(Util.createDOM(`
+
+
+
+
`)); @@ -62,15 +65,61 @@ describe('node size mapper test', () => { }); it('legend destroy', () => { graph.destroy(); - expect(document.getElementById('nodeSizeLegend').innerHTML).eql(originInnerHTML); + expect(document.getElementById('nodeSizeLegend')).eql(null); }); }); -describe('node color mapper test', () => { - const originInnerHTML = document.getElementById('nodeColorLegend').innerHTML; +describe('node color mapper domain length test', () => { + const fn = function() { + const nodeSizeMapper = new Mapper('node', 'class', 'color', [ '#ff0000', '#00ff00' ], { + legendCfg: { + layout: 'vertical' + } + }); + const data = { + nodes: [{ + id: 'node1', + x: 100, + y: 200, + class: 'class1' + }, { + id: 'node2', + x: 300, + y: 200, + class: 'class2' + }, { + id: 'node3', + x: 400, + y: 200, + class: 'class3' + }], + edges: [{ + target: 'node2', + source: 'node1' + }] + }; + const graph = new G6.Graph({ + container: 'mountNode', + width: 500, + height: 500, + plugins: [ nodeSizeMapper ] + }); + graph.read(data); + }; + + it('legend render', () => { + expect(fn).to.Throw(); + }); +}); + +describe('node color mapper domian equals 1 test', () => { + const originInnerHTML = document.getElementById('nodeColorLegend1').innerHTML; const nodeSizeMapper = new Mapper('node', 'weight', 'color', [ '#ff0000', '#00ff00' ], { + scaleCfg: { + type: 'pow' + }, legendCfg: { - containerId: 'nodeColorLegend', + containerId: 'nodeColorLegend1', layout: 'vertical' } }); @@ -84,7 +133,7 @@ describe('node color mapper test', () => { id: 'node2', x: 300, y: 200, - weight: 2 + weight: 1 }], edges: [{ target: 'node2', @@ -99,20 +148,123 @@ describe('node color mapper test', () => { }); graph.read(data); it('legend render', () => { - expect(document.getElementById('nodeColorLegend').innerHTML).not.eql(originInnerHTML); + expect(document.getElementById('nodeColorLegend1').innerHTML).not.eql(originInnerHTML); + }); + it('node color mapper', () => { + const node1Model = graph.find('node1').getModel(); + const node2Model = graph.find('node2').getModel(); + expect(node1Model.color).eql('#00ff00'); + expect(node2Model.color).eql('#00ff00'); + }); + it('legend destroy', () => { + graph.destroy(); + expect(document.getElementById('nodeColorLegend1')).eql(null); + }); +}); + +describe('node color mapper domian equals 0 test', () => { + const originInnerHTML = document.getElementById('nodeColorLegend2').innerHTML; + const nodeSizeMapper = new Mapper('node', 'weight', 'color', [ '#ff0000', '#00ff00' ], { + scaleCfg: { + type: 'pow' + }, + legendCfg: { + containerId: 'nodeColorLegend2', + layout: '', + formatter: num => { + return num * num; + } + } + }); + const data = { + nodes: [{ + id: 'node1', + x: 100, + y: 200, + weight: 0 + }, { + id: 'node2', + x: 300, + y: 200, + weight: 0 + }], + edges: [{ + target: 'node2', + source: 'node1' + }] + }; + const graph = new G6.Graph({ + container: 'mountNode', + width: 500, + height: 500, + plugins: [ nodeSizeMapper ] + }); + graph.read(data); + it('legend render', () => { + expect(document.getElementById('nodeColorLegend2').innerHTML).not.eql(originInnerHTML); + }); + it('node color mapper', () => { + const node1Model = graph.find('node1').getModel(); + const node2Model = graph.find('node2').getModel(); + expect(node1Model.color).eql('#00ff00'); + expect(node2Model.color).eql('#00ff00'); + }); + it('legend destroy', () => { + graph.destroy(); + expect(document.getElementById('nodeColorLegend2')).eql(null); + }); +}); + +describe('node color mapper domian equals -1 test', () => { + const originInnerHTML = document.getElementById('nodeColorLegend3').innerHTML; + const nodeSizeMapper = new Mapper('node', 'weight', 'color', [ '#ff0000', '#00ff00' ], { + scaleCfg: { + type: 'pow' + }, + legendCfg: { + containerId: 'nodeColorLegend3' + } + }); + const data = { + nodes: [{ + id: 'node1', + x: 100, + y: 200, + weight: -1 + }, { + id: 'node2', + x: 300, + y: 200, + weight: -1 + }], + edges: [{ + target: 'node2', + source: 'node1' + }] + }; + const graph = new G6.Graph({ + container: 'mountNode', + width: 500, + height: 500, + plugins: [ nodeSizeMapper ] + }); + graph.read(data); + it('legend render', () => { + expect(document.getElementById('nodeColorLegend3').innerHTML).not.eql(originInnerHTML); }); it('node color mapper', () => { const node1Model = graph.find('node1').getModel(); const node2Model = graph.find('node2').getModel(); expect(node1Model.color).eql('#ff0000'); - expect(node2Model.color).eql('#00ff00'); + expect(node2Model.color).eql('#ff0000'); }); it('legend destroy', () => { graph.destroy(); - expect(document.getElementById('nodeColorLegend').innerHTML).eql(originInnerHTML); + expect(document.getElementById('nodeColorLegend3')).eql(null); }); }); + describe('edge size mapper test', () => { const edgeSizeMapper = new Mapper('edge', 'weight', 'size', [ 10, 50 ], { legendCfg: null @@ -159,6 +311,53 @@ describe('edge size mapper test', () => { }); }); +describe('edge size mapper vertical test', () => { + const edgeSizeMapper = new Mapper('edge', 'weight', 'size', [ 10, 50 ], { + legendCfg: { + layout: 'vertical' + } + }); + const data = { + nodes: [{ + id: 'node1', + x: 100, + y: 200 + }, { + id: 'node2', + x: 300, + y: 200 + }, { + id: 'node3', + x: 400, + y: 200 + }], + edges: [{ + target: 'node2', + source: 'node1', + weight: 1 + }, { + target: 'node2', + source: 'node3', + weight: 2 + }] + }; + const graph = new G6.Graph({ + container: 'mountNode', + width: 500, + height: 500, + plugins: [ edgeSizeMapper ] + }); + graph.read(data); + it('edge size mapper', () => { + const edges = graph.getEdges(); + const edge1Model = edges[0].getModel(); + const edge2Model = edges[1].getModel(); + const size1 = edge1Model.size; + const size2 = edge2Model.size; + expect(size1).eql(10); + expect(size2).eql(50); + }); +}); describe('node color category mapper test', () => { const originInnerHTML = document.getElementById('nodeColorCatLegend').innerHTML; @@ -202,12 +401,11 @@ describe('node color category mapper test', () => { }); it('legend destroy', () => { graph.destroy(); - expect(document.getElementById('nodeColorCatLegend').innerHTML).eql(originInnerHTML); + expect(document.getElementById('nodeColorCatLegend')).eql(null); }); }); describe('node size mapper with formatter test', () => { - const originInnerHTML = document.getElementById('nodeSizeFormatLegend').innerHTML; const nodeSizeMapper = new Mapper('node', 'weight', 'size', [ 10, 50 ], { legendCfg: { formatter: num => { @@ -239,9 +437,6 @@ describe('node size mapper with formatter test', () => { plugins: [ nodeSizeMapper ] }); graph.read(data); - it('legend render', () => { - expect(document.getElementById('nodeSizeFormatLegend').innerHTML).not.eql(originInnerHTML); - }); it('node size mapper', () => { const node1Model = graph.find('node1').getModel(); const node2Model = graph.find('node2').getModel(); @@ -250,9 +445,45 @@ describe('node size mapper with formatter test', () => { expect(size1).eql(10); expect(size2).eql(50); }); - it('legend destroy', () => { - graph.destroy(); - expect(document.getElementById('nodeSizeFormatLegend').innerHTML).eql(originInnerHTML); +}); + + +describe('container undefined test', () => { + const fn = function() { + const nodeSizeMapper = new Mapper('node', 'weight', 'size', [ 10, 50 ], { + legendCfg: { + containerId: 'undefinedDOM' + } + }); + const data = { + nodes: [{ + id: 'node1', + x: 100, + y: 200, + weight: 2 + }, { + id: 'node2', + x: 300, + y: 200, + weight: 3 + }], + edges: [{ + target: 'node2', + source: 'node1' + }] + }; + const graph = new G6.Graph({ + container: 'mountNode', + width: 500, + height: 500, + plugins: [ nodeSizeMapper ] + }); + graph.read(data); + + }; + + it('legend render', () => { + expect(fn).to.Throw(); }); }); @@ -268,7 +499,7 @@ describe('slider test', () => { id: 'node1', x: 100, y: 200, - weight: 2 + weight: 1 }, { id: 'node2', x: 300, @@ -286,16 +517,11 @@ describe('slider test', () => { height: 500, plugins: [ nodeSizeMapper ] }); - const mouseEventWrapper = graph.getMouseEventWrapper(); graph.read(data); it('legend sliderchange', () => { - const node1Model = graph.find('node1').getModel(); - const clientPoint = graph.getClientPoint(node1Model); - Simulate.simulate(mouseEventWrapper, 'sliderchange', { - clientX: clientPoint.x - 50, - clientY: clientPoint.y - }); - expect(document.getElementsById('sliderChangeTestDiv')).not.eql(undefined); + const slider = nodeSizeMapper.legend.get('slider'); + slider.emit('sliderchange', { range: [ 0, 50 ] }); + expect(document.getElementById('sliderChangeTestDiv')).not.eql(undefined); }); });