Close the first prototype of the new layout

This commit is contained in:
Paulo Veiga 2011-12-11 13:13:43 -03:00
parent a482c410a2
commit 9c41f0719c
10 changed files with 780 additions and 0 deletions

View File

@ -0,0 +1,36 @@
mindplot.nlayout.ChangeEvent = new Class({
initialize:function(id) {
$assert(!isNaN(id), "id can not be null");
this._id = id;
this._position = null;
this._order = null;
},
getId:function() {
return this._id;
},
getOrder: function() {
return this._order;
},
getPosition: function() {
return this._position;
},
setOrder: function(value) {
$assert(!isNaN(value), "value can not be null");
this._order = value;
},
setPosition: function(value) {
$assert(value, "value can not be null");
this._position = value;
},
toString: function() {
return "[order:" + this.getOrder() + ", position: {" + this.getPosition().x + "," + this.getPosition().y + "}]";
}
});

View File

@ -0,0 +1,23 @@
mindplot.nlayout.ChildrenSorterStrategy = new Class({
initialize:function() {
},
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";
}
});

View File

@ -0,0 +1,8 @@
mindplot.nlayout.GridSorter = new Class({
Extends: mindplot.nlayout.SymetricSorder
});
mindplot.nlayout.GridSorter.GRID_HORIZONTAR_SIZE = 50;
mindplot.nlayout.GridSorter.INTER_NODE_VERTICAL_DISTANCE = 50;

View File

@ -0,0 +1,115 @@
mindplot.nlayout.LayoutManager = new Class({
Extends: Events,
initialize: function(rootNodeId, rootSize) {
$assert($defined(rootNodeId), "rootNodeId can not be null");
$assert(rootSize, "rootSize can not be null");
this._treeSet = new mindplot.nlayout.RootedTreeSet();
this._layout = new mindplot.nlayout.OriginalLayout(this._treeSet);
var rootNode = this._layout.createNode(rootNodeId, rootSize, {x:0,y:0}, 'root');
this._treeSet.setRoot(rootNode);
this._events = [];
},
updateNodeSize: function(id, size) {
var node = this._treeSet.find(id);
node.setSize(size);
},
updateShirkState: function(id, isShrink) {
},
connectNode: function(parentId, childId, order) {
$assert($defined(parentId), "parentId can not be null");
$assert($defined(childId), "childId can not be null");
$assert($defined(order), "order can not be null");
this._layout.connectNode(parentId, childId, order);
},
disconnectNode: function(sourceId) {
},
deleteNode : function(id) {
},
addNode:function(id, size, position) {
$assert($defined(id), "id can not be null");
var result = this._layout.createNode(id, size, position, 'topic');
this._treeSet.add(result);
},
predict: function(parentId, childId, position) {
$assert($defined(parentId), "parentId can not be null");
$assert($defined(childId), "childId can not be null");
$assert(position, "childId can not be null");
var parent = this._treeSet.find(parentId);
var sorter = parent.getSorter();
var result = sorter.predict(parent, this._treeSet, position);
},
dump: function() {
console.log(this._treeSet.dump());
},
layout: function(fireEvents) {
// File repositioning ...
this._layout.layout();
// Collect changes ...
this._collectChanges();
if (fireEvents) {
this.flushEvents();
}
},
flushEvents: function() {
this._events.forEach(function(event) {
this.fireEvent('change', event);
}, this);
this._events = [];
},
_collectChanges: function(nodes) {
if (!nodes)
nodes = this._treeSet.getTreeRoots();
nodes.forEach(function(node) {
if (node.hasOrderChanged() || node.hasPositionChanged()) {
// Find or create a event ...
var id = node.getId();
var event = this._events.some(function(event) {
return event.id == id;
});
if (!event) {
event = new mindplot.nlayout.ChangeEvent(id);
}
// Update nodes ...
if (node.hasOrderChanged()) {
event.setOrder(node.getOrder());
node.resetOrderState();
}
if (node.hasPositionChanged()) {
event.setPosition(node.getPosition());
node.resetPositionState();
}
this._events.push(event);
}
this._collectChanges(this._treeSet.getChildren(node));
}, this);
}
});

