Merge branch 'tree-intersects'

Conflicts:
	js/id/behavior/lasso.js
This commit is contained in:
Ansis Brammanis
2013-02-15 13:40:01 -05:00
11 changed files with 196 additions and 32 deletions
+1
View File
@@ -151,6 +151,7 @@
<script src='js/id/core/node.js'></script>
<script src='js/id/core/relation.js'></script>
<script src='js/id/core/way.js'></script>
<script src='js/id/core/tree.js'></script>
<script src='js/id/connection.js'></script>
<script src='js/id/validate.js'></script>
+1 -1
View File
@@ -49,7 +49,7 @@ iD.behavior.Lasso = function(context) {
.on('mouseup.lasso', null);
if (d3.event.clientX !== pos[0] || d3.event.clientY !== pos[1]) {
var selected = context.graph().intersects(extent).filter(function (entity) {
var selected = context.intersects(extent).filter(function (entity) {
return entity.type === 'node';
});
+25 -16
View File
@@ -25,6 +25,18 @@ iD.Difference = function(base, head) {
}
});
function addParents(parents, result) {
for (var i = 0; i < parents.length; i++) {
var parent = parents[i];
if (parent.id in result)
continue;
result[parent.id] = parent;
addParents(head.parentRelations(parent), result);
}
}
var difference = {};
difference.length = function() {
@@ -67,21 +79,18 @@ iD.Difference = function(base, head) {
return result;
};
difference.addParents = function(entities) {
for (var i in entities) {
addParents(head.parentWays(entities[i]), entities);
addParents(head.parentRelations(entities[i]), entities);
}
return entities;
},
difference.complete = function(extent) {
var result = {}, id, change;
function addParents(parents) {
for (var i = 0; i < parents.length; i++) {
var parent = parents[i];
if (parent.id in result)
continue;
result[parent.id] = parent;
addParents(head.parentRelations(parent));
}
}
for (id in changes) {
change = changes[id];
@@ -99,10 +108,10 @@ iD.Difference = function(base, head) {
if (entity.type === 'way') {
var nh = h ? h.nodes : [],
nb = b ? b.nodes : [],
diff;
diff, i;
diff = _.difference(nh, nb);
for (var i = 0; i < diff.length; i++) {
for (i = 0; i < diff.length; i++) {
result[diff[i]] = head.entity(diff[i]);
}
@@ -111,9 +120,9 @@ iD.Difference = function(base, head) {
result[diff[i]] = head.entity(diff[i]);
}
}
addParents(head.parentWays(entity), result);
addParents(head.parentRelations(entity), result);
addParents(head.parentWays(entity));
addParents(head.parentRelations(entity));
}
return result;
+1 -12
View File
@@ -226,21 +226,10 @@ iD.Graph.prototype = {
return this;
},
// get all objects that intersect an extent.
intersects: function(extent) {
var items = [];
for (var i in this.entities) {
var entity = this.entities[i];
if (entity && this.hasAllChildren(entity) && entity.intersects(extent, this)) {
items.push(entity);
}
}
return items;
},
hasAllChildren: function(entity) {
// we're only checking changed entities, since we assume fetched data
// must have all children present
var i;
if (this.entities.hasOwnProperty(entity.id)) {
if (entity.type === 'way') {
for (i = 0; i < entity.nodes.length; i++) {
+15 -1
View File
@@ -1,5 +1,5 @@
iD.History = function(context) {
var stack, index,
var stack, index, tree,
imagery_used = 'Bing',
dispatch = d3.dispatch('change', 'undone', 'redone'),
lock = false;
@@ -41,10 +41,19 @@ iD.History = function(context) {
},
merge: function(entities) {
var base = stack[0].graph.base(),
newentities = Object.keys(entities).filter(function(i) {
return !base.entities[i];
});
for (var i = 0; i < stack.length; i++) {
stack[i].graph.rebase(entities);
}
tree.rebase(newentities);
dispatch.change();
},
@@ -124,6 +133,10 @@ iD.History = function(context) {
}
},
intersects: function(extent) {
return tree.intersects(extent, stack[index].graph);
},
difference: function() {
var base = stack[0].graph,
head = stack[index].graph;
@@ -168,6 +181,7 @@ iD.History = function(context) {
reset: function() {
stack = [{graph: iD.Graph()}];
index = 0;
tree = iD.Tree(stack[0].graph);
dispatch.change();
},
+92
View File
@@ -0,0 +1,92 @@
iD.Tree = function(graph) {
var rtree = new RTree(),
m = 1000 * 1000 * 100,
head = graph,
queuedCreated = [],
queuedModified = [],
x, y, dx, dy, rebased;
function extentRectangle(extent) {
x = m * extent[0][0],
y = m * extent[0][1],
dx = m * extent[1][0] - x || 1,
dy = m * extent[1][1] - y || 1;
return new RTree.Rectangle(~~x, ~~y, ~~dx - 1, ~~dy - 1);
}
function insert(entity) {
rtree.insert(extentRectangle(entity.extent(head)), entity.id);
}
function remove(entity) {
rtree.remove(extentRectangle(entity.extent(graph)), entity.id);
}
function reinsert(entity) {
remove(graph.entities[entity.id]);
insert(entity);
}
var tree = {
rebase: function(entities) {
for (var i = 0; i < entities.length; i++) {
if (!graph.entities.hasOwnProperty(entities[i])) {
insert(graph.entity(entities[i]), true);
}
}
rebased = true;
return tree;
},
intersects: function(extent, g) {
head = g;
if (graph !== head || rebased) {
var diff = iD.Difference(graph, head),
modified = {};
diff.modified().forEach(function(d) {
var loc = graph.entities[d.id].loc;
if (!loc || loc[0] !== d.loc[0] || loc[1] !== d.loc[1]) {
modified[d.id] = d;
}
});
var created = diff.created().concat(queuedCreated);
modified = d3.values(diff.addParents(modified)).concat(queuedModified);
queuedCreated = [];
queuedModified = [];
modified.forEach(function(d) {
if (head.hasAllChildren(d)) reinsert(d);
else queuedModified.push(d);
});
created.forEach(function(d) {
if (head.hasAllChildren(d)) insert(d);
else queuedCreated.push(d);
});
diff.deleted().forEach(remove);
graph = head;
rebased = false;
}
return rtree.search(extentRectangle(extent))
.map(function(id) { return graph.entity(id); });
},
graph: function() {
return graph;
}
};
return tree;
};
+1
View File
@@ -36,6 +36,7 @@ window.iD = function () {
context.undo = history.undo;
context.redo = history.redo;
context.changes = history.changes;
context.intersects = history.intersects;
/* Graph */
context.entity = function(id) {
+1 -1
View File
@@ -65,7 +65,7 @@ iD.Map = function(context) {
graph = context.graph();
if (!difference) {
all = graph.intersects(extent);
all = context.intersects(extent);
filter = d3.functor(true);
} else {
var complete = difference.complete(extent);
+1 -1
View File
@@ -2,7 +2,7 @@ iD.ui.Contributors = function(context) {
function update(selection) {
var users = {},
limit = 3,
entities = context.graph().intersects(context.map().extent());
entities = context.intersects(context.map().extent());
for (var i in entities) {
if (entities[i].user) users[entities[i].user] = true;
+2
View File
@@ -137,6 +137,7 @@
<script src='../js/id/core/node.js'></script>
<script src='../js/id/core/relation.js'></script>
<script src='../js/id/core/way.js'></script>
<script src='../js/id/core/tree.js'></script>
<script src='../js/id/connection.js'></script>
@@ -186,6 +187,7 @@
<script src="spec/core/relation.js"></script>
<script src="spec/core/history.js"></script>
<script src="spec/core/difference.js"></script>
<script src="spec/core/tree.js"></script>
<script src="spec/renderer/background.js"></script>
<script src="spec/renderer/map.js"></script>
+56
View File
@@ -0,0 +1,56 @@
describe("iD.Tree", function() {
var tree;
beforeEach(function() {
tree = iD.Tree(iD.Graph());
});
describe("#rebase", function() {
it("adds entities to the tree", function() {
var node = iD.Node({ id: 'n', loc: [1, 1]});
tree.graph().rebase({ 'n': node });
tree.rebase(['n']);
expect(tree.intersects(iD.geo.Extent([0, 0], [2, 2]), tree.graph())).to.eql([node]);
});
it("does not insert if entity has a modified version", function() {
var node = iD.Node({ id: 'n', loc: [1, 1]}),
node_ = node.update({ loc: [10, 10]}),
g = tree.graph().replace(node_);
expect(tree.intersects(iD.geo.Extent([9, 9], [11, 11]), g)).to.eql([node_]);
tree.graph().rebase({ 'n': node });
tree.rebase(['n']);
expect(tree.intersects(iD.geo.Extent([0, 0], [2, 2]), g)).to.eql([]);
expect(tree.intersects(iD.geo.Extent([0, 0], [11, 11]), g)).to.eql([node_]);
});
});
describe("#intersects", function() {
it("excludes entities with missing children, adds them when all are present", function() {
var way = iD.Way({id: 'w1', nodes: ['n']});
var g = tree.graph().replace(way);
expect(tree.intersects(iD.geo.Extent([0, 0], [1, 1]), g)).to.eql([]);
var node = iD.Node({id: 'n', loc: [0.5, 0.5]});
g = tree.graph().replace(node);
expect(tree.intersects(iD.geo.Extent([0, 0], [1, 1]), g)).to.eql([way, node]);
});
it("includes entities that used to have missing children, after rebase added them", function() {
var base = tree.graph();
var way = iD.Way({id: 'w1', nodes: ['n']});
var g = base.replace(way);
expect(tree.intersects(iD.geo.Extent([0, 0], [1, 1]), g)).to.eql([]);
var node = iD.Node({id: 'n', loc: [0.5, 0.5]});
base.rebase({ 'n': node });
tree.rebase(['n']);
expect(tree.intersects(iD.geo.Extent([0, 0], [1, 1]), g)).to.eql([way, node]);
});
it("includes entities within extent, excludes those without", function() {
var n1 = iD.Node({ id: 'n1', loc: [1, 1]});
var n2 = iD.Node({ id: 'n2', loc: [3, 3]});
var g = tree.graph().replace(n1).replace(n2);
expect(tree.intersects(iD.geo.Extent([0, 0], [1.1, 1.1]), g)).to.eql([n1]);
});
});
});