From 7cf642aecc95ff58fe06226b96d6f3495089bfdb Mon Sep 17 00:00:00 2001 From: Rajkumar Sriramulu Date: Mon, 27 Feb 2017 06:40:06 +0530 Subject: [PATCH 1/3] added trace to construct elements still work pending in construct and tree code --- .../binary_search_tree/bst_insert/data.js | 28 +- js/module/tracer/directed_graph_construct.js | 448 ++++++++++++++++++ js/module/tracer/index.js | 2 + 3 files changed, 452 insertions(+), 26 deletions(-) create mode 100644 js/module/tracer/directed_graph_construct.js diff --git a/algorithm/tree/binary_search_tree/bst_insert/data.js b/algorithm/tree/binary_search_tree/bst_insert/data.js index d062c779..bbf2cf33 100644 --- a/algorithm/tree/binary_search_tree/bst_insert/data.js +++ b/algorithm/tree/binary_search_tree/bst_insert/data.js @@ -1,31 +1,7 @@ -var G = [ // G[i][j] indicates whether the path from the i-th node to the j-th node exists or not - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] -]; +var G = []; -var T = [ // mapping to G as a binary tree , [i][0] indicates left child, [i][1] indicates right child - [-1,-1], - [-1,-1], - [-1,-1], - [-1,-1], - [-1,-1], - [-1,-1], - [-1,-1], - [-1,-1], - [-1,-1], - [-1,-1], - [-1,-1] -]; +var T = {}; var elements = [5,8,10,3,1,6,9,7,2,0,4]; // item to be searched var tracer = new DirectedGraphTracer( " BST - Elements marked red indicates the current status of tree "); diff --git a/js/module/tracer/directed_graph_construct.js b/js/module/tracer/directed_graph_construct.js new file mode 100644 index 00000000..962d0e1d --- /dev/null +++ b/js/module/tracer/directed_graph_construct.js @@ -0,0 +1,448 @@ +'use strict'; + +const Tracer = require('./tracer'); + +const { + refineByType +} = require('../../tracer_manager/util/index'); + +class DirectedGraphConstructTracer extends Tracer { + static getClassName() { + return 'DirectedGraphConstructTracer'; + } + + constructor(name, nodePlacement = null) { + super(name); + this.nodePlacement = nodePlacement; + if (this.isNew) initView(this); + } + + _addNode(G, root, element, parentElement = null) { + this.manager.pushStep(this.capsule, { + type: 'addNode', + arguments: arguments + }); + return this; + } + + _findNode(G, val) { + var idToFind = this.n(val); + var result = null; + for (let i = 0; i < G.length; i++) { + if(G[i].id === idToFind) { + result = G[i]; + break; + } + } + return result; + } + + _visit(G, target, source) { + this.manager.pushStep(this.capsule, { + type: 'visit', + G: G, + target: target, + source: source + }); + return this; + } + + _leave(target, source) { + this.manager.pushStep(this.capsule, { + type: 'leave', + target: target, + source: source + }); + return this; + } + + _setNodePositions(positions) { + this.manager.pushStep(this.capsule, { + type: 'setNodePositions', + positions: positions + }); + return this; + } + + processStep(step, options) { + switch (step.type) { + case 'setTreeData': + this.setTreeData.apply(this, step.arguments); + break; + case 'setNodePositions': + $.each(this.graph.nodes(), (i, node) => { + if (i >= step.positions.length) return false; + const position = step.positions[i]; + node.x = position.x; + node.y = position.y; + }); + break; + case 'addNode': + this.addNode.apply(this, step.arguments); + break; + case 'visit': + case 'leave': + var visit = step.type == 'visit'; + var nodeObject = this._findNode(step.G, step.target); + nodeObject.visited = visit; + nodeObject.isNew = false; + var targetNode = this.graph.nodes(this.n(step.target)); + var color = visit ? this.color.visited : this.color.left; + if(targetNode) { + targetNode.color = color; + if (step.source !== undefined) { + var edgeId = this.e(step.source, step.target); + var edge = this.graph.edges(edgeId); + edge.color = color; + this.graph.dropEdge(edgeId).addEdge(edge); + } + } + if (this.logTracer) { + var source = step.source; + if (source === undefined) source = ''; + this.logTracer.print(visit ? source + ' -> ' + step.target : source + ' <- ' + step.target); + } + break; + default: + super.processStep(step, options); + } + } + + addNode(G, root, node, parent) { + var nodeObject = this.nodeConstruct(node); + var parentObject = this._findNode(G, parent); + if (parentObject) { + nodeObject.parent = parentObject; + nodeObject.level = parentObject.level + 1; + if (this.nodePlacement === null) { + parentObject.children.push(nodeObject); + } else if (this.nodePlacement === 0) { + var isSpliced = false; + var insertIndex = 0; + if (parentObject.children.length > 0) { + for(let i = 0; i < parentObject.children.length; i++) { + var child = parentObject.children[i]; + if(child.originalVal > node) { + isSpliced = true; + break; + } + insertIndex++; + } + } + if(isSpliced) { + parentObject.children.splice(insertIndex, 0, nodeObject); + console.log('exchange Happened'); + } else { + parentObject.children.push(nodeObject); + } + } + } + nodeObject.updateBreadth(); + G.push(nodeObject); + this.drawGraph(G, root, nodeObject.level); + if(node === 4) { + console.log(this.graph.nodes); + } + } + + nodeConstruct(val) { + var nodeObject = { + id: this.n(val), + originalVal: val, + isNew: true, + children: [], + breadth: 0, + level: 1, + parent: null, + visited: false, + updateBreadth: function() { + var oldBreadth = nodeObject.breadth; + if ( nodeObject.children.length > 0 ) { + nodeObject.breadth = nodeObject.children.length % 2 ? 0 : 1; + for (let j = 0; j < nodeObject.children.length; j++) { + nodeObject.breadth += nodeObject.children[j].breadth; + } + } else { nodeObject.breadth = 1; } + if ( oldBreadth !== nodeObject.breadth && nodeObject.parent ) { + nodeObject.parent.updateBreadth(); + } + } + } + return nodeObject; + } + + drawGraph(G, root, nodeLevel) { + const nodes = []; + const edges = []; + var tracer = this; + var drawNode = function (node, parentNode, occupiedBreadth) { + var calculatedX = node.breadth; + if (parentNode) { + calculatedX = parentNode.breadth + occupiedBreadth - node.breadth; + } else if (node.children.length > 0) { + calculatedX = Math.ceil(calculatedX/node.children.length); + } + + nodes.push({ + id: node.id, + label: '' + node.originalVal, + x: calculatedX, + y: node.level - 1, + size: 1, + color: node.isNew ? tracer.color.selected : (node.visited ? tracer.color.visited : tracer.color.default), + weight: 0 + }); + + if ( node.children.length > 0 ) { + var midPoint = node.children.length / 2; + var occupiedBreadth = 0; + for (let j = 0; j < node.children.length; j++) { + var childNode = node.children[j]; + edges.push({ + id: tracer.e(node.originalVal, childNode.originalVal), + source: node.id, + target: childNode.id, + color: node.visited && childNode.visited ? tracer.color.visited : tracer.color.default, + size: 1, + weight: refineByType(childNode.originalVal) + }); + drawNode(childNode, node, occupiedBreadth); + occupiedBreadth += node.breadth; + } + } + } + var rootObject = this._findNode(G, root); + drawNode(rootObject); + + this.graph.clear(); + this.graph.read({ + nodes: nodes, + edges: edges + }); + this.s.camera.goTo({ + x: 0, + y: nodeLevel, + angle: 0, + ratio: 1 + }); + this.refresh(); + + return false; + } + + resize() { + super.resize(); + + this.s.renderers[0].resize(); + this.refresh(); + } + + refresh() { + super.refresh(); + + this.s.refresh(); + } + + clear() { + super.clear(); + + this.clearGraphColor(); + this.refresh(); + } + + clearGraphColor() { + var tracer = this; + + this.graph.nodes().forEach(function (node) { + node.color = tracer.color.default; + }); + this.graph.edges().forEach(function (edge) { + edge.color = tracer.color.default; + }); + } + + n(v) { + return 'n' + v; + } + + e(v1, v2) { + return 'e' + v1 + '_' + v2; + } + + getColor(edge, source, target, settings) { + var color = edge.color, + edgeColor = settings('edgeColor'), + defaultNodeColor = settings('defaultNodeColor'), + defaultEdgeColor = settings('defaultEdgeColor'); + if (!color) + switch (edgeColor) { + case 'source': + color = source.color || defaultNodeColor; + break; + case 'target': + color = target.color || defaultNodeColor; + break; + default: + color = defaultEdgeColor; + break; + } + + return color; + } + + drawLabel(node, context, settings) { + var fontSize, + prefix = settings('prefix') || '', + size = node[prefix + 'size']; + + if (size < settings('labelThreshold')) + return; + + if (!node.label || typeof node.label !== 'string') + return; + + fontSize = (settings('labelSize') === 'fixed') ? + settings('defaultLabelSize') : + settings('labelSizeRatio') * size; + + context.font = (settings('fontStyle') ? settings('fontStyle') + ' ' : '') + + fontSize + 'px ' + settings('font'); + context.fillStyle = (settings('labelColor') === 'node') ? + (node.color || settings('defaultNodeColor')) : + settings('defaultLabelColor'); + + context.textAlign = 'center'; + context.fillText( + node.label, + Math.round(node[prefix + 'x']), + Math.round(node[prefix + 'y'] + fontSize / 3) + ); + } + + drawArrow(edge, source, target, color, context, settings) { + var prefix = settings('prefix') || '', + size = edge[prefix + 'size'] || 1, + tSize = target[prefix + 'size'], + sX = source[prefix + 'x'], + sY = source[prefix + 'y'], + tX = target[prefix + 'x'], + tY = target[prefix + 'y'], + angle = Math.atan2(tY - sY, tX - sX), + dist = 3; + sX += Math.sin(angle) * dist; + tX += Math.sin(angle) * dist; + sY += -Math.cos(angle) * dist; + tY += -Math.cos(angle) * dist; + var aSize = Math.max(size * 2.5, settings('minArrowSize')), + d = Math.sqrt(Math.pow(tX - sX, 2) + Math.pow(tY - sY, 2)), + aX = sX + (tX - sX) * (d - aSize - tSize) / d, + aY = sY + (tY - sY) * (d - aSize - tSize) / d, + vX = (tX - sX) * aSize / d, + vY = (tY - sY) * aSize / d; + + context.strokeStyle = color; + context.lineWidth = size; + context.beginPath(); + context.moveTo(sX, sY); + context.lineTo( + aX, + aY + ); + context.stroke(); + + context.fillStyle = color; + context.beginPath(); + context.moveTo(aX + vX, aY + vY); + context.lineTo(aX + vY * 0.6, aY - vX * 0.6); + context.lineTo(aX - vY * 0.6, aY + vX * 0.6); + context.lineTo(aX + vX, aY + vY); + context.closePath(); + context.fill(); + } + + drawOnHover(node, context, settings, next) { + var tracer = this; + + context.setLineDash([5, 5]); + var nodeIdx = node.id.substring(1); + this.graph.edges().forEach(function (edge) { + var ends = edge.id.substring(1).split("_"); + if (ends[0] == nodeIdx) { + var color = '#0ff'; + var source = node; + var target = tracer.graph.nodes('n' + ends[1]); + tracer.drawArrow(edge, source, target, color, context, settings); + if (next) next(edge, source, target, color, context, settings); + } else if (ends[1] == nodeIdx) { + var color = '#ff0'; + var source = tracer.graph.nodes('n' + ends[0]); + var target = node; + tracer.drawArrow(edge, source, target, color, context, settings); + if (next) next(edge, source, target, color, context, settings); + } + }); + } +} + +const initView = (tracer) => { + tracer.s = tracer.capsule.s = new sigma({ + renderer: { + container: tracer.$container[0], + type: 'canvas' + }, + settings: { + minArrowSize: 8, + defaultEdgeType: 'arrow', + maxEdgeSize: 2.5, + labelThreshold: 4, + font: 'Roboto', + defaultLabelColor: '#fff', + zoomMin: 0.6, + zoomMax: 1.2, + skipErrors: true, + minNodeSize: .5, + maxNodeSize: 12, + labelSize: 'proportional', + labelSizeRatio: 1.3, + funcLabelsDef(node, context, settings) { + tracer.drawLabel(node, context, settings); + }, + funcHoversDef(node, context, settings, next) { + tracer.drawOnHover(node, context, settings, next); + }, + funcEdgesArrow(edge, source, target, context, settings) { + var color = tracer.getColor(edge, source, target, settings); + tracer.drawArrow(edge, source, target, color, context, settings); + } + } + }); + sigma.plugins.dragNodes(tracer.s, tracer.s.renderers[0]); + tracer.graph = tracer.capsule.graph = tracer.s.graph; +}; + +sigma.canvas.labels.def = function (node, context, settings) { + var func = settings('funcLabelsDef'); + if (func) { + func(node, context, settings); + } +}; +sigma.canvas.hovers.def = function (node, context, settings) { + var func = settings('funcHoversDef'); + if (func) { + func(node, context, settings); + } +}; +sigma.canvas.edges.def = function (edge, source, target, context, settings) { + var func = settings('funcEdgesDef'); + if (func) { + func(edge, source, target, context, settings); + } +}; +sigma.canvas.edges.arrow = function (edge, source, target, context, settings) { + var func = settings('funcEdgesArrow'); + if (func) { + func(edge, source, target, context, settings); + } +}; + +module.exports = DirectedGraphConstructTracer; diff --git a/js/module/tracer/index.js b/js/module/tracer/index.js index 45f82759..d3605202 100644 --- a/js/module/tracer/index.js +++ b/js/module/tracer/index.js @@ -7,6 +7,7 @@ const Array2DTracer = require('./array2d'); const ChartTracer = require('./chart'); const CoordinateSystemTracer = require('./coordinate_system'); const DirectedGraphTracer = require('./directed_graph'); +const DirectedGraphConstructTracer = require('./directed_graph_construct'); const UndirectedGraphTracer = require('./undirected_graph'); const WeightedDirectedGraphTracer = require('./weighted_directed_graph'); const WeightedUndirectedGraphTracer = require('./weighted_undirected_graph'); @@ -19,6 +20,7 @@ module.exports = { ChartTracer, CoordinateSystemTracer, DirectedGraphTracer, + DirectedGraphConstructTracer, UndirectedGraphTracer, WeightedDirectedGraphTracer, WeightedUndirectedGraphTracer From 274ec63129117b8bd25481f3653650fcd171db9b Mon Sep 17 00:00:00 2001 From: Rajkumar Sriramulu Date: Mon, 27 Feb 2017 14:51:22 +0530 Subject: [PATCH 2/3] Removed node collection dependecy from consumed app udpated code to store nodeCollection and root with in the construct tracer --- .../binary_search_tree/bst_insert/code.js | 34 +++++------ .../binary_search_tree/bst_insert/data.js | 5 +- js/module/tracer/directed_graph_construct.js | 58 +++++++++++++------ 3 files changed, 56 insertions(+), 41 deletions(-) diff --git a/algorithm/tree/binary_search_tree/bst_insert/code.js b/algorithm/tree/binary_search_tree/bst_insert/code.js index 8dad4b60..2e9ee31b 100644 --- a/algorithm/tree/binary_search_tree/bst_insert/code.js +++ b/algorithm/tree/binary_search_tree/bst_insert/code.js @@ -1,34 +1,32 @@ -function bst_insert ( root, element, parent ) { // node = current node , parent = previous node +function bst_insert ( root, element, parent ) { // root = current node , parent = previous node tracer._visit ( root, parent )._wait (); + var treeNode = T[root]; + var propName = ''; if ( element < root ) { - if ( T[root][0] === -1 ) { // insert as left child of root - T[root][0] = element; - G[root][element] = 1; // update in graph - tracer._visit ( element, root )._wait (); + propName = 'left'; + } else if ( element > root) { + propName = 'right'; + } + if(propName !== '') { + if ( !treeNode.hasOwnProperty(propName) ) { // insert as left child of root + treeNode[propName] = element; + T[element] = {}; + tracer._addNode(element, root); logger._print( element + ' Inserted '); } else { - //tracer._clear (); - bst_insert ( T[root][0], element, root ); - } - } else if ( element > root ) { // insert as right child of root - if( T[root][1] === -1 ) { - T[root][1] = element; - G[root][element] = 1; // update in graph - tracer._visit ( element, root )._wait (); - logger._print ( element + ' Inserted '); - } else { - //tracer._clear (); - bst_insert ( T[root][1], element, root ); + bst_insert ( treeNode[propName], element, root ); } } } var Root = elements[0]; // take first element as root +T[Root] = {}; +tracer._addRoot(Root); logger._print ( Root + ' Inserted as root of tree '); -tracer._setTreeData ( G, Root ); for (var i = 1; i < elements.length; i++) { tracer2._select ( i )._wait(); bst_insert ( Root, elements[i] ); // insert ith element tracer2._deselect( i ); + tracer.clear(); } diff --git a/algorithm/tree/binary_search_tree/bst_insert/data.js b/algorithm/tree/binary_search_tree/bst_insert/data.js index bbf2cf33..f7f3e256 100644 --- a/algorithm/tree/binary_search_tree/bst_insert/data.js +++ b/algorithm/tree/binary_search_tree/bst_insert/data.js @@ -1,10 +1,7 @@ -var G = []; - - var T = {}; var elements = [5,8,10,3,1,6,9,7,2,0,4]; // item to be searched -var tracer = new DirectedGraphTracer( " BST - Elements marked red indicates the current status of tree "); +var tracer = new DirectedGraphConstructTracer( " BST - Elements marked red indicates the current status of tree ", 0); var tracer2 = new Array1DTracer ( " Elements ")._setData ( elements ); var logger = new LogTracer ( " Log "); tracer.attach ( logger ); \ No newline at end of file diff --git a/js/module/tracer/directed_graph_construct.js b/js/module/tracer/directed_graph_construct.js index 962d0e1d..c89c7e43 100644 --- a/js/module/tracer/directed_graph_construct.js +++ b/js/module/tracer/directed_graph_construct.js @@ -14,10 +14,19 @@ class DirectedGraphConstructTracer extends Tracer { constructor(name, nodePlacement = null) { super(name); this.nodePlacement = nodePlacement; + this.nodeCollection = []; if (this.isNew) initView(this); } - _addNode(G, root, element, parentElement = null) { + _addRoot(root) { + this.manager.pushStep(this.capsule, { + type: 'addRoot', + arguments: arguments + }); + return this; + } + + _addNode(element, parentElement = null) { this.manager.pushStep(this.capsule, { type: 'addNode', arguments: arguments @@ -25,8 +34,9 @@ class DirectedGraphConstructTracer extends Tracer { return this; } - _findNode(G, val) { + _findNode(val) { var idToFind = this.n(val); + var G = this.nodeCollection; var result = null; for (let i = 0; i < G.length; i++) { if(G[i].id === idToFind) { @@ -37,10 +47,9 @@ class DirectedGraphConstructTracer extends Tracer { return result; } - _visit(G, target, source) { + _visit(target, source) { this.manager.pushStep(this.capsule, { type: 'visit', - G: G, target: target, source: source }); @@ -77,13 +86,16 @@ class DirectedGraphConstructTracer extends Tracer { node.y = position.y; }); break; + case 'addRoot': + this.addRoot.apply(this, step.arguments); + break; case 'addNode': this.addNode.apply(this, step.arguments); break; case 'visit': case 'leave': var visit = step.type == 'visit'; - var nodeObject = this._findNode(step.G, step.target); + var nodeObject = this._findNode(step.target); nodeObject.visited = visit; nodeObject.isNew = false; var targetNode = this.graph.nodes(this.n(step.target)); @@ -108,9 +120,20 @@ class DirectedGraphConstructTracer extends Tracer { } } - addNode(G, root, node, parent) { + addRoot(root) { + if(this.rootObject) throw 'Root for this graph is already added'; + this.rootObject = this.createGraphNode(root); + this.drawGraph(this.rootObject.level); + } + + addNode(node, parent) { + var nodeObject = this.createGraphNode(node, parent) + this.drawGraph(nodeObject.level); + } + + createGraphNode(node, parent) { var nodeObject = this.nodeConstruct(node); - var parentObject = this._findNode(G, parent); + var parentObject = this._findNode(parent); if (parentObject) { nodeObject.parent = parentObject; nodeObject.level = parentObject.level + 1; @@ -131,18 +154,14 @@ class DirectedGraphConstructTracer extends Tracer { } if(isSpliced) { parentObject.children.splice(insertIndex, 0, nodeObject); - console.log('exchange Happened'); } else { parentObject.children.push(nodeObject); } } } nodeObject.updateBreadth(); - G.push(nodeObject); - this.drawGraph(G, root, nodeObject.level); - if(node === 4) { - console.log(this.graph.nodes); - } + this.nodeCollection.push(nodeObject); + return nodeObject; } nodeConstruct(val) { @@ -171,7 +190,7 @@ class DirectedGraphConstructTracer extends Tracer { return nodeObject; } - drawGraph(G, root, nodeLevel) { + drawGraph(nodeLevel) { const nodes = []; const edges = []; var tracer = this; @@ -210,9 +229,8 @@ class DirectedGraphConstructTracer extends Tracer { occupiedBreadth += node.breadth; } } - } - var rootObject = this._findNode(G, root); - drawNode(rootObject); + }; + drawNode(this.rootObject); this.graph.clear(); this.graph.read({ @@ -251,8 +269,10 @@ class DirectedGraphConstructTracer extends Tracer { } clearGraphColor() { - var tracer = this; - + this.nodeCollection.forEach(function(node){ + node.isNew = false; + }); + this.graph.nodes().forEach(function (node) { node.color = tracer.color.default; }); From 05465626cceaeeaa898115ac09e592b0a61a9d7d Mon Sep 17 00:00:00 2001 From: Rajkumar Sriramulu Date: Tue, 28 Feb 2017 15:20:32 +0530 Subject: [PATCH 3/3] Updated layout stratergy to draw nodes --- .../binary_search_tree/bst_insert/code.js | 6 +- js/module/tracer/directed_graph_construct.js | 115 ++++++++++-------- 2 files changed, 66 insertions(+), 55 deletions(-) diff --git a/algorithm/tree/binary_search_tree/bst_insert/code.js b/algorithm/tree/binary_search_tree/bst_insert/code.js index 2e9ee31b..f2a2479e 100644 --- a/algorithm/tree/binary_search_tree/bst_insert/code.js +++ b/algorithm/tree/binary_search_tree/bst_insert/code.js @@ -11,7 +11,7 @@ function bst_insert ( root, element, parent ) { // root = current node , parent if ( !treeNode.hasOwnProperty(propName) ) { // insert as left child of root treeNode[propName] = element; T[element] = {}; - tracer._addNode(element, root); + tracer._addNode(element, root)._wait(); logger._print( element + ' Inserted '); } else { bst_insert ( treeNode[propName], element, root ); @@ -27,6 +27,6 @@ logger._print ( Root + ' Inserted as root of tree '); for (var i = 1; i < elements.length; i++) { tracer2._select ( i )._wait(); bst_insert ( Root, elements[i] ); // insert ith element - tracer2._deselect( i ); - tracer.clear(); + tracer2._deselect( i )._wait(); + tracer._clearTraversal(); } diff --git a/js/module/tracer/directed_graph_construct.js b/js/module/tracer/directed_graph_construct.js index c89c7e43..8ccd4e81 100644 --- a/js/module/tracer/directed_graph_construct.js +++ b/js/module/tracer/directed_graph_construct.js @@ -72,11 +72,18 @@ class DirectedGraphConstructTracer extends Tracer { }); return this; } + + _clearTraversal() { + this.manager.pushStep(this.capsule, { + type: 'clear' + }); + return this; + } processStep(step, options) { switch (step.type) { - case 'setTreeData': - this.setTreeData.apply(this, step.arguments); + case 'clear': + this.clear.apply(this); break; case 'setNodePositions': $.each(this.graph.nodes(), (i, node) => { @@ -101,13 +108,13 @@ class DirectedGraphConstructTracer extends Tracer { var targetNode = this.graph.nodes(this.n(step.target)); var color = visit ? this.color.visited : this.color.left; if(targetNode) { - targetNode.color = color; - if (step.source !== undefined) { - var edgeId = this.e(step.source, step.target); - var edge = this.graph.edges(edgeId); - edge.color = color; - this.graph.dropEdge(edgeId).addEdge(edge); - } + targetNode.color = color; + if (step.source !== undefined) { + var edgeId = this.e(step.source, step.target); + var edge = this.graph.edges(edgeId); + edge.color = color; + this.graph.dropEdge(edgeId).addEdge(edge); + } } if (this.logTracer) { var source = step.source; @@ -159,7 +166,6 @@ class DirectedGraphConstructTracer extends Tracer { } } } - nodeObject.updateBreadth(); this.nodeCollection.push(nodeObject); return nodeObject; } @@ -169,23 +175,10 @@ class DirectedGraphConstructTracer extends Tracer { id: this.n(val), originalVal: val, isNew: true, + visited: false, children: [], - breadth: 0, level: 1, - parent: null, - visited: false, - updateBreadth: function() { - var oldBreadth = nodeObject.breadth; - if ( nodeObject.children.length > 0 ) { - nodeObject.breadth = nodeObject.children.length % 2 ? 0 : 1; - for (let j = 0; j < nodeObject.children.length; j++) { - nodeObject.breadth += nodeObject.children[j].breadth; - } - } else { nodeObject.breadth = 1; } - if ( oldBreadth !== nodeObject.breadth && nodeObject.parent ) { - nodeObject.parent.updateBreadth(); - } - } + parent: null } return nodeObject; } @@ -194,43 +187,60 @@ class DirectedGraphConstructTracer extends Tracer { const nodes = []; const edges = []; var tracer = this; - var drawNode = function (node, parentNode, occupiedBreadth) { - var calculatedX = node.breadth; - if (parentNode) { - calculatedX = parentNode.breadth + occupiedBreadth - node.breadth; - } else if (node.children.length > 0) { - calculatedX = Math.ceil(calculatedX/node.children.length); + + var arrangeChildNodes = function(node, offsetWidth) { + if(node.children.length > 1){ + var midPoint = Math.floor(node.children.length / 2); + for (let i = 0; i < node.children.length; i++) { + if (i===midPoint) { + offsetWidth += (node.children.length % 2 === 0 ? 1 : 0); + addGraphNode(node, offsetWidth); + } + offsetWidth = arrangeChildNodes(node.children[i], offsetWidth); + addEdge(node, node.children[i]); + } + } else { + if (node.children.length === 0) { + offsetWidth += 1; + } else { + offsetWidth = arrangeChildNodes(node.children[0], offsetWidth); + addEdge(node, node.children[0]); + } + addGraphNode(node, offsetWidth); } - + return offsetWidth; + }; + + var addGraphNode = function (node, calculatedX) { + var color = getColor(node.isNew, node.visited, tracer.color); nodes.push({ id: node.id, label: '' + node.originalVal, x: calculatedX, y: node.level - 1, size: 1, - color: node.isNew ? tracer.color.selected : (node.visited ? tracer.color.visited : tracer.color.default), + color: color, weight: 0 }); + }; - if ( node.children.length > 0 ) { - var midPoint = node.children.length / 2; - var occupiedBreadth = 0; - for (let j = 0; j < node.children.length; j++) { - var childNode = node.children[j]; - edges.push({ - id: tracer.e(node.originalVal, childNode.originalVal), - source: node.id, - target: childNode.id, - color: node.visited && childNode.visited ? tracer.color.visited : tracer.color.default, - size: 1, - weight: refineByType(childNode.originalVal) - }); - drawNode(childNode, node, occupiedBreadth); - occupiedBreadth += node.breadth; - } - } + var addEdge = function (node, childNode) { + var color = getColor(node.visited && childNode.isNew, node.visited && childNode.visited, tracer.color); + edges.push({ + id: tracer.e(node.originalVal, childNode.originalVal), + source: node.id, + target: childNode.id, + color: color, + size: 1, + weight: refineByType(childNode.originalVal) + }); }; - drawNode(this.rootObject); + + var getColor = function (isNew, isVisited, colorPalete) { + return isNew ? colorPalete.selected : + (isVisited ? colorPalete.visited : colorPalete.default); + }; + arrangeChildNodes(this.rootObject, 0); this.graph.clear(); this.graph.read({ @@ -269,8 +279,9 @@ class DirectedGraphConstructTracer extends Tracer { } clearGraphColor() { + var tracer = this; this.nodeCollection.forEach(function(node){ - node.isNew = false; + node.visited = node.isNew = false; }); this.graph.nodes().forEach(function (node) {