View File

@ -0,0 +1,117 @@
mindplot.nlayout.Node = new Class({
initialize:function(id, size, position, sorter) {
$assert(!isNaN(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");
this._id = id;
this._sorter = sorter;
this._properties = {};
this.setSize(size);
this.setPosition(position);
},
getId:function() {
return this._id;
},
setOrder: function(order) {
$assert(!isNaN(order), "Order can not be null");
this._setProperty('order', order, false);
},
resetPositionState : function() {
var prop = this._properties['position'];
if (prop) {
prop.hasChanded = false;
}
},
resetOrderState : function() {
var prop = this._properties['order'];
if (prop) {
prop.hasChanded = false;
}
},
getOrder: function() {
return this._getProperty('order');
},
hasOrderChanged: function() {
return this._isPropertyChanged('order');
},
hasPositionChanged: function() {
return this._isPropertyChanged('position');
},
getPosition: function() {
return this._getProperty('position');
},
setSize : function(size) {
$assert($defined(size), "Size can not be null");
this._setProperty('size', Object.clone(size));
},
getSize: function() {
return this._getProperty('size');
},
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");
this._setProperty('position', Object.clone(position));
},
_setProperty: function(key, value) {
var prop = this._properties[key];
if (!prop) {
prop = {
hasChanded:false,
value: null,
oldValue : null
};
}
prop.oldValue = prop.value;
prop.value = value;
prop.hasChanded = true;
this._properties[key] = prop;
},
_getProperty: function(key) {
var prop = this._properties[key];
return $defined(prop) ? prop.value : null;
},
_isPropertyChanged: function(key) {
var prop = this._properties[key];
return prop ? prop.hasChanded : false;
},
_setPropertyUpdated : function(key) {
var prop = this._properties[key];
if (prop) {
this._properties[key] = true;
}
},
getSorter: function() {
return this._sorter;
},
toString: function() {
return "[order:" + this.getOrder() + ", position: {" + this.getPosition().x + "," + this.getPosition().y + "}]";
}
});

View File

@ -0,0 +1,90 @@
mindplot.nlayout.OriginalLayout = new Class({
initialize: function(treeSet) {
this._treeSet = treeSet;
this._heightByNode = {};
},
createNode:function(id, size, position, type) {
$assert($defined(id), "id can not be null");
$assert(size, "size can not be null");
$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;
return new mindplot.nlayout.Node(id, size, position, strategy);
},
connectNode: function(parentId, childId, order) {
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);
// Fire a basic validation ...
sorter.verify(this._treeSet, parent);
},
layout: function() {
var roots = this._treeSet.getTreeRoots();
roots.forEach(function(node) {
// Calculate all node heights ...
var sorter = node.getSorter();
// @Todo: This must not be implemented in this way.Each sorter could have different notion of heights ...
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 ...
},
_layoutChildren: function(node, heightById) {
var nodeId = node.getId();
var children = this._treeSet.getChildren(node);
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 ....
if (childrenOrderMoved || heightChanged) {
var sorter = node.getSorter();
var offsetById = sorter.computeOffsets(this._treeSet, node);
var parentPosition = node.getPosition();
children.forEach(function(child) {
var offset = offsetById[child.getId()];
var newPos = {x:parentPosition.x + offset.x,y:parentPosition.y + offset.y};
this._treeSet.updateBranchPosition(child, newPos);
}.bind(this));
}
// Continue reordering the children nodes ...
children.forEach(function(child) {
this._layoutChildren(child, heightById);
}.bind(this));
}
});
mindplot.nlayout.OriginalLayout.SYMETRIC_SORTER = new mindplot.nlayout.SymetricSorder();
mindplot.nlayout.OriginalLayout.GRID_SORTER = new mindplot.nlayout.SymetricSorder();

View File

@ -0,0 +1,141 @@
mindplot.nlayout.RootedTreeSet = new Class({
initialize:function() {
this._rootNodes = [];
},
setRoot:function(root) {
$assert(root, 'root can not be null');
this._rootNodes.push(this._decodate(root));
},
getTreeRoots:function() {
return this._rootNodes;
},
_decodate:function(node) {
node._children = [];
return node;
},
add: function(node) {
$assert(node, 'node can not be null');
$assert(!node._children, 'node already added');
this._rootNodes.push(this._decodate(node));
},
remove: function(node) {
throw "Must be implemted";
},
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 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');
node._parent._children.erase(node);
this._isolated.push(node);
node._parent = null;
},
find:function(id, validate) {
$assert($defined(id), 'id can not be null');
var graphs = this._rootNodes;
var result = null;
for (var i = 0; i < graphs.length; i++) {
var node = graphs[i];
result = this._find(id, node);
if (result) {
break;
}
}
$assert(validate ? result : true, 'node could not be found id:' + id);
return result;
},
_find:function(id, parent) {
if (parent.getId() == id) {
return parent;
}
var result = null;
var children = parent._children;
for (var i = 0; i < children.length; i++) {
var child = children[i];
result = this._find(id, child);
if (result)
break;
}
return result;
},
getChildren:function(node) {
$assert(node, 'node can not be null');
return node._children;
},
dump: function() {
var branches = this._rootNodes;
var result = "";
for (var i = 0; i < branches.length; i++) {
var branch = branches[i];
result += this._dump(branch, "");
}
return result;
},
_dump:function(node, indent) {
var result = indent + node + "\n";
var children = this.getChildren(node);
for (var i = 0; i < children.length; i++) {
var child = children[i];
result += this._dump(child, indent + " ");
}
return result;
},
updateBranchPosition : function(node, position) {
var oldPos = node.getPosition();
node.setPosition(position);
var xOffset = oldPos.x - position.x;
var yOffset = oldPos.y - position.y;
var children = this.getChildren(node);
children.forEach(function(child) {
this._shiftBranchPosition(child, xOffset, yOffset);
}.bind(this));
},
_shiftBranchPosition : function(node, xOffset, yOffset) {
var position = node.getPosition();
node.setPosition({x:position.x + xOffset, y:position.y + yOffset});
var children = this.getChildren(node);
children.forEach(function(child) {
this._shiftBranchPosition(child, xOffset, yOffset);
}.bind(this));
}
});

View File

@ -0,0 +1,139 @@
mindplot.nlayout.SymetricSorder = new Class({
Extends: mindplot.nlayout.ChildrenSorterStrategy,
initialize:function() {
},
computeChildrenIdByHeights: function(treeSet, node) {
var result = {};
this._computeChildrenHeight(treeSet, node, result);
return result;
},
_computeChildrenHeight : function(treeSet, node, heightCache) {
var height = node.getSize().height + (mindplot.nlayout.SymetricSorder.INTERNODE_VERTICAL_PADDING * 2); // 2* Top and down padding;
var result;
var children = treeSet.getChildren(node);
if (children.length == 0) {
result = height;
} else {
var childrenHeight = 0;
children.forEach(function(child) {
childrenHeight += this._computeChildrenHeight(treeSet, child, heightCache);
}, this);
result = Math.max(height, childrenHeight);
}
if (heightCache) {
heightCache[node.getId()] = result;
}
return result;
},
predict : function(parent, graph, position) {
// No children...
var children = graph.getChildren(parent);
if (children.length == 0) {
return [0,parent.getPosition()];
}
// Try to fit within ...
//
// - Order is change if the position top position is changed ...
// - Suggested position is the middle bitween the two topics...
//
var result = null;
children.forEach(function(child) {
var cpos = child.getPosition();
if (position.y > cpos.y) {
result = [child.getOrder(),{x:cpos.x,y:cpos.y + child.getSize().height}];
}
});
// Ok, no overlap. Suggest a new order.
if (result) {
var last = children.getLast();
result = [last.getOrder() + 1,{x:cpos.x,y:cpos.y - (mindplot.nlayout.SymetricSorder.INTERNODE_VERTICAL_PADDING * 4)}];
}
return result;
},
insert: function(treeSet, parent, child, order) {
var children = treeSet.getChildren(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);
}
child.setOrder(order);
},
verify:function(treeSet, node) {
// Check that all is consistent ...
var children = treeSet.getChildren(node);
children.sort(function(a, b) {
return a.getOrder() - b.getOrder()
});
for (var i = 0; i < children.length; i++) {
$assert(children[i].getOrder() == i, "missing order elements");
}
},
computeOffsets:function(treeSet, node) {
$assert(treeSet, "treeSet can no be null.");
$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()
});
// Compute heights ...
var heights = children.map(function(child) {
return {id:child.getId(),height:this._computeChildrenHeight(treeSet, child)};
}.bind(this));
// Compute the center of the branch ...
var totalHeight = 0;
heights.forEach(function(elem) {
totalHeight += elem.height;
});
var ysum = totalHeight / 2;
// Calculate the offsets ...
var result = {};
for (var i = 0; i < heights.length; i++) {
ysum = ysum - heights[i].height;
var yOffset = ysum + mindplot.nlayout.SymetricSorder.INTERNODE_VERTICAL_PADDING;
var xOffset = mindplot.nlayout.SymetricSorder.INTERNODE_HORIZONTAL_PADDING;
$assert(!isNaN(xOffset), "xOffset can not be null");
$assert(!isNaN(yOffset), "yOffset can not be null");
result[heights[i].id] = {x:xOffset,y:yOffset};
}
return result;
}
});
mindplot.nlayout.SymetricSorder.INTERNODE_VERTICAL_PADDING = 5;
mindplot.nlayout.SymetricSorder.INTERNODE_HORIZONTAL_PADDING = 5;

