Merge pull request #1754 from tschaub/clip

Track max extent for replay and limit draw instructions outside that extent.
This commit is contained in:
Tim Schaub
2014-03-06 09:37:43 -07:00
7 changed files with 655 additions and 21 deletions

View File

@@ -1,7 +1,9 @@
goog.provide('ol.Extent');
goog.provide('ol.extent');
goog.provide('ol.extent.Relationship');
goog.require('goog.asserts');
goog.require('goog.vec.Mat4');
goog.require('ol.Coordinate');
goog.require('ol.Size');
goog.require('ol.TransformFunction');
@@ -15,6 +17,20 @@ goog.require('ol.TransformFunction');
ol.Extent;
/**
* Relationship to an extent.
* @enum {number}
*/
ol.extent.Relationship = {
UNKNOWN: 0,
INTERSECTING: 1,
ABOVE: 2,
RIGHT: 4,
BELOW: 8,
LEFT: 16
};
/**
* Builds an extent that includes all given coordinates.
*
@@ -150,6 +166,38 @@ ol.extent.containsExtent = function(extent1, extent2) {
};
/**
* Get the relationship between a coordinate and extent.
* @param {ol.Extent} extent The extent.
* @param {ol.Coordinate} coordinate The coordinate.
* @return {number} The relationship (bitwise compare with
* ol.extent.Relationship).
*/
ol.extent.coordinateRelationship = function(extent, coordinate) {
var minX = extent[0];
var minY = extent[1];
var maxX = extent[2];
var maxY = extent[3];
var x = coordinate[0];
var y = coordinate[1];
var relationship = ol.extent.Relationship.UNKNOWN;
if (x < minX) {
relationship = relationship | ol.extent.Relationship.LEFT;
} else if (x > maxX) {
relationship = relationship | ol.extent.Relationship.RIGHT;
}
if (y < minY) {
relationship = relationship | ol.extent.Relationship.BELOW;
} else if (y > maxY) {
relationship = relationship | ol.extent.Relationship.ABOVE;
}
if (relationship === ol.extent.Relationship.UNKNOWN) {
relationship = ol.extent.Relationship.INTERSECTING;
}
return relationship;
};
/**
* @return {ol.Extent} Empty extent.
* @todo stability experimental
@@ -596,6 +644,59 @@ ol.extent.scaleFromCenter = function(extent, value) {
};
/**
* Determine if the segment between two coordinates intersects (crosses,
* touches, or is contained by) the provided extent.
* @param {ol.Extent} extent The extent.
* @param {ol.Coordinate} start Segment start coordinate.
* @param {ol.Coordinate} end Segment end coordinate.
* @return {boolean} The segment intersects the extent.
*/
ol.extent.segmentIntersects = function(extent, start, end) {
var intersects = false;
var startRel = ol.extent.coordinateRelationship(extent, start);
var endRel = ol.extent.coordinateRelationship(extent, end);
if (startRel === ol.extent.Relationship.INTERSECTING ||
endRel === ol.extent.Relationship.INTERSECTING) {
intersects = true;
} else {
var minX = extent[0];
var minY = extent[1];
var maxX = extent[2];
var maxY = extent[3];
var startX = start[0];
var startY = start[1];
var endX = end[0];
var endY = end[1];
var slope = (endY - startY) / (endX - startX);
var x, y;
if (!!(endRel & ol.extent.Relationship.ABOVE) &&
!(startRel & ol.extent.Relationship.ABOVE)) {
// potentially intersects top
x = endX - ((endY - maxY) / slope);
intersects = x >= minX && x <= maxX;
} else if (!!(endRel & ol.extent.Relationship.RIGHT) &&
!(startRel & ol.extent.Relationship.RIGHT)) {
// potentially intersects right
y = endY - ((endX - maxX) * slope);
intersects = y >= minY && y <= maxY;
} else if (!!(endRel & ol.extent.Relationship.BELOW) &&
!(startRel & ol.extent.Relationship.BELOW)) {
// potentially intersects bottom
x = endX - ((endY - minY) / slope);
intersects = x >= minX && x <= maxX;
} else if (!!(endRel & ol.extent.Relationship.LEFT) &&
!(startRel & ol.extent.Relationship.LEFT)) {
// potentially intersects left
y = endY - ((endX - minX) * slope);
intersects = y >= minY && y <= maxY;
}
}
return intersects;
};
/**
* @param {ol.Extent} extent1 Extent 1.
* @param {ol.Extent} extent2 Extent 2.
@@ -629,3 +730,37 @@ ol.extent.transform = function(extent, transformFn, opt_extent) {
var ys = [coordinates[1], coordinates[3], coordinates[5], coordinates[7]];
return ol.extent.boundingExtentXYs_(xs, ys, opt_extent);
};
/**
* Apply a 2d transform to an extent.
* @param {ol.Extent} extent Input extent.
* @param {goog.vec.Mat4.Number} transform The transform matrix.
* @param {ol.Extent=} opt_extent Optional extent for return values.
* @return {ol.Extent} The transformed extent.
*/
ol.extent.transform2D = function(extent, transform, opt_extent) {
var dest = goog.isDef(opt_extent) ? opt_extent : [];
var m00 = goog.vec.Mat4.getElement(transform, 0, 0);
var m10 = goog.vec.Mat4.getElement(transform, 1, 0);
var m01 = goog.vec.Mat4.getElement(transform, 0, 1);
var m11 = goog.vec.Mat4.getElement(transform, 1, 1);
var m03 = goog.vec.Mat4.getElement(transform, 0, 3);
var m13 = goog.vec.Mat4.getElement(transform, 1, 3);
var xi = [0, 2, 0, 2];
var yi = [1, 1, 3, 3];
var xs = [];
var ys = [];
var i, x, y;
for (i = 0; i < 4; ++i) {
x = extent[xi[i]];
y = extent[yi[i]];
xs[i] = m00 * x + m01 * y + m03;
ys[i] = m10 * x + m11 * y + m13;
}
dest[0] = Math.min.apply(null, xs);
dest[1] = Math.min.apply(null, ys);
dest[2] = Math.max.apply(null, xs);
dest[3] = Math.max.apply(null, ys);
return dest;
};

