More operations supported.

This commit is contained in:
Paulo Veiga 2011-12-11 23:47:01 -03:00
parent 9c41f0719c
commit c2748f67cf
8 changed files with 245 additions and 83 deletions

View File

@ -28,3 +28,5 @@ mindplot.collaboration = {};
mindplot.collaboration.framework = {}; mindplot.collaboration.framework = {};
mindplot.collaboration.framework.brix = {}; mindplot.collaboration.framework.brix = {};
mindplot.collaboration.framework.brix.model = {}; mindplot.collaboration.framework.brix.model = {};
mindplot.nlayout = {};

View File

@ -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) { computeChildrenIdByHeights: function(treeSet, node) {
throw "Method must be implemented"; throw "Method must be implemented";
}, },
computeOffsets:function(treeSet, node) { computeOffsets:function(treeSet, node) {
throw "Method must be implemented"; 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";
} }
}); });

View File

@ -15,10 +15,11 @@ mindplot.nlayout.LayoutManager = new Class({
updateNodeSize: function(id, size) { updateNodeSize: function(id, size) {
var node = this._treeSet.find(id); var node = this._treeSet.find(id);
node.setSize(size); node.setSize(size);
// @Todo: finish...
}, },
updateShirkState: function(id, isShrink) { updateShirkState: function(id, isShrink) {
// @Todo: finish...
}, },
connectNode: function(parentId, childId, order) { connectNode: function(parentId, childId, order) {
@ -29,12 +30,9 @@ mindplot.nlayout.LayoutManager = new Class({
this._layout.connectNode(parentId, childId, order); this._layout.connectNode(parentId, childId, order);
}, },
disconnectNode: function(sourceId) { disconnectNode: function(id) {
$assert($defined(id), "id can not be null");
}, this._layout.disconnectNode(id);
deleteNode : function(id) {
}, },
addNode:function(id, size, position) { addNode:function(id, size, position) {
@ -43,6 +41,20 @@ mindplot.nlayout.LayoutManager = new Class({
this._treeSet.add(result); 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) { predict: function(parentId, childId, position) {
$assert($defined(parentId), "parentId can not be null"); $assert($defined(parentId), "parentId can not be null");
$assert($defined(childId), "childId 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 parent = this._treeSet.find(parentId);
var sorter = parent.getSorter(); var sorter = parent.getSorter();
var result = sorter.predict(parent, this._treeSet, position); return sorter.predict(parent, this._treeSet, position);
}, },
dump: function() { dump: function() {
@ -64,13 +76,12 @@ mindplot.nlayout.LayoutManager = new Class({
// Collect changes ... // Collect changes ...
this._collectChanges(); this._collectChanges();
if (fireEvents) { if (!$(fireEvents) || fireEvents) {
this.flushEvents(); this._flushEvents();
} }
}, },
flushEvents: function() { _flushEvents: function() {
this._events.forEach(function(event) { this._events.forEach(function(event) {
this.fireEvent('change', event); this.fireEvent('change', event);
}, this); }, this);

View File

@ -1,6 +1,6 @@
mindplot.nlayout.Node = new Class({ mindplot.nlayout.Node = new Class({
initialize:function(id, size, position, sorter) { 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(size, "size can not be null");
$assert(position, "position can not be null"); $assert(position, "position can not be null");
$assert(sorter, "sorter can not be null"); $assert(sorter, "sorter can not be null");
@ -18,7 +18,7 @@ mindplot.nlayout.Node = new Class({
}, },
setOrder: function(order) { 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); this._setProperty('order', order, false);
}, },
@ -64,8 +64,8 @@ mindplot.nlayout.Node = new Class({
setPosition : function(position) { setPosition : function(position) {
$assert($defined(position), "Position can not be null"); $assert($defined(position), "Position can not be null");
$assert(!isNaN(position.x), "x can not be null"); $assert($defined(position.x), "x can not be null");
$assert(!isNaN(position.y), "y can not be null"); $assert($defined(position.y), "y can not be null");
this._setProperty('position', Object.clone(position)); this._setProperty('position', Object.clone(position));
}, },
@ -110,7 +110,7 @@ mindplot.nlayout.Node = new Class({
toString: function() { 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 + "}]";
} }
}); });

View File

@ -1,8 +1,6 @@
mindplot.nlayout.OriginalLayout = new Class({ mindplot.nlayout.OriginalLayout = new Class({
initialize: function(treeSet) { initialize: function(treeSet) {
this._treeSet = treeSet; this._treeSet = treeSet;
this._heightByNode = {};
}, },
createNode:function(id, size, position, type) { createNode:function(id, size, position, type) {
@ -11,7 +9,7 @@ mindplot.nlayout.OriginalLayout = new Class({
$assert(position, "position can not be null"); $assert(position, "position can not be null");
$assert(type, "type 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); 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 parent = this._treeSet.find(parentId);
var child = this._treeSet.find(childId); var child = this._treeSet.find(childId);
// Connect the new node ...
this._treeSet.connect(parentId, childId);
// Insert the new node ... // Insert the new node ...
var sorter = parent.getSorter(); var sorter = parent.getSorter();
sorter.insert(this._treeSet, parent, child, order); sorter.insert(this._treeSet, parent, child, order);
// Connect the new node ...
this._treeSet.connect(parentId, childId);
// Fire a basic validation ... // Fire a basic validation ...
sorter.verify(this._treeSet, parent); 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() { layout: function() {
var roots = this._treeSet.getTreeRoots(); var roots = this._treeSet.getTreeRoots();
roots.forEach(function(node) { roots.forEach(function(node) {
@ -42,12 +56,7 @@ mindplot.nlayout.OriginalLayout = new Class({
var heightById = sorter.computeChildrenIdByHeights(this._treeSet, node); var heightById = sorter.computeChildrenIdByHeights(this._treeSet, node);
this._layoutChildren(node, heightById); this._layoutChildren(node, heightById);
}.bind(this)); }, this);
// Finally, return the list of nodes and properties that has been changed during the layout ...
}, },
_layoutChildren: function(node, heightById) { _layoutChildren: function(node, heightById) {
@ -57,11 +66,11 @@ mindplot.nlayout.OriginalLayout = new Class({
var childrenOrderMoved = children.some(function(child) { var childrenOrderMoved = children.some(function(child) {
return child.hasOrderChanged(); 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 // 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 .... // the same, children nodes must be repositioned ....
var newBranchHeight = heightById[nodeId];
var heightChanged = node._branchHeight != newBranchHeight;
if (childrenOrderMoved || heightChanged) { if (childrenOrderMoved || heightChanged) {
var sorter = node.getSorter(); 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}; var newPos = {x:parentPosition.x + offset.x,y:parentPosition.y + offset.y};
this._treeSet.updateBranchPosition(child, newPos); this._treeSet.updateBranchPosition(child, newPos);
}.bind(this)); }.bind(this));
node._branchHeight = newBranchHeight;
} }
// Continue reordering the children nodes ... // Continue reordering the children nodes ...
children.forEach(function(child) { children.forEach(function(child) {
this._layoutChildren(child, heightById); this._layoutChildren(child, heightById);
}.bind(this)); }, this);
} }
}); });

View File

@ -23,31 +23,32 @@ mindplot.nlayout.RootedTreeSet = new Class({
this._rootNodes.push(this._decodate(node)); this._rootNodes.push(this._decodate(node));
}, },
remove: function(node) { remove: function(nodeId) {
throw "Must be implemted"; $assert($defined(nodeId), 'nodeId can not be null');
var node = this.find(nodeId);
this._rootNodes.erase(node);
}, },
connect: function(parentId, childId) { connect: function(parentId, childId) {
$assert($defined(parentId), 'parent can not be null'); $assert($defined(parentId), 'parent can not be null');
$assert($defined(childId), 'child 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); var child = this.find(childId, true);
$assert(!child._parent, 'node already connected. Id:' + child.getId() + ",previous:" + child._parent); $assert(!child._parent, 'node already connected. Id:' + child.getId() + ",previous:" + child._parent);
parent._children.push(child); parent._children.push(child);
child._parent = parent; child._parent = parent;
this._rootNodes.erase(child); this._rootNodes.erase(child);
}, },
disconnect: function(nodeId) { disconnect: function(nodeId) {
$assert(node, 'node can not be null'); $assert($defined(nodeId), 'nodeId can not be null');
$assert(node._parent, 'child node not connected connected'); var node = this.find(nodeId);
$assert(node._parent, "Node is not connected");
node._parent._children.erase(node); node._parent._children.erase(node);
this._isolated.push(node); this._rootNodes.push(node);
node._parent = null; node._parent = null;
}, },
@ -63,7 +64,7 @@ mindplot.nlayout.RootedTreeSet = new Class({
break; 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; return result;
}, },
@ -91,6 +92,11 @@ mindplot.nlayout.RootedTreeSet = new Class({
return node._children; return node._children;
}, },
getParent:function(node) {
$assert(node, 'node can not be null');
return node._parent;
},
dump: function() { dump: function() {
var branches = this._rootNodes; var branches = this._rootNodes;
var result = ""; var result = "";

View File

@ -65,28 +65,42 @@ mindplot.nlayout.SymetricSorder = new Class({
}, },
insert: function(treeSet, parent, child, order) { 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); $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 . // Shift all the elements in one .
for (var i = order; i < children.length; i++) { for (var i = order; i < children.length; i++) {
var node = children[i]; var node = children[i];
node.setOrder(node.getOrder() + 1); node.setOrder(i + 1);
} }
child.setOrder(order); child.setOrder(order);
}, },
verify:function(treeSet, node) { detach:function(treeSet, node) {
// Check that all is consistent ... var parent = treeSet.getParent(node);
var children = treeSet.getChildren(node); var children = this._getSortedChildren(treeSet, parent);
children.sort(function(a, b) { 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 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++) { for (var i = 0; i < children.length; i++) {
$assert(children[i].getOrder() == i, "missing order elements"); $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(node, "node can no be null.");
$assert("order can no be null."); $assert("order can no be null.");
var children = treeSet.getChildren(node); var children = this._getSortedChildren(treeSet, node);
children.sort(function(a, b) {
return a.getOrder() - b.getOrder()
});
// Compute heights ... // Compute heights ...
var heights = children.map(function(child) { var heights = children.map(function(child) {

View File

@ -2,10 +2,11 @@ mindplot.nlayout.TestSuite = new Class({
Extends: mindplot.nlayout.ChildrenSorterStrategy, Extends: mindplot.nlayout.ChildrenSorterStrategy,
initialize:function() { initialize:function() {
// this.testAligned(); this.testAligned();
this.testEvents(); this.testEvents();
this.testEventsComplex();
// @ Agregar tests que garantice que no se reposicional cosan inecesariamente 2 veces... this.testDisconnect();
this.testRemoveNode();
}, },
testAligned: function() { testAligned: function() {
@ -37,26 +38,15 @@ mindplot.nlayout.TestSuite = new Class({
manager.connectNode(0, 2, 0); manager.connectNode(0, 2, 0);
manager.connectNode(1, 3, 0); manager.connectNode(1, 3, 0);
// Reposition ... // Basic layout repositioning ...
manager.layout(); console.log("-- Updated tree ---");
console.log("Updated tree:");
manager.dump();
// Listen for changes ...
console.log("Updated nodes ...");
var events = []; var events = [];
manager.addEvent('change', function(event) { manager.addEvent('change', function(event) {
console.log("Updated nodes: {id:" + event.getId() + ", order: " + event.getOrder() + ",position: {" + event.getPosition().x + "," + event.getPosition().y + "}"); console.log("Updated nodes: {id:" + event.getId() + ", order: " + event.getOrder() + ",position: {" + event.getPosition().x + "," + event.getPosition().y + "}");
events.push(event); events.push(event);
}); });
manager.flushEvents(); manager.layout(true);
manager.dump();
// Second flush must not fire events ...
console.log("---- Test Flush ---");
events.empty();
manager.flushEvents();
$assert(events.length == 0, "Event should not be fire twice.");
// Ok, if a new node is added, this an event should be fired ... // Ok, if a new node is added, this an event should be fired ...
console.log("---- Layout without changes should not affect the tree ---"); console.log("---- Layout without changes should not affect the tree ---");
@ -64,7 +54,130 @@ mindplot.nlayout.TestSuite = new Class({
manager.layout(true); manager.layout(true);
$assert(events.length == 0, "Unnecessary tree updated."); $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();
} }
}); });