View File

@ -0,0 +1,70 @@
mindplot.nlayout.TestSuite = new Class({
Extends: mindplot.nlayout.ChildrenSorterStrategy,
initialize:function() {
// this.testAligned();
this.testEvents();
// @ Agregar tests que garantice que no se reposicional cosan inecesariamente 2 veces...
},
testAligned: function() {
var size = {width:30,height:30};
var position = {x:0,y:0};
var manager = new mindplot.nlayout.LayoutManager(0, size);
manager.addNode(1, size, position);
manager.connectNode(0, 1, 0);
manager.layout();
manager.dump();
},
testEvents: 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(0, 2, 0);
manager.connectNode(1, 3, 0);
// Reposition ...
manager.layout();
console.log("Updated tree:");
manager.dump();
// Listen for changes ...
console.log("Updated nodes ...");
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.");
// Ok, if a new node is added, this an event should be fired ...
console.log("---- Layout without changes should not affect the tree ---");
events.empty();
manager.layout(true);
$assert(events.length == 0, "Unnecessary tree updated.");
}
});

View File

@ -0,0 +1,41 @@
<!DOCTYPE HTML>
<html>
<head>
<script type='text/javascript'
src='../../../../../wise-doc/src/main/webapp/js/mootools-core-1.3.2-full-compat.js'></script>
<script type='text/javascript'
src='../../../../../wise-doc/src/main/webapp/js/mootools-more-1.3.2.1-yui.js'></script>
<script type='text/javascript' src='../../../main/javascript/header.js'></script>
<script type='text/javascript' src='../../../../../core-js/target/classes/core.js'></script>
<script type='text/javascript' src='../../../main/javascript/widget/ToolbarItem.js'></script>
<script type='text/javascript' src='../../../main/javascript/widget/ColorPalettePanel.js'></script>
<script type='text/javascript'>
window.addEvent("load", function(e) {
var model = {
getValue: function() {
},
setValue : function(value) {
console.log("value:" + value);
}
};
var palette = new mindplot.widget.ColorPalettePanel('myButton', model,"/mindplot/src/main/javascript/widget");
});
</script>
</head>
<body>
<div id="myButton" style="border: 1px red solid">
The button
</div>
</body>
</html>