View File

@@ -14,6 +14,7 @@ goog.require('ol.BrowserFeature');
goog.require('ol.array');
goog.require('ol.color');
goog.require('ol.extent');
goog.require('ol.extent.Relationship');
goog.require('ol.geom.flat');
goog.require('ol.geom.simplify');
goog.require('ol.render.IReplayGroup');
@@ -47,10 +48,11 @@ ol.render.canvas.Instruction = {
* @constructor
* @implements {ol.render.IVectorContext}
* @param {number} tolerance Tolerance.
* @param {ol.Extent} maxExtent Maximum extent.
* @protected
* @struct
*/
ol.render.canvas.Replay = function(tolerance) {
ol.render.canvas.Replay = function(tolerance, maxExtent) {
/**
* @protected
@@ -58,6 +60,12 @@ ol.render.canvas.Replay = function(tolerance) {
*/
this.tolerance = tolerance;
/**
* @protected
* @type {ol.Extent}
*/
this.maxExtent = maxExtent;
/**
* @private
* @type {Array.<*>}
@@ -126,12 +134,38 @@ ol.render.canvas.Replay = function(tolerance) {
*/
ol.render.canvas.Replay.prototype.appendFlatCoordinates =
function(flatCoordinates, offset, end, stride, close) {
var myEnd = this.coordinates.length;
var i;
for (i = offset; i < end; i += stride) {
this.coordinates[myEnd++] = flatCoordinates[i];
this.coordinates[myEnd++] = flatCoordinates[i + 1];
var extent = this.maxExtent;
var lastCoord = [flatCoordinates[offset], flatCoordinates[offset + 1]];
var nextCoord = [NaN, NaN];
var skipped = true;
var i, lastRel, nextRel;
for (i = offset + stride; i < end; i += stride) {
nextCoord[0] = flatCoordinates[i];
nextCoord[1] = flatCoordinates[i + 1];
nextRel = ol.extent.coordinateRelationship(extent, nextCoord);
if (nextRel !== lastRel) {
if (skipped) {
this.coordinates[myEnd++] = lastCoord[0];
this.coordinates[myEnd++] = lastCoord[1];
}
this.coordinates[myEnd++] = nextCoord[0];
this.coordinates[myEnd++] = nextCoord[1];
skipped = false;
} else if (nextRel === ol.extent.Relationship.INTERSECTING) {
this.coordinates[myEnd++] = nextCoord[0];
this.coordinates[myEnd++] = nextCoord[1];
skipped = false;
} else {
skipped = true;
}
lastCoord[0] = nextCoord[0];
lastCoord[1] = nextCoord[1];
lastRel = nextRel;
}
if (close) {
this.coordinates[myEnd++] = flatCoordinates[offset];
this.coordinates[myEnd++] = flatCoordinates[offset + 1];
@@ -582,12 +616,13 @@ ol.render.canvas.Replay.prototype.setTextStyle = goog.abstractMethod;
* @constructor
* @extends {ol.render.canvas.Replay}
* @param {number} tolerance Tolerance.
* @param {ol.Extent} maxExtent Maximum extent.
* @protected
* @struct
*/
ol.render.canvas.ImageReplay = function(tolerance) {
ol.render.canvas.ImageReplay = function(tolerance, maxExtent) {
goog.base(this, tolerance);
goog.base(this, tolerance, maxExtent);
/**
* @private
@@ -810,12 +845,13 @@ ol.render.canvas.ImageReplay.prototype.setImageStyle = function(imageStyle) {
* @constructor
* @extends {ol.render.canvas.Replay}
* @param {number} tolerance Tolerance.
* @param {ol.Extent} maxExtent Maximum extent.
* @protected
* @struct
*/
ol.render.canvas.LineStringReplay = function(tolerance) {
ol.render.canvas.LineStringReplay = function(tolerance, maxExtent) {
goog.base(this, tolerance);
goog.base(this, tolerance, maxExtent);
/**
* @private
@@ -1027,12 +1063,13 @@ ol.render.canvas.LineStringReplay.prototype.setFillStrokeStyle =
* @constructor
* @extends {ol.render.canvas.Replay}
* @param {number} tolerance Tolerance.
* @param {ol.Extent} maxExtent Maximum extent.
* @protected
* @struct
*/
ol.render.canvas.PolygonReplay = function(tolerance) {
ol.render.canvas.PolygonReplay = function(tolerance, maxExtent) {
goog.base(this, tolerance);
goog.base(this, tolerance, maxExtent);
/**
* @private
@@ -1360,12 +1397,13 @@ ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyles_ = function() {
* @constructor
* @extends {ol.render.canvas.Replay}
* @param {number} tolerance Tolerance.
* @param {ol.Extent} maxExtent Maximum extent.
* @protected
* @struct
*/
ol.render.canvas.TextReplay = function(tolerance) {
ol.render.canvas.TextReplay = function(tolerance, maxExtent) {
goog.base(this, tolerance);
goog.base(this, tolerance, maxExtent);
/**
* @private
@@ -1659,9 +1697,10 @@ ol.render.canvas.TextReplay.prototype.setTextStyle = function(textStyle) {
* @constructor
* @implements {ol.render.IReplayGroup}
* @param {number} tolerance Tolerance.
* @param {ol.Extent} maxExtent Max extent.
* @struct
*/
ol.render.canvas.ReplayGroup = function(tolerance) {
ol.render.canvas.ReplayGroup = function(tolerance, maxExtent) {
/**
* @private
@@ -1669,6 +1708,12 @@ ol.render.canvas.ReplayGroup = function(tolerance) {
*/
this.tolerance_ = tolerance;
/**
* @private
* @type {ol.Extent}
*/
this.maxExtent_ = maxExtent;
/**
* @private
* @type {Object.<string,
@@ -1772,6 +1817,23 @@ ol.render.canvas.ReplayGroup.prototype.replayHitDetection_ = function(
ol.render.canvas.ReplayGroup.prototype.replay_ = function(
zs, context, extent, pixelRatio, transform, viewRotation,
renderGeometryFunction) {
var maxExtent = this.maxExtent_;
var minX = maxExtent[0];
var minY = maxExtent[1];
var maxX = maxExtent[2];
var maxY = maxExtent[3];
var flatClipCoords = ol.geom.flat.transform2D(
[minX, minY, minX, maxY, maxX, maxY, maxX, minY], 2, transform);
context.save();
context.beginPath();
context.moveTo(flatClipCoords[0], flatClipCoords[1]);
context.lineTo(flatClipCoords[2], flatClipCoords[3]);
context.lineTo(flatClipCoords[4], flatClipCoords[5]);
context.lineTo(flatClipCoords[6], flatClipCoords[7]);
context.closePath();
context.clip();
var i, ii, j, jj, replays, replayType, replay, result;
for (i = 0, ii = zs.length; i < ii; ++i) {
replays = this.replaysByZIndex_[zs[i].toString()];
@@ -1787,6 +1849,8 @@ ol.render.canvas.ReplayGroup.prototype.replay_ = function(
}
}
}
context.restore();
return undefined;
};
@@ -1866,9 +1930,9 @@ ol.render.canvas.ReplayGroup.prototype.getReplay =
}
var replay = replays[replayType];
if (!goog.isDef(replay)) {
var constructor = ol.render.canvas.BATCH_CONSTRUCTORS_[replayType];
goog.asserts.assert(goog.isDef(constructor));
replay = new constructor(this.tolerance_);
var Constructor = ol.render.canvas.BATCH_CONSTRUCTORS_[replayType];
goog.asserts.assert(goog.isDef(Constructor));
replay = new Constructor(this.tolerance_, this.maxExtent_);
replays[replayType] = replay;
}
return replay;
@@ -1887,7 +1951,7 @@ ol.render.canvas.ReplayGroup.prototype.isEmpty = function() {
* @const
* @private
* @type {Object.<ol.render.ReplayType,
* function(new: ol.render.canvas.Replay, number)>}
* function(new: ol.render.canvas.Replay, number, ol.Extent)>}
*/
ol.render.canvas.BATCH_CONSTRUCTORS_ = {
'Image': ol.render.canvas.ImageReplay,

View File

@@ -243,7 +243,7 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
styleFunction = ol.feature.defaultStyleFunction;
}
var tolerance = frameStateResolution / (2 * pixelRatio);
var replayGroup = new ol.render.canvas.ReplayGroup(tolerance);
var replayGroup = new ol.render.canvas.ReplayGroup(tolerance, extent);
vectorSource.forEachFeatureInExtent(extent,
/**
* @param {ol.Feature} feature Feature.

View File

@@ -111,7 +111,7 @@ ol.source.ImageVector.prototype.canvasFunctionInternal_ =
function(extent, resolution, pixelRatio, size, projection) {
var tolerance = resolution / (2 * pixelRatio);
var replayGroup = new ol.render.canvas.ReplayGroup(tolerance);
var replayGroup = new ol.render.canvas.ReplayGroup(tolerance, extent);
var loading = false;
this.source_.forEachFeatureInExtent(extent,