From c2748f67cf8091827ab30447ae6e4cab15fe1b81 Mon Sep 17 00:00:00 2001 From: Paulo Veiga Date: Sun, 11 Dec 2011 23:47:01 -0300 Subject: [PATCH] More operations supported. --- mindplot/src/main/javascript/header.js | 4 +- .../nlayout/ChildrenSorterStrategy.js | 24 ++- .../main/javascript/nlayout/LayoutManager.js | 35 ++-- mindplot/src/main/javascript/nlayout/Node.js | 10 +- .../main/javascript/nlayout/OriginalLayout.js | 41 +++-- .../main/javascript/nlayout/RootedTreeSet.js | 24 +-- .../main/javascript/nlayout/SymetricSorter.js | 41 +++-- .../src/main/javascript/nlayout/TestSuite.js | 149 +++++++++++++++--- 8 files changed, 245 insertions(+), 83 deletions(-) diff --git a/mindplot/src/main/javascript/header.js b/mindplot/src/main/javascript/header.js index fb4fb674..03e446a9 100644 --- a/mindplot/src/main/javascript/header.js +++ b/mindplot/src/main/javascript/header.js @@ -27,4 +27,6 @@ mindplot.model = {}; mindplot.collaboration = {}; mindplot.collaboration.framework = {}; mindplot.collaboration.framework.brix = {}; -mindplot.collaboration.framework.brix.model = {}; \ No newline at end of file +mindplot.collaboration.framework.brix.model = {}; + +mindplot.nlayout = {}; diff --git a/mindplot/src/main/javascript/nlayout/ChildrenSorterStrategy.js b/mindplot/src/main/javascript/nlayout/ChildrenSorterStrategy.js index 37c362c0..8241295a 100644 --- a/mindplot/src/main/javascript/nlayout/ChildrenSorterStrategy.js +++ b/mindplot/src/main/javascript/nlayout/ChildrenSorterStrategy.js @@ -3,21 +3,29 @@ mindplot.nlayout.ChildrenSorterStrategy = new Class({ }, - predict:function(treeSet, parent, position) { - throw "Method must be implemented"; - }, - - sorter: function(treeSet, parent, child, order) { - throw "Method must be implemented"; - }, - computeChildrenIdByHeights: function(treeSet, node) { throw "Method must be implemented"; }, computeOffsets:function(treeSet, node) { throw "Method must be implemented"; + }, + insert: function(treeSet, parent, child, order) { + throw "Method must be implemented"; + }, + + detach:function(treeSet, node) { + throw "Method must be implemented"; + }, + + predict:function(treeSet, parent, position) { + throw "Method must be implemented"; + }, + + verify:function(treeSet, node) { + throw "Method must be implemented"; } + }); diff --git a/mindplot/src/main/javascript/nlayout/LayoutManager.js b/mindplot/src/main/javascript/nlayout/LayoutManager.js index ad7c2ba3..86669ee2 100644 --- a/mindplot/src/main/javascript/nlayout/LayoutManager.js +++ b/mindplot/src/main/javascript/nlayout/LayoutManager.js @@ -15,10 +15,11 @@ mindplot.nlayout.LayoutManager = new Class({ updateNodeSize: function(id, size) { var node = this._treeSet.find(id); node.setSize(size); + // @Todo: finish... }, updateShirkState: function(id, isShrink) { - + // @Todo: finish... }, connectNode: function(parentId, childId, order) { @@ -29,12 +30,9 @@ mindplot.nlayout.LayoutManager = new Class({ this._layout.connectNode(parentId, childId, order); }, - disconnectNode: function(sourceId) { - - }, - - deleteNode : function(id) { - + disconnectNode: function(id) { + $assert($defined(id), "id can not be null"); + this._layout.disconnectNode(id); }, addNode:function(id, size, position) { @@ -43,6 +41,20 @@ mindplot.nlayout.LayoutManager = new Class({ this._treeSet.add(result); }, + removeNode: function(id) { + $assert($defined(id), "id can not be null"); + var node = this._treeSet.find(id); + + // Is It connected ? + if (this._treeSet.getParent(node)) { + this.disconnectNode(id); + } + + // Remove the all the branch ... + this._treeSet.remove(id); + + }, + predict: function(parentId, childId, position) { $assert($defined(parentId), "parentId can not be null"); $assert($defined(childId), "childId can not be null"); @@ -50,7 +62,7 @@ mindplot.nlayout.LayoutManager = new Class({ var parent = this._treeSet.find(parentId); var sorter = parent.getSorter(); - var result = sorter.predict(parent, this._treeSet, position); + return sorter.predict(parent, this._treeSet, position); }, dump: function() { @@ -64,13 +76,12 @@ mindplot.nlayout.LayoutManager = new Class({ // Collect changes ... this._collectChanges(); - if (fireEvents) { - this.flushEvents(); + if (!$(fireEvents) || fireEvents) { + this._flushEvents(); } - }, - flushEvents: function() { + _flushEvents: function() { this._events.forEach(function(event) { this.fireEvent('change', event); }, this); diff --git a/mindplot/src/main/javascript/nlayout/Node.js b/mindplot/src/main/javascript/nlayout/Node.js index 619d5a05..e9f88de5 100644 --- a/mindplot/src/main/javascript/nlayout/Node.js +++ b/mindplot/src/main/javascript/nlayout/Node.js @@ -1,6 +1,6 @@ mindplot.nlayout.Node = new Class({ initialize:function(id, size, position, sorter) { - $assert(!isNaN(id), "id can not be null"); + $assert(typeof id === 'number' && isFinite(id), "id can not be null"); $assert(size, "size can not be null"); $assert(position, "position can not be null"); $assert(sorter, "sorter can not be null"); @@ -18,7 +18,7 @@ mindplot.nlayout.Node = new Class({ }, setOrder: function(order) { - $assert(!isNaN(order), "Order can not be null"); + $assert(typeof order === 'number' && isFinite(order), "Order can not be null. Value:" + order); this._setProperty('order', order, false); }, @@ -64,8 +64,8 @@ mindplot.nlayout.Node = new Class({ setPosition : function(position) { $assert($defined(position), "Position can not be null"); - $assert(!isNaN(position.x), "x can not be null"); - $assert(!isNaN(position.y), "y can not be null"); + $assert($defined(position.x), "x can not be null"); + $assert($defined(position.y), "y can not be null"); this._setProperty('position', Object.clone(position)); }, @@ -110,7 +110,7 @@ mindplot.nlayout.Node = new Class({ toString: function() { - return "[order:" + this.getOrder() + ", position: {" + this.getPosition().x + "," + this.getPosition().y + "}]"; + return "[id:" + this.getId() + ", order:" + this.getOrder() + ", position: {" + this.getPosition().x + "," + this.getPosition().y + "}]"; } }); diff --git a/mindplot/src/main/javascript/nlayout/OriginalLayout.js b/mindplot/src/main/javascript/nlayout/OriginalLayout.js index dc5d1c84..435b9038 100644 --- a/mindplot/src/main/javascript/nlayout/OriginalLayout.js +++ b/mindplot/src/main/javascript/nlayout/OriginalLayout.js @@ -1,8 +1,6 @@ mindplot.nlayout.OriginalLayout = new Class({ initialize: function(treeSet) { this._treeSet = treeSet; - - this._heightByNode = {}; }, createNode:function(id, size, position, type) { @@ -11,7 +9,7 @@ mindplot.nlayout.OriginalLayout = new Class({ $assert(position, "position can not be null"); $assert(type, "type can not be null"); - var strategy = type === 'root' ? mindplot.nlayout.OriginalLayout.GRID_SORTER : mindplot.nlayout.OriginalLayout.SYMETRIC_SORTER; + var strategy = type === 'root' ? mindplot.nlayout.OriginalLayout.GRID_SORTER : mindplot.nlayout.OriginalLayout.SYMETRIC_SORTER; return new mindplot.nlayout.Node(id, size, position, strategy); }, @@ -20,17 +18,33 @@ mindplot.nlayout.OriginalLayout = new Class({ var parent = this._treeSet.find(parentId); var child = this._treeSet.find(childId); - // Connect the new node ... - this._treeSet.connect(parentId, childId); - // Insert the new node ... var sorter = parent.getSorter(); sorter.insert(this._treeSet, parent, child, order); + // Connect the new node ... + this._treeSet.connect(parentId, childId); + // Fire a basic validation ... sorter.verify(this._treeSet, parent); }, + disconnectNode: function(nodeId) { + var node = this._treeSet.find(nodeId); + $assert(this._treeSet.getParent(node),"Node already disconnected"); + + // Remove from children list. + var sorter = node.getSorter(); + sorter.detach(this._treeSet, node); + + // Disconnect the new node ... + this._treeSet.disconnect(nodeId); + + // Fire a basic validation ... + sorter.verify(this._treeSet, node); + + }, + layout: function() { var roots = this._treeSet.getTreeRoots(); roots.forEach(function(node) { @@ -42,12 +56,7 @@ mindplot.nlayout.OriginalLayout = new Class({ var heightById = sorter.computeChildrenIdByHeights(this._treeSet, node); this._layoutChildren(node, heightById); - }.bind(this)); - - // Finally, return the list of nodes and properties that has been changed during the layout ... - - - + }, this); }, _layoutChildren: function(node, heightById) { @@ -57,11 +66,11 @@ mindplot.nlayout.OriginalLayout = new Class({ var childrenOrderMoved = children.some(function(child) { return child.hasOrderChanged(); }); - var heightChanged = this._heightByNode[nodeId] != heightById[nodeId]; - throw "Esto no esta bien:"+ this._heightByNode; // If ether any of the nodes has been changed of position or the height of the children is not // the same, children nodes must be repositioned .... + var newBranchHeight = heightById[nodeId]; + var heightChanged = node._branchHeight != newBranchHeight; if (childrenOrderMoved || heightChanged) { var sorter = node.getSorter(); @@ -73,12 +82,14 @@ mindplot.nlayout.OriginalLayout = new Class({ var newPos = {x:parentPosition.x + offset.x,y:parentPosition.y + offset.y}; this._treeSet.updateBranchPosition(child, newPos); }.bind(this)); + + node._branchHeight = newBranchHeight; } // Continue reordering the children nodes ... children.forEach(function(child) { this._layoutChildren(child, heightById); - }.bind(this)); + }, this); } }); diff --git a/mindplot/src/main/javascript/nlayout/RootedTreeSet.js b/mindplot/src/main/javascript/nlayout/RootedTreeSet.js index d039fb59..33e13bde 100644 --- a/mindplot/src/main/javascript/nlayout/RootedTreeSet.js +++ b/mindplot/src/main/javascript/nlayout/RootedTreeSet.js @@ -23,31 +23,32 @@ mindplot.nlayout.RootedTreeSet = new Class({ this._rootNodes.push(this._decodate(node)); }, - remove: function(node) { - throw "Must be implemted"; + remove: function(nodeId) { + $assert($defined(nodeId), 'nodeId can not be null'); + var node = this.find(nodeId); + this._rootNodes.erase(node); }, connect: function(parentId, childId) { $assert($defined(parentId), 'parent can not be null'); $assert($defined(childId), 'child can not be null'); - var parent = this.find(parentId, true); + var parent = this.find(parentId); var child = this.find(childId, true); $assert(!child._parent, 'node already connected. Id:' + child.getId() + ",previous:" + child._parent); - parent._children.push(child); child._parent = parent; this._rootNodes.erase(child); }, disconnect: function(nodeId) { - $assert(node, 'node can not be null'); - $assert(node._parent, 'child node not connected connected'); + $assert($defined(nodeId), 'nodeId can not be null'); + var node = this.find(nodeId); + $assert(node._parent, "Node is not connected"); node._parent._children.erase(node); - this._isolated.push(node); - + this._rootNodes.push(node); node._parent = null; }, @@ -63,7 +64,7 @@ mindplot.nlayout.RootedTreeSet = new Class({ break; } } - $assert(validate ? result : true, 'node could not be found id:' + id); + $assert($defined(validate) ? result : true, 'node could not be found id:' + id); return result; }, @@ -91,6 +92,11 @@ mindplot.nlayout.RootedTreeSet = new Class({ return node._children; }, + getParent:function(node) { + $assert(node, 'node can not be null'); + return node._parent; + }, + dump: function() { var branches = this._rootNodes; var result = ""; diff --git a/mindplot/src/main/javascript/nlayout/SymetricSorter.js b/mindplot/src/main/javascript/nlayout/SymetricSorter.js index 11f635c8..6b391135 100644 --- a/mindplot/src/main/javascript/nlayout/SymetricSorter.js +++ b/mindplot/src/main/javascript/nlayout/SymetricSorter.js @@ -65,28 +65,42 @@ mindplot.nlayout.SymetricSorder = new Class({ }, insert: function(treeSet, parent, child, order) { - var children = treeSet.getChildren(parent); + var children = this._getSortedChildren(treeSet, parent); $assert(order <= children.length, "Order must be continues and can not have holes. Order:" + order); - // Sort array list .. - children.sort(function(a, b) { - return a.getOrder() - b.getOrder() - }); - // Shift all the elements in one . for (var i = order; i < children.length; i++) { var node = children[i]; - node.setOrder(node.getOrder() + 1); + node.setOrder(i + 1); } child.setOrder(order); }, - verify:function(treeSet, node) { - // Check that all is consistent ... - var children = treeSet.getChildren(node); - children.sort(function(a, b) { + detach:function(treeSet, node) { + var parent = treeSet.getParent(node); + var children = this._getSortedChildren(treeSet, parent); + var order = node.getOrder(); + $assert(children[order] === node, "Node seems not to be in the right position"); + + // Shift all the nodes ... + for (var i = node.getOrder() + 1; i < children.length; i++) { + var child = children[i]; + child.setOrder(child.getOrder() - 1); + } + node.setOrder(0); + }, + + _getSortedChildren:function(treeSet, node) { + var result = treeSet.getChildren(node); + result.sort(function(a, b) { return a.getOrder() - b.getOrder() }); + return result; + }, + + verify:function(treeSet, node) { + // Check that all is consistent ... + var children = this._getSortedChildren(treeSet, node); for (var i = 0; i < children.length; i++) { $assert(children[i].getOrder() == i, "missing order elements"); @@ -98,10 +112,7 @@ mindplot.nlayout.SymetricSorder = new Class({ $assert(node, "node can no be null."); $assert("order can no be null."); - var children = treeSet.getChildren(node); - children.sort(function(a, b) { - return a.getOrder() - b.getOrder() - }); + var children = this._getSortedChildren(treeSet, node); // Compute heights ... var heights = children.map(function(child) { diff --git a/mindplot/src/main/javascript/nlayout/TestSuite.js b/mindplot/src/main/javascript/nlayout/TestSuite.js index 7bb0e937..cfc59cb8 100644 --- a/mindplot/src/main/javascript/nlayout/TestSuite.js +++ b/mindplot/src/main/javascript/nlayout/TestSuite.js @@ -2,10 +2,11 @@ mindplot.nlayout.TestSuite = new Class({ Extends: mindplot.nlayout.ChildrenSorterStrategy, initialize:function() { -// this.testAligned(); + this.testAligned(); this.testEvents(); - - // @ Agregar tests que garantice que no se reposicional cosan inecesariamente 2 veces... + this.testEventsComplex(); + this.testDisconnect(); + this.testRemoveNode(); }, testAligned: function() { @@ -37,26 +38,15 @@ mindplot.nlayout.TestSuite = new Class({ manager.connectNode(0, 2, 0); manager.connectNode(1, 3, 0); - // Reposition ... - manager.layout(); - console.log("Updated tree:"); - manager.dump(); - - // Listen for changes ... - console.log("Updated nodes ..."); + // Basic layout repositioning ... + console.log("-- Updated tree ---"); var events = []; manager.addEvent('change', function(event) { console.log("Updated nodes: {id:" + event.getId() + ", order: " + event.getOrder() + ",position: {" + event.getPosition().x + "," + event.getPosition().y + "}"); events.push(event); }); - manager.flushEvents(); - - // Second flush must not fire events ... - console.log("---- Test Flush ---"); - - events.empty(); - manager.flushEvents(); - $assert(events.length == 0, "Event should not be fire twice."); + manager.layout(true); + manager.dump(); // Ok, if a new node is added, this an event should be fired ... console.log("---- Layout without changes should not affect the tree ---"); @@ -64,7 +54,130 @@ mindplot.nlayout.TestSuite = new Class({ manager.layout(true); $assert(events.length == 0, "Unnecessary tree updated."); + }, + + testEventsComplex: function() { + var size = {width:10,height:10}; + var position = {x:0,y:0}; + var manager = new mindplot.nlayout.LayoutManager(0, size); + + // Add 3 nodes... + manager.addNode(1, size, position); + manager.addNode(2, size, position); + manager.addNode(3, size, position); + manager.addNode(4, size, position); + + // Now connect one with two.... + manager.connectNode(0, 1, 0); + manager.connectNode(1, 2, 0); + manager.connectNode(1, 3, 1); + + var events = []; + manager.addEvent('change', function(event) { + console.log("Updated nodes: {id:" + event.getId() + ", order: " + event.getOrder() + ",position: {" + event.getPosition().x + "," + event.getPosition().y + "}"); + events.push(event); + }); + + // Reposition ... + manager.layout(true); + manager.dump(); + + // Add a new node and connect. Only children nodes should be affected. + console.log("---- Connect a new node ---"); + + events.empty(); + manager.connectNode(1, 4, 2); + manager.layout(true); + manager.dump(); + + // @todo: This seems no to be ok... + $assert(events.length == 4, "Only 3 nodes should be repositioned."); + }, + + testDisconnect: function() { + var size = {width:10,height:10}; + var position = {x:0,y:0}; + var manager = new mindplot.nlayout.LayoutManager(0, size); + + // Prepare a sample graph ... + manager.addNode(1, size, position); + manager.addNode(2, size, position); + manager.addNode(3, size, position); + manager.addNode(4, size, position); + + manager.connectNode(0, 1, 0); + manager.connectNode(1, 2, 0); + manager.connectNode(1, 3, 1); + manager.connectNode(3, 4, 0); + + var events = []; + manager.addEvent('change', function(event) { + var pos = event.getPosition(); + var posStr = pos ? ",position: {" + pos.x + "," + event.getPosition().y : ""; + console.log("Updated nodes: {id:" + event.getId() + ", order: " + event.getOrder() + posStr + "}"); + events.push(event); + }); + + manager.layout(true); + manager.dump(); + + // Now, disconnect one node ... + console.log("--- Disconnect a single node ---"); + events.empty(); + manager.disconnectNode(2); + manager.layout(true); + manager.dump(); + + $assert(events.some( + function(event) { + return event.getId() == 2; + }), "Event for disconnected node seems not to be propagated"); + + // Great, let's disconnect a not with children. + console.log("--- Disconnect a node with children ---"); + manager.disconnectNode(3); + manager.layout(true); + manager.dump(); + + $assert(events.some( + function(event) { + return event.getId() == 2; + }), "Event for disconnected node seems not to be propagated"); + }, + + testRemoveNode: function() { + var size = {width:10,height:10}; + var position = {x:0,y:0}; + var manager = new mindplot.nlayout.LayoutManager(0, size); + + // Prepare a sample graph ... + manager.addNode(1, size, position); + manager.addNode(2, size, position); + manager.addNode(3, size, position); + manager.addNode(4, size, position); + + manager.connectNode(0, 1, 0); + manager.connectNode(1, 2, 0); + manager.connectNode(1, 3, 1); + manager.connectNode(3, 4, 0); + + var events = []; + manager.addEvent('change', function(event) { + var pos = event.getPosition(); + var posStr = pos ? ",position: {" + pos.x + "," + event.getPosition().y : ""; + console.log("Updated nodes: {id:" + event.getId() + ", order: " + event.getOrder() + posStr + "}"); + events.push(event); + }); + manager.layout(true); + manager.dump(); + + // Test removal of a connected node ... + console.log("--- Remove node 3 ---"); + manager.removeNode(3); + manager.layout(true); + manager.dump(); } + });