Better optimization for advanced compilation mode

This commit is contained in:
ahocevar
2013-05-20 08:50:15 +02:00
parent bf510b570a
commit 9dd4dc3730
+74 -77
View File
@@ -35,9 +35,9 @@ goog.require('goog.object');
/** /**
* @typedef {{x: (number), y: (number), w: (number), h: (number), * @typedef {{x: (number), y: (number), w: (number), h: (number),
* 'leaf': (Object|undefined), * leaf: (Object|undefined),
* 'nodes': (Array.<ol.structs.RTreeNode>|undefined), * nodes: (Array.<ol.structs.RTreeNode>|undefined),
* 'target': (Object|undefined), type: (string|undefined)}} * target: (Object|undefined), type: (string|undefined)}}
*/ */
ol.structs.RTreeNode; ol.structs.RTreeNode;
@@ -57,7 +57,7 @@ ol.structs.RTree = function(opt_width) {
} }
// Start with an empty root-tree // Start with an empty root-tree
var tree = /** @type {ol.structs.RTreeNode} */ var tree = /** @type {ol.structs.RTreeNode} */
({x: 0, y: 0, w: 0, h: 0, 'nodes': []}); ({x: 0, y: 0, w: 0, h: 0, nodes: []});
// This is my special addition to the world of r-trees // This is my special addition to the world of r-trees
// every other (simple) method I found produced crap trees // every other (simple) method I found produced crap trees
@@ -92,84 +92,82 @@ ol.structs.RTree = function(opt_width) {
} }
var workingObject = /** @type {ol.structs.RTreeNode} */ ({ var workingObject = /** @type {ol.structs.RTreeNode} */ ({
x: rect.x, y: rect.y, w: rect.w, h: rect.h, 'target': obj x: rect.x, y: rect.y, w: rect.w, h: rect.h, target: obj
}); });
countStack.push(root['nodes'].length); countStack.push(root.nodes.length);
hitStack.push(root); hitStack.push(root);
do { do {
var tree = hitStack.pop(); var tree = hitStack.pop();
var i = countStack.pop() - 1; var i = countStack.pop() - 1;
if (goog.object.containsKey(workingObject, 'target')) { if (goog.isDef(workingObject.target)) {
// We are searching for a target // We are searching for a target
while (i >= 0) { while (i >= 0) {
var lTree = tree['nodes'][i]; var lTree = tree.nodes[i];
if (ol.structs.RTreeRectangle.overlapRectangle( if (ol.structs.RTreeRectangle.overlapRectangle(
workingObject, lTree)) { workingObject, lTree)) {
if ((workingObject['target'] && if ((workingObject.target && goog.isDef(lTree.leaf) &&
goog.object.containsKey(lTree, 'leaf') && lTree.leaf === workingObject.target) ||
lTree['leaf'] === workingObject['target']) || (!workingObject.target && (goog.isDef(lTree.leaf) ||
(!workingObject['target'] &&
(goog.object.containsKey(lTree, 'leaf') ||
ol.structs.RTreeRectangle.containsRectangle( ol.structs.RTreeRectangle.containsRectangle(
lTree, workingObject)))) { // A Match !! lTree, workingObject)))) { // A Match !!
// Yup we found a match... // Yup we found a match...
// we can cancel search and start walking up the list // we can cancel search and start walking up the list
if (goog.object.containsKey(lTree, 'nodes')) { if (goog.isDef(lTree.nodes)) {
// If we are deleting a node not a leaf... // If we are deleting a node not a leaf...
returnArray = searchSubtree(lTree, true, [], lTree); returnArray = searchSubtree(lTree, true, [], lTree);
tree['nodes'].splice(i, 1); tree.nodes.splice(i, 1);
} else { } else {
returnArray = tree['nodes'].splice(i, 1); returnArray = tree.nodes.splice(i, 1);
} }
// Resize MBR down... // Resize MBR down...
ol.structs.RTreeRectangle.makeMBR(tree['nodes'], tree); ol.structs.RTreeRectangle.makeMBR(tree.nodes, tree);
delete workingObject['target']; workingObject.target = undefined;
if (tree['nodes'].length < minWidth) { // Underflow if (tree.nodes.length < minWidth) { // Underflow
workingObject['nodes'] = /** @type {Array} */ workingObject.nodes = /** @type {Array} */
(searchSubtree(tree, true, [], tree)); (searchSubtree(tree, true, [], tree));
} }
break; break;
} else if (goog.object.containsKey(lTree, 'nodes')) { } else if (goog.isDef(lTree.nodes)) {
// Not a Leaf // Not a Leaf
currentDepth += 1; currentDepth += 1;
countStack.push(i); countStack.push(i);
hitStack.push(tree); hitStack.push(tree);
tree = lTree; tree = lTree;
i = lTree['nodes'].length; i = lTree.nodes.length;
} }
} }
i -= 1; i -= 1;
} }
} else if (goog.object.containsKey(workingObject, 'nodes')) { } else if (goog.isDef(workingObject.nodes)) {
// We are unsplitting // We are unsplitting
tree['nodes'].splice(i + 1, 1); // Remove unsplit node tree.nodes.splice(i + 1, 1); // Remove unsplit node
// workingObject.nodes contains a list of elements removed from the // workingObject.nodes contains a list of elements removed from the
// tree so far // tree so far
if (tree['nodes'].length > 0) if (tree.nodes.length > 0)
ol.structs.RTreeRectangle.makeMBR(tree['nodes'], tree); ol.structs.RTreeRectangle.makeMBR(tree.nodes, tree);
for (var t = 0, tt = workingObject['nodes'].length; t < tt; ++t) for (var t = 0, tt = workingObject.nodes.length; t < tt; ++t)
insertSubtree(workingObject['nodes'][t], tree); insertSubtree(workingObject.nodes[t], tree);
workingObject['nodes'].length = 0; workingObject.nodes.length = 0;
if (hitStack.length == 0 && tree['nodes'].length <= 1) { if (hitStack.length == 0 && tree.nodes.length <= 1) {
// Underflow..on root! // Underflow..on root!
workingObject['nodes'] = /** @type {Array} */ workingObject.nodes = /** @type {Array} */
(searchSubtree(tree, true, workingObject['nodes'], tree)); (searchSubtree(tree, true, workingObject.nodes, tree));
tree['nodes'].length = 0; tree.nodes.length = 0;
hitStack.push(tree); hitStack.push(tree);
countStack.push(1); countStack.push(1);
} else if (hitStack.length > 0 && tree['nodes'].length < minWidth) { } else if (hitStack.length > 0 && tree.nodes.length < minWidth) {
// Underflow..AGAIN! // Underflow..AGAIN!
workingObject['nodes'] = /** @type {Array} */ workingObject.nodes = /** @type {Array} */
(searchSubtree(tree, true, workingObject['nodes'], tree)); (searchSubtree(tree, true, workingObject.nodes, tree));
tree['nodes'].length = 0; tree.nodes.length = 0;
} else { } else {
delete workingObject['nodes']; // Just start resizing workingObject.nodes = undefined; // Just start resizing
} }
} else { // we are just resizing } else { // we are just resizing
ol.structs.RTreeRectangle.makeMBR(tree['nodes'], tree); ol.structs.RTreeRectangle.makeMBR(tree.nodes, tree);
} }
currentDepth -= 1; currentDepth -= 1;
} while (hitStack.length > 0); } while (hitStack.length > 0);
@@ -190,25 +188,25 @@ ol.structs.RTree = function(opt_width) {
var bestChoiceArea; var bestChoiceArea;
bestChoiceStack.push(root); bestChoiceStack.push(root);
var nodes = root['nodes']; var nodes = root.nodes;
do { do {
if (bestChoiceIndex != -1) { if (bestChoiceIndex != -1) {
bestChoiceStack.push(nodes[bestChoiceIndex]); bestChoiceStack.push(nodes[bestChoiceIndex]);
nodes = nodes[bestChoiceIndex]['nodes']; nodes = nodes[bestChoiceIndex].nodes;
bestChoiceIndex = -1; bestChoiceIndex = -1;
} }
for (var i = nodes.length - 1; i >= 0; --i) { for (var i = nodes.length - 1; i >= 0; --i) {
var lTree = nodes[i]; var lTree = nodes[i];
if (goog.object.containsKey(lTree, 'leaf')) { if (goog.isDef(lTree.leaf)) {
// Bail out of everything and start inserting // Bail out of everything and start inserting
bestChoiceIndex = -1; bestChoiceIndex = -1;
break; break;
} }
// Area of new enlarged rectangle // Area of new enlarged rectangle
var oldLRatio = squarifiedRatio(lTree.w, lTree.h, var oldLRatio = squarifiedRatio(lTree.w, lTree.h,
lTree['nodes'].length + 1); lTree.nodes.length + 1);
// Enlarge rectangle to fit new rectangle // Enlarge rectangle to fit new rectangle
var nw = Math.max(lTree.x + lTree.w, rect.x + rect.w) - var nw = Math.max(lTree.x + lTree.w, rect.x + rect.w) -
@@ -217,7 +215,7 @@ ol.structs.RTree = function(opt_width) {
Math.min(lTree.y, rect.y); Math.min(lTree.y, rect.y);
// Area of new enlarged rectangle // Area of new enlarged rectangle
var lRatio = squarifiedRatio(nw, nh, lTree['nodes'].length + 2); var lRatio = squarifiedRatio(nw, nh, lTree.nodes.length + 2);
if (bestChoiceIndex < 0 || if (bestChoiceIndex < 0 ||
Math.abs(lRatio - oldLRatio) < bestChoiceArea) { Math.abs(lRatio - oldLRatio) < bestChoiceArea) {
@@ -249,8 +247,8 @@ ol.structs.RTree = function(opt_width) {
*/ */
var pickNext = function(nodes, a, b) { var pickNext = function(nodes, a, b) {
// Area of new enlarged rectangle // Area of new enlarged rectangle
var areaA = squarifiedRatio(a.w, a.h, a['nodes'].length + 1); var areaA = squarifiedRatio(a.w, a.h, a.nodes.length + 1);
var areaB = squarifiedRatio(b.w, b.h, b['nodes'].length + 1); var areaB = squarifiedRatio(b.w, b.h, b.nodes.length + 1);
var highAreaDelta; var highAreaDelta;
var highAreaNode; var highAreaNode;
var lowestGrowthGroup; var lowestGrowthGroup;
@@ -263,7 +261,7 @@ ol.structs.RTree = function(opt_width) {
newAreaA.w = Math.max(a.x + a.w, l.x + l.w) - newAreaA.x; newAreaA.w = Math.max(a.x + a.w, l.x + l.w) - newAreaA.x;
newAreaA.h = Math.max(a.y + a.h, l.y + l.h) - newAreaA.y; newAreaA.h = Math.max(a.y + a.h, l.y + l.h) - newAreaA.y;
var changeNewAreaA = Math.abs(squarifiedRatio(newAreaA.w, newAreaA.h, var changeNewAreaA = Math.abs(squarifiedRatio(newAreaA.w, newAreaA.h,
a['nodes'].length + 2) - areaA); a.nodes.length + 2) - areaA);
var newAreaB = {}; var newAreaB = {};
newAreaB.x = Math.min(b.x, l.x); newAreaB.x = Math.min(b.x, l.x);
@@ -271,7 +269,7 @@ ol.structs.RTree = function(opt_width) {
newAreaB.w = Math.max(b.x + b.w, l.x + l.w) - newAreaB.x; newAreaB.w = Math.max(b.x + b.w, l.x + l.w) - newAreaB.x;
newAreaB.h = Math.max(b.y + b.h, l.y + l.h) - newAreaB.y; newAreaB.h = Math.max(b.y + b.h, l.y + l.h) - newAreaB.y;
var changeNewAreaB = Math.abs(squarifiedRatio( var changeNewAreaB = Math.abs(squarifiedRatio(
newAreaB.w, newAreaB.h, b['nodes'].length + 2) - areaB); newAreaB.w, newAreaB.h, b.nodes.length + 2) - areaB);
if (!highAreaNode || !highAreaDelta || if (!highAreaNode || !highAreaDelta ||
Math.abs(changeNewAreaB - changeNewAreaA) < highAreaDelta) { Math.abs(changeNewAreaB - changeNewAreaA) < highAreaDelta) {
@@ -281,15 +279,15 @@ ol.structs.RTree = function(opt_width) {
} }
} }
var tempNode = nodes.splice(highAreaNode, 1)[0]; var tempNode = nodes.splice(highAreaNode, 1)[0];
if (a['nodes'].length + nodes.length + 1 <= minWidth) { if (a.nodes.length + nodes.length + 1 <= minWidth) {
a['nodes'].push(tempNode); a.nodes.push(tempNode);
ol.structs.RTreeRectangle.expandRectangle(a, tempNode); ol.structs.RTreeRectangle.expandRectangle(a, tempNode);
} else if (b['nodes'].length + nodes.length + 1 <= minWidth) { } else if (b.nodes.length + nodes.length + 1 <= minWidth) {
b['nodes'].push(tempNode); b.nodes.push(tempNode);
ol.structs.RTreeRectangle.expandRectangle(b, tempNode); ol.structs.RTreeRectangle.expandRectangle(b, tempNode);
} }
else { else {
lowestGrowthGroup['nodes'].push(tempNode); lowestGrowthGroup.nodes.push(tempNode);
ol.structs.RTreeRectangle.expandRectangle(lowestGrowthGroup, tempNode); ol.structs.RTreeRectangle.expandRectangle(lowestGrowthGroup, tempNode);
} }
}; };
@@ -342,14 +340,14 @@ ol.structs.RTree = function(opt_width) {
} }
return [ return [
/** @type {ol.structs.RTreeNode} */ /** @type {ol.structs.RTreeNode} */
({x: t1.x, y: t1.y, w: t1.w, h: t1.h, 'nodes': [t1]}), ({x: t1.x, y: t1.y, w: t1.w, h: t1.h, nodes: [t1]}),
/** @type {ol.structs.RTreeNode} */ /** @type {ol.structs.RTreeNode} */
({x: t2.x, y: t2.y, w: t2.w, h: t2.h, 'nodes': [t2]}) ({x: t2.x, y: t2.y, w: t2.w, h: t2.h, nodes: [t2]})
]; ];
}; };
var attachData = function(node, moreTree) { var attachData = function(node, moreTree) {
node['nodes'] = moreTree['nodes']; node.nodes = moreTree.nodes;
node.x = moreTree.x; node.y = moreTree.y; node.x = moreTree.x; node.y = moreTree.y;
node.w = moreTree.w; node.h = moreTree.h; node.w = moreTree.w; node.h = moreTree.h;
return node; return node;
@@ -372,7 +370,7 @@ ol.structs.RTree = function(opt_width) {
return result; return result;
} }
hitStack.push(root['nodes']); hitStack.push(root.nodes);
do { do {
var nodes = hitStack.pop(); var nodes = hitStack.pop();
@@ -380,14 +378,14 @@ ol.structs.RTree = function(opt_width) {
for (var i = nodes.length - 1; i >= 0; --i) { for (var i = nodes.length - 1; i >= 0; --i) {
var lTree = nodes[i]; var lTree = nodes[i];
if (ol.structs.RTreeRectangle.overlapRectangle(rect, lTree)) { if (ol.structs.RTreeRectangle.overlapRectangle(rect, lTree)) {
if (goog.object.containsKey(lTree, 'nodes')) { // Not a Leaf if (goog.isDef(lTree.nodes)) { // Not a Leaf
hitStack.push(lTree['nodes']); hitStack.push(lTree.nodes);
} else if (goog.object.containsKey(lTree, 'leaf')) { // A Leaf !! } else if (goog.isDef(lTree.leaf)) { // A Leaf !!
if (!returnNode) { if (!returnNode) {
// TODO keep track of type on all nodes so we don't have to // TODO keep track of type on all nodes so we don't have to
// walk all the way in to the leaf to know that we don't need it // walk all the way in to the leaf to know that we don't need it
if (!goog.isDef(opt_type) || lTree.type == opt_type) { if (!goog.isDef(opt_type) || lTree.type == opt_type) {
var obj = lTree['leaf']; var obj = lTree.leaf;
result[goog.getUid(obj).toString()] = obj; result[goog.getUid(obj).toString()] = obj;
} }
} else { } else {
@@ -409,12 +407,12 @@ ol.structs.RTree = function(opt_width) {
var bc; // Best Current node var bc; // Best Current node
// Initial insertion is special because we resize the Tree and we don't // Initial insertion is special because we resize the Tree and we don't
// care about any overflow (seriously, how can the first object overflow?) // care about any overflow (seriously, how can the first object overflow?)
if (root['nodes'].length == 0) { if (root.nodes.length == 0) {
root.x = node.x; root.x = node.x;
root.y = node.y; root.y = node.y;
root.w = node.w; root.w = node.w;
root.h = node.h; root.h = node.h;
root['nodes'].push(node); root.nodes.push(node);
return; return;
} }
@@ -427,13 +425,12 @@ ol.structs.RTree = function(opt_width) {
// Walk back up the tree resizing and inserting as needed // Walk back up the tree resizing and inserting as needed
do { do {
//handle the case of an empty node (from a split) //handle the case of an empty node (from a split)
if (bc && goog.object.containsKey(bc, 'nodes') && if (bc && goog.isDef(bc.nodes) && bc.nodes.length == 0) {
bc['nodes'].length == 0) {
var pbc = bc; // Past bc var pbc = bc; // Past bc
bc = treeStack.pop(); bc = treeStack.pop();
for (var t = 0, tt = bc['nodes'].length; t < tt; ++t) { for (var t = 0, tt = bc.nodes.length; t < tt; ++t) {
if (bc['nodes'][t] === pbc || bc['nodes'][t]['nodes'].length == 0) { if (bc.nodes[t] === pbc || bc.nodes[t].nodes.length == 0) {
bc['nodes'].splice(t, 1); bc.nodes.splice(t, 1);
break; break;
} }
} }
@@ -443,30 +440,30 @@ ol.structs.RTree = function(opt_width) {
// If there is data attached to this workingObject // If there is data attached to this workingObject
var isArray = goog.isArray(workingObject); var isArray = goog.isArray(workingObject);
if (goog.object.containsKey(workingObject, 'leaf') || if (goog.isDef(workingObject.leaf) ||
goog.object.containsKey(workingObject, 'nodes') || isArray) { goog.isDef(workingObject.nodes) || isArray) {
// Do Insert // Do Insert
if (isArray) { if (isArray) {
for (var ai = 0, aii = workingObject.length; ai < aii; ++ai) { for (var ai = 0, aii = workingObject.length; ai < aii; ++ai) {
ol.structs.RTreeRectangle.expandRectangle(bc, workingObject[ai]); ol.structs.RTreeRectangle.expandRectangle(bc, workingObject[ai]);
} }
bc['nodes'] = bc['nodes'].concat(workingObject); bc.nodes = bc.nodes.concat(workingObject);
} else { } else {
ol.structs.RTreeRectangle.expandRectangle(bc, workingObject); ol.structs.RTreeRectangle.expandRectangle(bc, workingObject);
bc['nodes'].push(workingObject); // Do Insert bc.nodes.push(workingObject); // Do Insert
} }
if (bc['nodes'].length <= maxWidth) { // Start Resizeing Up the Tree if (bc.nodes.length <= maxWidth) { // Start Resizeing Up the Tree
workingObject = /** @type {ol.structs.RTreeNode} */ workingObject = /** @type {ol.structs.RTreeNode} */
({x: bc.x, y: bc.y, w: bc.w, h: bc.h}); ({x: bc.x, y: bc.y, w: bc.w, h: bc.h});
} else { // Otherwise Split this Node } else { // Otherwise Split this Node
// linearSplit() returns an array containing two new nodes // linearSplit() returns an array containing two new nodes
// formed from the split of the previous node's overflow // formed from the split of the previous node's overflow
var a = linearSplit(bc['nodes']); var a = linearSplit(bc.nodes);
workingObject = a;//[1]; workingObject = a;//[1];
if (treeStack.length < 1) { // If are splitting the root.. if (treeStack.length < 1) { // If are splitting the root..
bc['nodes'].push(a[0]); bc.nodes.push(a[0]);
treeStack.push(bc); // Reconsider the root element treeStack.push(bc); // Reconsider the root element
workingObject = a[1]; workingObject = a[1];
} }
@@ -513,7 +510,7 @@ ol.structs.RTree = function(opt_width) {
default: default:
arguments.length = 3; arguments.length = 3;
} }
if (arguments[1] === false) { // Do area-wide delete if (arguments[1] === false) { // Do area-wide
var numberDeleted = 0; var numberDeleted = 0;
var result = []; var result = [];
do { do {
@@ -537,7 +534,7 @@ ol.structs.RTree = function(opt_width) {
var node = /** @type {ol.structs.RTreeNode} */ ({ var node = /** @type {ol.structs.RTreeNode} */ ({
x: extent[0], y: extent[2], x: extent[0], y: extent[2],
w: extent[1] - extent[0], h: extent[3] - extent[2], w: extent[1] - extent[0], h: extent[3] - extent[2],
'leaf': obj leaf: obj
}); });
if (goog.isDef(opt_type)) { if (goog.isDef(opt_type)) {
node.type = opt_type; node.type = opt_type;