From 6060e886cb44a2abccf5c08b20cb33912ab1d176 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Sun, 5 May 2013 22:55:45 -0700 Subject: [PATCH] Reduce calls to iD.svg.TagClasses For fills and shadows, style changes can happen only when a new version of the feature is produced. If we include a version number in the key, we need only call TagClasses on the enter selection. We cannot apply the same optimization for strokes, since stroke style can depend on the tags of parent relations as well. --- js/id/core/difference.js | 8 ++++++-- js/id/core/entity.js | 4 ++-- js/id/svg/areas.js | 18 ++++++++++-------- js/id/svg/lines.js | 18 ++++++++++-------- js/id/svg/tag_classes.js | 2 +- test/spec/core/entity.js | 8 ++++++++ 6 files changed, 37 insertions(+), 21 deletions(-) diff --git a/js/id/core/difference.js b/js/id/core/difference.js index a5a29cf3a..9c1a80ca5 100644 --- a/js/id/core/difference.js +++ b/js/id/core/difference.js @@ -9,9 +9,13 @@ iD.Difference = function(base, head) { var changes = {}, length = 0; + function changed(h, b) { + return !_.isEqual(_.omit(h, 'v'), _.omit(b, 'v')); + } + _.each(head.entities, function(h, id) { var b = base.entities[id]; - if (!_.isEqual(h, b)) { + if (changed(h, b)) { changes[id] = {base: b, head: h}; length++; } @@ -19,7 +23,7 @@ iD.Difference = function(base, head) { _.each(base.entities, function(b, id) { var h = head.entities[id]; - if (!changes[id] && !_.isEqual(h, b)) { + if (!changes[id] && changed(h, b)) { changes[id] = {base: b, head: h}; length++; } diff --git a/js/id/core/entity.js b/js/id/core/entity.js index ab6f76bc6..0ec14f70b 100644 --- a/js/id/core/entity.js +++ b/js/id/core/entity.js @@ -31,7 +31,7 @@ iD.Entity.id.type = function(id) { // A function suitable for use as the second argument to d3.selection#data(). iD.Entity.key = function(entity) { - return entity.id; + return entity.id + ',' + entity.v; }; iD.Entity.prototype = { @@ -72,7 +72,7 @@ iD.Entity.prototype = { }, update: function(attrs) { - return iD.Entity(this, attrs); + return iD.Entity(this, attrs, {v: 1 + (this.v || 0)}); }, mergeTags: function(tags) { diff --git a/js/id/svg/areas.js b/js/id/svg/areas.js index 7b40b8441..8f94a3f69 100644 --- a/js/id/svg/areas.js +++ b/js/id/svg/areas.js @@ -58,25 +58,27 @@ iD.svg.Areas = function(projection) { }); function drawPaths(areas, klass, closeWay) { - var tagClasses = iD.svg.TagClasses(); - - if (klass === 'stroke') { - tagClasses.tags(iD.svg.MultipolygonMemberTags(graph)); - } - var paths = surface.select('.layer-' + klass) .selectAll('path.area') .filter(filter) .data(areas, iD.Entity.key); - paths.enter() + var enter = paths.enter() .append('path') .attr('class', function(d) { return d.type + ' area ' + klass + ' ' + d.id; }); + // Optimization: call simple TagClasses only on enter selection. This + // works because iD.Entity.key is defined to include the entity v attribute. + if (klass !== 'stroke') { + enter.call(iD.svg.TagClasses()); + } else { + paths.call(iD.svg.TagClasses() + .tags(iD.svg.MultipolygonMemberTags(graph))); + } + paths .order() .attr('d', function(entity) { return path(entity.asGeoJSON(graph, closeWay)); }) - .call(tagClasses) .call(iD.svg.MemberClasses(graph)); if (klass === 'fill') paths.call(setPattern); diff --git a/js/id/svg/lines.js b/js/id/svg/lines.js index 23a02cedd..789bcb3d7 100644 --- a/js/id/svg/lines.js +++ b/js/id/svg/lines.js @@ -50,25 +50,27 @@ iD.svg.Lines = function(projection) { lines.sort(waystack); function drawPaths(klass) { - var tagClasses = iD.svg.TagClasses(); - - if (klass === 'stroke') { - tagClasses.tags(iD.svg.MultipolygonMemberTags(graph)); - } - var paths = surface.select('.layer-' + klass) .selectAll('path.line') .filter(filter) .data(lines, iD.Entity.key); - paths.enter() + var enter = paths.enter() .append('path') .attr('class', function(d) { return 'way line ' + klass + ' ' + d.id; }); + // Optimization: call simple TagClasses only on enter selection. This + // works because iD.Entity.key is defined to include the entity v attribute. + if (klass !== 'stroke') { + enter.call(iD.svg.TagClasses()); + } else { + paths.call(iD.svg.TagClasses() + .tags(iD.svg.MultipolygonMemberTags(graph))); + } + paths .order() .attr('d', path) - .call(tagClasses) .call(iD.svg.MemberClasses(graph)); paths.exit() diff --git a/js/id/svg/tag_classes.js b/js/id/svg/tag_classes.js index 96b907828..434acc62b 100644 --- a/js/id/svg/tag_classes.js +++ b/js/id/svg/tag_classes.js @@ -19,7 +19,7 @@ iD.svg.TagClasses = function() { var t = tags(entity); for (var k in t) { if (!keys.has(k)) continue; - classes += ' tag-' + k + ' ' + 'tag-' + k + '-' + t[k]; + classes += ' tag-' + k + ' tag-' + k + '-' + t[k]; } classes = classes.trim(); diff --git a/test/spec/core/entity.js b/test/spec/core/entity.js index 8ab310f5d..2190d7125 100644 --- a/test/spec/core/entity.js +++ b/test/spec/core/entity.js @@ -61,6 +61,14 @@ describe('iD.Entity', function () { it("doesn't copy prototype properties", function () { expect(iD.Entity().update({})).not.to.have.ownProperty('update'); }); + + it("sets v to 1 if previously undefined", function() { + expect(iD.Entity().update({}).v).to.equal(1); + }); + + it("increments v", function() { + expect(iD.Entity({v: 1}).update({}).v).to.equal(2); + }); }); describe("#mergeTags", function () {