Merge pull request #418 from twpayne/webgl-core-improvements
WebGL core improvements
This commit is contained in:
@@ -36,15 +36,20 @@ ol.Extent.boundingExtent = function(var_args) {
|
||||
var i;
|
||||
for (i = 1; i < arguments.length; ++i) {
|
||||
var coordinate = arguments[i];
|
||||
extent.minX = Math.min(extent.minX, coordinate.x);
|
||||
extent.minY = Math.min(extent.minY, coordinate.y);
|
||||
extent.maxX = Math.max(extent.maxX, coordinate.x);
|
||||
extent.maxY = Math.max(extent.maxY, coordinate.y);
|
||||
extent.extendXY(coordinate.x, coordinate.y);
|
||||
}
|
||||
return extent;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {ol.Extent} Empty extent.
|
||||
*/
|
||||
ol.Extent.createEmptyExtent = function() {
|
||||
return new ol.Extent(Infinity, Infinity, -Infinity, -Infinity);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Coordinate} center Center.
|
||||
* @param {number} resolution Resolution.
|
||||
|
||||
@@ -29,6 +29,16 @@ ol.math.csch = function(x) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} x X.
|
||||
* @return {number} The smallest power of two greater than or equal to x.
|
||||
*/
|
||||
ol.math.roundUpToPowerOfTwo = function(x) {
|
||||
goog.asserts.assert(0 < x);
|
||||
return Math.pow(2, Math.ceil(Math.log(x) / Math.LN2));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} x X.
|
||||
* @return {number} Hyperbolic secant of x.
|
||||
|
||||
@@ -15,9 +15,6 @@ goog.require('ol.Size');
|
||||
*/
|
||||
ol.Rectangle = function(minX, minY, maxX, maxY) {
|
||||
|
||||
goog.asserts.assert(minX <= maxX);
|
||||
goog.asserts.assert(minY <= maxY);
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
@@ -55,10 +52,38 @@ ol.Rectangle.prototype.equals = function(rectangle) {
|
||||
* @param {ol.Rectangle} rectangle Rectangle.
|
||||
*/
|
||||
ol.Rectangle.prototype.extend = function(rectangle) {
|
||||
this.minX = Math.min(this.minX, rectangle.minX);
|
||||
this.minY = Math.min(this.minY, rectangle.minY);
|
||||
this.maxX = Math.max(this.maxX, rectangle.maxX);
|
||||
this.maxY = Math.max(this.maxY, rectangle.maxY);
|
||||
if (rectangle.minX < this.minX) {
|
||||
this.minX = rectangle.minX;
|
||||
}
|
||||
if (rectangle.minY < this.minY) {
|
||||
this.minY = rectangle.minY;
|
||||
}
|
||||
if (rectangle.maxX > this.maxX) {
|
||||
this.maxX = rectangle.maxX;
|
||||
}
|
||||
if (rectangle.maxY > this.maxY) {
|
||||
this.maxY = rectangle.maxY;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} x X.
|
||||
* @param {number} y Y.
|
||||
*/
|
||||
ol.Rectangle.prototype.extendXY = function(x, y) {
|
||||
if (x < this.minX) {
|
||||
this.minX = x;
|
||||
}
|
||||
if (y < this.minY) {
|
||||
this.minY = y;
|
||||
}
|
||||
if (x > this.maxX) {
|
||||
this.maxX = x;
|
||||
}
|
||||
if (y > this.maxY) {
|
||||
this.maxY = y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -107,6 +132,14 @@ ol.Rectangle.prototype.intersects = function(rectangle) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Is empty.
|
||||
*/
|
||||
ol.Rectangle.prototype.isEmpty = function() {
|
||||
return this.maxX < this.minX || this.maxY < this.minY;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Coordinate} coordinate Coordinate.
|
||||
* @return {ol.Coordinate} Coordinate.
|
||||
|
||||
@@ -28,25 +28,6 @@ ol.renderer.webgl.ImageLayer = function(mapRenderer, imageLayer) {
|
||||
*/
|
||||
this.image_ = null;
|
||||
|
||||
/**
|
||||
* The last rendered texture.
|
||||
* @private
|
||||
* @type {WebGLTexture}
|
||||
*/
|
||||
this.texture_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {!goog.vec.Mat4.Number}
|
||||
*/
|
||||
this.texCoordMatrix_ = goog.vec.Mat4.createNumberIdentity();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {!goog.vec.Mat4.Number}
|
||||
*/
|
||||
this.projectionMatrix_ = goog.vec.Mat4.createNumber();
|
||||
|
||||
};
|
||||
goog.inherits(ol.renderer.webgl.ImageLayer, ol.renderer.webgl.Layer);
|
||||
|
||||
@@ -63,7 +44,7 @@ ol.renderer.webgl.ImageLayer.prototype.createTexture_ = function(image) {
|
||||
// http://learningwebgl.com/blog/?p=2101
|
||||
|
||||
var imageElement = image.getImageElement(this);
|
||||
var gl = this.getMapRenderer().getGL();
|
||||
var gl = this.getWebGLMapRenderer().getGL();
|
||||
|
||||
var texture = gl.createTexture();
|
||||
|
||||
@@ -86,43 +67,6 @@ ol.renderer.webgl.ImageLayer.prototype.createTexture_ = function(image) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.ImageLayer.prototype.disposeInternal = function() {
|
||||
var mapRenderer = this.getMapRenderer();
|
||||
var gl = mapRenderer.getGL();
|
||||
if (!gl.isContextLost()) {
|
||||
gl.deleteTexture(this.texture_);
|
||||
}
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.ImageLayer.prototype.getTexCoordMatrix = function() {
|
||||
return this.texCoordMatrix_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.ImageLayer.prototype.getTexture = function() {
|
||||
return this.texture_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.ImageLayer.prototype.getProjectionMatrix = function() {
|
||||
return this.projectionMatrix_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {ol.layer.ImageLayer} Tile layer.
|
||||
*/
|
||||
@@ -131,21 +75,13 @@ ol.renderer.webgl.ImageLayer.prototype.getImageLayer = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.ImageLayer.prototype.handleWebGLContextLost = function() {
|
||||
this.texture_ = null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.ImageLayer.prototype.renderFrame =
|
||||
function(frameState, layerState) {
|
||||
|
||||
var gl = this.getMapRenderer().getGL();
|
||||
var gl = this.getWebGLMapRenderer().getGL();
|
||||
|
||||
var view2DState = frameState.view2DState;
|
||||
var viewCenter = view2DState.center;
|
||||
@@ -153,7 +89,7 @@ ol.renderer.webgl.ImageLayer.prototype.renderFrame =
|
||||
var viewRotation = view2DState.rotation;
|
||||
|
||||
var image = this.image_;
|
||||
var texture = this.texture_;
|
||||
var texture = this.texture;
|
||||
var imageLayer = this.getImageLayer();
|
||||
var imageSource = imageLayer.getImageSource();
|
||||
|
||||
@@ -171,13 +107,13 @@ ol.renderer.webgl.ImageLayer.prototype.renderFrame =
|
||||
} else if (imageState == ol.ImageState.LOADED) {
|
||||
image = image_;
|
||||
texture = this.createTexture_(image_);
|
||||
if (!goog.isNull(this.texture_)) {
|
||||
if (!goog.isNull(this.texture)) {
|
||||
frameState.postRenderFunctions.push(
|
||||
goog.partial(function(gl, texture) {
|
||||
if (!gl.isContextLost()) {
|
||||
gl.deleteTexture(texture);
|
||||
}
|
||||
}, gl, this.texture_));
|
||||
}, gl, this.texture));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,19 +122,19 @@ ol.renderer.webgl.ImageLayer.prototype.renderFrame =
|
||||
if (!goog.isNull(image)) {
|
||||
goog.asserts.assert(!goog.isNull(texture));
|
||||
|
||||
var canvas = this.getMapRenderer().getCanvas();
|
||||
var canvas = this.getWebGLMapRenderer().getCanvas();
|
||||
|
||||
this.updateProjectionMatrix_(canvas.width, canvas.height,
|
||||
viewCenter, viewResolution, viewRotation, image.getExtent());
|
||||
|
||||
// Translate and scale to flip the Y coord.
|
||||
var texCoordMatrix = this.texCoordMatrix_;
|
||||
var texCoordMatrix = this.texCoordMatrix;
|
||||
goog.vec.Mat4.makeIdentity(texCoordMatrix);
|
||||
goog.vec.Mat4.scale(texCoordMatrix, 1, -1, 1);
|
||||
goog.vec.Mat4.translate(texCoordMatrix, 0, -1, 0);
|
||||
|
||||
this.image_ = image;
|
||||
this.texture_ = texture;
|
||||
this.texture = texture;
|
||||
|
||||
this.updateAttributions(frameState.attributions, image.getAttributions());
|
||||
}
|
||||
@@ -221,7 +157,7 @@ ol.renderer.webgl.ImageLayer.prototype.updateProjectionMatrix_ =
|
||||
var canvasExtentWidth = canvasWidth * viewResolution;
|
||||
var canvasExtentHeight = canvasHeight * viewResolution;
|
||||
|
||||
var projectionMatrix = this.projectionMatrix_;
|
||||
var projectionMatrix = this.projectionMatrix;
|
||||
goog.vec.Mat4.makeIdentity(projectionMatrix);
|
||||
goog.vec.Mat4.scale(projectionMatrix,
|
||||
2 / canvasExtentWidth, 2 / canvasExtentHeight, 1);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
goog.provide('ol.renderer.webgl.Layer');
|
||||
|
||||
goog.require('goog.vec.Mat4');
|
||||
goog.require('ol.FrameState');
|
||||
goog.require('ol.layer.Layer');
|
||||
goog.require('ol.renderer.Layer');
|
||||
goog.require('ol.vec.Mat4');
|
||||
@@ -19,6 +20,36 @@ ol.renderer.webgl.Layer = function(mapRenderer, layer) {
|
||||
|
||||
goog.base(this, mapRenderer, layer);
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @type {WebGLTexture}
|
||||
*/
|
||||
this.texture = null;
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @type {WebGLFramebuffer}
|
||||
*/
|
||||
this.framebuffer = null;
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
this.framebufferDimension = undefined;
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @type {!goog.vec.Mat4.Number}
|
||||
*/
|
||||
this.texCoordMatrix = goog.vec.Mat4.createNumber();
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @type {!goog.vec.Mat4.Number}
|
||||
*/
|
||||
this.projectionMatrix = goog.vec.Mat4.createNumberIdentity();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {!goog.vec.Mat4.Float32}
|
||||
@@ -64,6 +95,55 @@ ol.renderer.webgl.Layer = function(mapRenderer, layer) {
|
||||
goog.inherits(ol.renderer.webgl.Layer, ol.renderer.Layer);
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.FrameState} frameState Frame state.
|
||||
* @param {number} framebufferDimension Framebuffer dimension.
|
||||
* @protected
|
||||
*/
|
||||
ol.renderer.webgl.Layer.prototype.bindFramebuffer =
|
||||
function(frameState, framebufferDimension) {
|
||||
|
||||
var mapRenderer = this.getWebGLMapRenderer();
|
||||
var gl = mapRenderer.getGL();
|
||||
|
||||
if (!goog.isDef(this.framebufferDimension) ||
|
||||
this.framebufferDimension != framebufferDimension) {
|
||||
|
||||
var map = this.getMap();
|
||||
frameState.postRenderFunctions.push(
|
||||
goog.partial(function(gl, framebuffer, texture) {
|
||||
if (!gl.isContextLost()) {
|
||||
gl.deleteFramebuffer(framebuffer);
|
||||
gl.deleteTexture(texture);
|
||||
}
|
||||
}, gl, this.framebuffer, this.texture));
|
||||
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(goog.webgl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA,
|
||||
framebufferDimension, framebufferDimension, 0, goog.webgl.RGBA,
|
||||
goog.webgl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER,
|
||||
goog.webgl.LINEAR);
|
||||
gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER,
|
||||
goog.webgl.LINEAR);
|
||||
|
||||
var framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, framebuffer);
|
||||
gl.framebufferTexture2D(goog.webgl.FRAMEBUFFER,
|
||||
goog.webgl.COLOR_ATTACHMENT0, goog.webgl.TEXTURE_2D, texture, 0);
|
||||
|
||||
this.texture = texture;
|
||||
this.framebuffer = framebuffer;
|
||||
this.framebufferDimension = framebufferDimension;
|
||||
|
||||
} else {
|
||||
gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, this.framebuffer);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!goog.vec.Mat4.Float32} Color matrix.
|
||||
*/
|
||||
@@ -76,31 +156,35 @@ ol.renderer.webgl.Layer.prototype.getColorMatrix = function() {
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return {ol.renderer.Map} MapRenderer.
|
||||
* @return {ol.renderer.webgl.Map} MapRenderer.
|
||||
*/
|
||||
ol.renderer.webgl.Layer.prototype.getMapRenderer = function() {
|
||||
return /** @type {ol.renderer.webgl.Map} */ (goog.base(
|
||||
this, 'getMapRenderer'));
|
||||
ol.renderer.webgl.Layer.prototype.getWebGLMapRenderer = function() {
|
||||
return /** @type {ol.renderer.webgl.Map} */ (this.getMapRenderer());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!goog.vec.Mat4.Number} Matrix.
|
||||
*/
|
||||
ol.renderer.webgl.Layer.prototype.getTexCoordMatrix = goog.abstractMethod;
|
||||
ol.renderer.webgl.Layer.prototype.getTexCoordMatrix = function() {
|
||||
return this.texCoordMatrix;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {WebGLTexture} Texture.
|
||||
*/
|
||||
ol.renderer.webgl.Layer.prototype.getTexture = goog.abstractMethod;
|
||||
ol.renderer.webgl.Layer.prototype.getTexture = function() {
|
||||
return this.texture;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!goog.vec.Mat4.Number} Matrix.
|
||||
*/
|
||||
ol.renderer.webgl.Layer.prototype.getProjectionMatrix = goog.abstractMethod;
|
||||
ol.renderer.webgl.Layer.prototype.getProjectionMatrix = function() {
|
||||
return this.projectionMatrix;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@@ -150,7 +234,11 @@ ol.renderer.webgl.Layer.prototype.handleLayerSaturationChange = function() {
|
||||
/**
|
||||
* Handle webglcontextlost.
|
||||
*/
|
||||
ol.renderer.webgl.Layer.prototype.handleWebGLContextLost = goog.nullFunction;
|
||||
ol.renderer.webgl.Layer.prototype.handleWebGLContextLost = function() {
|
||||
this.texture = null;
|
||||
this.framebuffer = null;
|
||||
this.framebufferDimension = undefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,8 @@ goog.require('ol.renderer.webgl.FragmentShader');
|
||||
goog.require('ol.renderer.webgl.ImageLayer');
|
||||
goog.require('ol.renderer.webgl.TileLayer');
|
||||
goog.require('ol.renderer.webgl.VertexShader');
|
||||
goog.require('ol.structs.Buffer');
|
||||
goog.require('ol.structs.IntegerSet');
|
||||
goog.require('ol.structs.LRUCache');
|
||||
goog.require('ol.webgl');
|
||||
goog.require('ol.webgl.WebGLContextEventType');
|
||||
@@ -33,6 +35,14 @@ goog.require('ol.webgl.WebGLContextEventType');
|
||||
ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK = 1024;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{buf: ol.structs.Buffer,
|
||||
* buffer: WebGLBuffer,
|
||||
* dirtySet: ol.structs.IntegerSet}}
|
||||
*/
|
||||
ol.renderer.webgl.BufferCacheEntry;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{magFilter: number, minFilter: number, texture: WebGLTexture}}
|
||||
*/
|
||||
@@ -170,9 +180,20 @@ ol.renderer.webgl.Map = function(container, map) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {WebGLBuffer}
|
||||
* @type {ol.structs.Buffer}
|
||||
*/
|
||||
this.arrayBuffer_ = null;
|
||||
this.arrayBuffer_ = new ol.structs.Buffer([
|
||||
-1, -1, 0, 0,
|
||||
1, -1, 1, 0,
|
||||
-1, 1, 0, 1,
|
||||
1, 1, 1, 1
|
||||
]);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Object.<number, ol.renderer.webgl.BufferCacheEntry>}
|
||||
*/
|
||||
this.bufferCache_ = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -216,6 +237,47 @@ ol.renderer.webgl.Map = function(container, map) {
|
||||
goog.inherits(ol.renderer.webgl.Map, ol.renderer.Map);
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} target Target.
|
||||
* @param {ol.structs.Buffer} buf Buffer.
|
||||
*/
|
||||
ol.renderer.webgl.Map.prototype.bindBuffer = function(target, buf) {
|
||||
var gl = this.getGL();
|
||||
var arr = buf.getArray();
|
||||
var bufferKey = goog.getUid(buf);
|
||||
if (bufferKey in this.bufferCache_) {
|
||||
var bufferCacheEntry = this.bufferCache_[bufferKey];
|
||||
gl.bindBuffer(target, bufferCacheEntry.buffer);
|
||||
bufferCacheEntry.dirtySet.forEachRange(function(start, stop) {
|
||||
// FIXME check if slice is really efficient here
|
||||
var slice = arr.slice(start, stop);
|
||||
gl.bufferSubData(
|
||||
target,
|
||||
start,
|
||||
target == goog.webgl.ARRAY_BUFFER ?
|
||||
new Float32Array(slice) :
|
||||
new Uint16Array(slice));
|
||||
});
|
||||
bufferCacheEntry.dirtySet.clear();
|
||||
} else {
|
||||
var buffer = gl.createBuffer();
|
||||
gl.bindBuffer(target, buffer);
|
||||
gl.bufferData(
|
||||
target,
|
||||
target == goog.webgl.ARRAY_BUFFER ?
|
||||
new Float32Array(arr) : new Uint16Array(arr),
|
||||
buf.getUsage());
|
||||
var dirtySet = new ol.structs.IntegerSet();
|
||||
buf.addDirtySet(dirtySet);
|
||||
this.bufferCache_[bufferKey] = {
|
||||
buf: buf,
|
||||
buffer: buffer,
|
||||
dirtySet: dirtySet
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Tile} tile Tile.
|
||||
* @param {number} magFilter Mag filter.
|
||||
@@ -276,12 +338,35 @@ ol.renderer.webgl.Map.prototype.createLayerRenderer = function(layer) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.structs.Buffer} buf Buffer.
|
||||
*/
|
||||
ol.renderer.webgl.Map.prototype.deleteBuffer = function(buf) {
|
||||
var gl = this.getGL();
|
||||
var arr = buf.getArray();
|
||||
var bufferKey = goog.getUid(buf);
|
||||
goog.asserts.assert(bufferKey in this.bufferCache_);
|
||||
var bufferCacheEntry = this.bufferCache_[bufferKey];
|
||||
bufferCacheEntry.buf.removeDirtySet(bufferCacheEntry.dirtySet);
|
||||
if (!gl.isContextLost()) {
|
||||
gl.deleteBuffer(bufferCacheEntry.buffer);
|
||||
}
|
||||
delete this.bufferCache_[bufferKey];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.Map.prototype.disposeInternal = function() {
|
||||
var gl = this.getGL();
|
||||
goog.object.forEach(this.bufferCache_, function(bufferCacheEntry) {
|
||||
bufferCacheEntry.buf.removeDirtySet(bufferCacheEntry.dirtySet);
|
||||
});
|
||||
if (!gl.isContextLost()) {
|
||||
goog.object.forEach(this.bufferCache_, function(bufferCacheEntry) {
|
||||
gl.deleteBuffer(bufferCacheEntry.buffer);
|
||||
});
|
||||
goog.object.forEach(this.programCache_, function(program) {
|
||||
gl.deleteProgram(program);
|
||||
});
|
||||
@@ -409,7 +494,7 @@ ol.renderer.webgl.Map.prototype.handleWebGLContextLost = function(event) {
|
||||
}
|
||||
event.preventDefault();
|
||||
this.locations_ = null;
|
||||
this.arrayBuffer_ = null;
|
||||
this.bufferCache_ = {};
|
||||
this.shaderCache_ = {};
|
||||
this.programCache_ = {};
|
||||
this.textureCache_.clear();
|
||||
@@ -511,19 +596,7 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
|
||||
};
|
||||
}
|
||||
|
||||
if (goog.isNull(this.arrayBuffer_)) {
|
||||
var arrayBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(goog.webgl.ARRAY_BUFFER, arrayBuffer);
|
||||
gl.bufferData(goog.webgl.ARRAY_BUFFER, new Float32Array([
|
||||
-1, -1, 0, 0,
|
||||
1, -1, 1, 0,
|
||||
-1, 1, 0, 1,
|
||||
1, 1, 1, 1
|
||||
]), goog.webgl.STATIC_DRAW);
|
||||
this.arrayBuffer_ = arrayBuffer;
|
||||
} else {
|
||||
gl.bindBuffer(goog.webgl.ARRAY_BUFFER, this.arrayBuffer_);
|
||||
}
|
||||
this.bindBuffer(goog.webgl.ARRAY_BUFFER, this.arrayBuffer_);
|
||||
|
||||
gl.enableVertexAttribArray(this.locations_.a_position);
|
||||
gl.vertexAttribPointer(
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
// FIXME animated shaders! check in redraw
|
||||
|
||||
goog.provide('ol.renderer.webgl.TileLayer');
|
||||
goog.provide('ol.renderer.webgl.tilelayerrenderer');
|
||||
goog.provide('ol.renderer.webgl.tilelayerrenderer.shader.Fragment');
|
||||
goog.provide('ol.renderer.webgl.tilelayerrenderer.shader.Vertex');
|
||||
goog.provide('ol.renderer.webgl.tilelayer.shader');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.object');
|
||||
@@ -13,16 +11,17 @@ goog.require('goog.vec.Mat4');
|
||||
goog.require('goog.vec.Vec4');
|
||||
goog.require('goog.webgl');
|
||||
goog.require('ol.Extent');
|
||||
goog.require('ol.FrameState');
|
||||
goog.require('ol.Size');
|
||||
goog.require('ol.Tile');
|
||||
goog.require('ol.TileCoord');
|
||||
goog.require('ol.TileRange');
|
||||
goog.require('ol.TileState');
|
||||
goog.require('ol.layer.TileLayer');
|
||||
goog.require('ol.math');
|
||||
goog.require('ol.renderer.webgl.FragmentShader');
|
||||
goog.require('ol.renderer.webgl.Layer');
|
||||
goog.require('ol.renderer.webgl.VertexShader');
|
||||
goog.require('ol.structs.Buffer');
|
||||
|
||||
|
||||
|
||||
@@ -30,23 +29,23 @@ goog.require('ol.renderer.webgl.VertexShader');
|
||||
* @constructor
|
||||
* @extends {ol.renderer.webgl.FragmentShader}
|
||||
*/
|
||||
ol.renderer.webgl.tilelayerrenderer.shader.Fragment = function() {
|
||||
ol.renderer.webgl.tilelayer.shader.Fragment = function() {
|
||||
goog.base(this, [
|
||||
'precision mediump float;',
|
||||
'',
|
||||
'uniform sampler2D uTexture;',
|
||||
'uniform sampler2D u_texture;',
|
||||
'',
|
||||
'varying vec2 vTexCoord;',
|
||||
'varying vec2 v_texCoord;',
|
||||
'',
|
||||
'void main(void) {',
|
||||
' gl_FragColor = texture2D(uTexture, vTexCoord);',
|
||||
' gl_FragColor = texture2D(u_texture, v_texCoord);',
|
||||
'}'
|
||||
].join('\n'));
|
||||
};
|
||||
goog.inherits(
|
||||
ol.renderer.webgl.tilelayerrenderer.shader.Fragment,
|
||||
ol.renderer.webgl.tilelayer.shader.Fragment,
|
||||
ol.renderer.webgl.FragmentShader);
|
||||
goog.addSingletonGetter(ol.renderer.webgl.tilelayerrenderer.shader.Fragment);
|
||||
goog.addSingletonGetter(ol.renderer.webgl.tilelayer.shader.Fragment);
|
||||
|
||||
|
||||
|
||||
@@ -54,27 +53,27 @@ goog.addSingletonGetter(ol.renderer.webgl.tilelayerrenderer.shader.Fragment);
|
||||
* @constructor
|
||||
* @extends {ol.renderer.webgl.VertexShader}
|
||||
*/
|
||||
ol.renderer.webgl.tilelayerrenderer.shader.Vertex = function() {
|
||||
ol.renderer.webgl.tilelayer.shader.Vertex = function() {
|
||||
goog.base(this, [
|
||||
'attribute vec2 aPosition;',
|
||||
'attribute vec2 aTexCoord;',
|
||||
'attribute vec2 a_position;',
|
||||
'attribute vec2 a_texCoord;',
|
||||
'',
|
||||
'varying vec2 vTexCoord;',
|
||||
'varying vec2 v_texCoord;',
|
||||
'',
|
||||
'uniform vec4 uTileOffset;',
|
||||
'uniform vec4 u_tileOffset;',
|
||||
'',
|
||||
'void main(void) {',
|
||||
' gl_Position.xy = aPosition * uTileOffset.xy + uTileOffset.zw;',
|
||||
' gl_Position.xy = a_position * u_tileOffset.xy + u_tileOffset.zw;',
|
||||
' gl_Position.z = 0.;',
|
||||
' gl_Position.w = 1.;',
|
||||
' vTexCoord = aTexCoord;',
|
||||
' v_texCoord = a_texCoord;',
|
||||
'}'
|
||||
].join('\n'));
|
||||
};
|
||||
goog.inherits(
|
||||
ol.renderer.webgl.tilelayerrenderer.shader.Vertex,
|
||||
ol.renderer.webgl.tilelayer.shader.Vertex,
|
||||
ol.renderer.webgl.VertexShader);
|
||||
goog.addSingletonGetter(ol.renderer.webgl.tilelayerrenderer.shader.Vertex);
|
||||
goog.addSingletonGetter(ol.renderer.webgl.tilelayer.shader.Vertex);
|
||||
|
||||
|
||||
|
||||
@@ -93,59 +92,33 @@ ol.renderer.webgl.TileLayer = function(mapRenderer, tileLayer) {
|
||||
* @type {ol.renderer.webgl.FragmentShader}
|
||||
*/
|
||||
this.fragmentShader_ =
|
||||
ol.renderer.webgl.tilelayerrenderer.shader.Fragment.getInstance();
|
||||
ol.renderer.webgl.tilelayer.shader.Fragment.getInstance();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.renderer.webgl.VertexShader}
|
||||
*/
|
||||
this.vertexShader_ =
|
||||
ol.renderer.webgl.tilelayerrenderer.shader.Vertex.getInstance();
|
||||
this.vertexShader_ = ol.renderer.webgl.tilelayer.shader.Vertex.getInstance();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {{aPosition: number,
|
||||
* aTexCoord: number,
|
||||
* uTileOffset: WebGLUniformLocation,
|
||||
* uTexture: WebGLUniformLocation}|null}
|
||||
* @type {{a_position: number,
|
||||
* a_texCoord: number,
|
||||
* u_tileOffset: WebGLUniformLocation,
|
||||
* u_texture: WebGLUniformLocation}|null}
|
||||
*/
|
||||
this.locations_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {WebGLBuffer}
|
||||
* @type {ol.structs.Buffer}
|
||||
*/
|
||||
this.arrayBuffer_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {WebGLTexture}
|
||||
*/
|
||||
this.texture_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {WebGLFramebuffer}
|
||||
*/
|
||||
this.framebuffer_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
this.framebufferDimension_ = undefined;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {!goog.vec.Mat4.Number}
|
||||
*/
|
||||
this.texCoordMatrix_ = goog.vec.Mat4.createNumber();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {!goog.vec.Mat4.Number}
|
||||
*/
|
||||
this.projectionMatrix_ = goog.vec.Mat4.createNumberIdentity();
|
||||
this.arrayBuffer_ = new ol.structs.Buffer([
|
||||
0, 0, 0, 1,
|
||||
1, 0, 1, 1,
|
||||
0, 1, 0, 0,
|
||||
1, 1, 1, 0
|
||||
]);
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -163,94 +136,16 @@ ol.renderer.webgl.TileLayer = function(mapRenderer, tileLayer) {
|
||||
goog.inherits(ol.renderer.webgl.TileLayer, ol.renderer.webgl.Layer);
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.FrameState} frameState Frame state.
|
||||
* @param {number} framebufferDimension Framebuffer dimension.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.webgl.TileLayer.prototype.bindFramebuffer_ =
|
||||
function(frameState, framebufferDimension) {
|
||||
|
||||
var mapRenderer = this.getMapRenderer();
|
||||
var gl = mapRenderer.getGL();
|
||||
|
||||
if (!goog.isDef(this.framebufferDimension_) ||
|
||||
this.framebufferDimension_ != framebufferDimension) {
|
||||
|
||||
var map = this.getMap();
|
||||
frameState.postRenderFunctions.push(
|
||||
goog.partial(function(gl, framebuffer, texture) {
|
||||
if (!gl.isContextLost()) {
|
||||
gl.deleteFramebuffer(framebuffer);
|
||||
gl.deleteTexture(texture);
|
||||
}
|
||||
}, gl, this.framebuffer_, this.texture_));
|
||||
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(goog.webgl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA,
|
||||
framebufferDimension, framebufferDimension, 0, goog.webgl.RGBA,
|
||||
goog.webgl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER,
|
||||
goog.webgl.LINEAR);
|
||||
gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER,
|
||||
goog.webgl.LINEAR);
|
||||
|
||||
var framebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, framebuffer);
|
||||
gl.framebufferTexture2D(goog.webgl.FRAMEBUFFER,
|
||||
goog.webgl.COLOR_ATTACHMENT0, goog.webgl.TEXTURE_2D, texture, 0);
|
||||
|
||||
this.texture_ = texture;
|
||||
this.framebuffer_ = framebuffer;
|
||||
this.framebufferDimension_ = framebufferDimension;
|
||||
|
||||
} else {
|
||||
gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, this.framebuffer_);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.TileLayer.prototype.disposeInternal = function() {
|
||||
var mapRenderer = this.getMapRenderer();
|
||||
var gl = mapRenderer.getGL();
|
||||
if (!gl.isContextLost()) {
|
||||
gl.deleteBuffer(this.arrayBuffer_);
|
||||
gl.deleteFramebuffer(this.framebuffer_);
|
||||
gl.deleteTexture(this.texture_);
|
||||
}
|
||||
var mapRenderer = this.getWebGLMapRenderer();
|
||||
mapRenderer.deleteBuffer(this.arrayBuffer_);
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.TileLayer.prototype.getTexCoordMatrix = function() {
|
||||
return this.texCoordMatrix_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.TileLayer.prototype.getTexture = function() {
|
||||
return this.texture_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.TileLayer.prototype.getProjectionMatrix = function() {
|
||||
return this.projectionMatrix_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {ol.layer.TileLayer} Tile layer.
|
||||
*/
|
||||
@@ -263,11 +158,8 @@ ol.renderer.webgl.TileLayer.prototype.getTileLayer = function() {
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.TileLayer.prototype.handleWebGLContextLost = function() {
|
||||
goog.base(this, 'handleWebGLContextLost');
|
||||
this.locations_ = null;
|
||||
this.arrayBuffer_ = null;
|
||||
this.texture_ = null;
|
||||
this.framebuffer_ = null;
|
||||
this.framebufferDimension_ = undefined;
|
||||
};
|
||||
|
||||
|
||||
@@ -277,7 +169,7 @@ ol.renderer.webgl.TileLayer.prototype.handleWebGLContextLost = function() {
|
||||
ol.renderer.webgl.TileLayer.prototype.renderFrame =
|
||||
function(frameState, layerState) {
|
||||
|
||||
var mapRenderer = this.getMapRenderer();
|
||||
var mapRenderer = this.getWebGLMapRenderer();
|
||||
var gl = mapRenderer.getGL();
|
||||
|
||||
var view2DState = frameState.view2DState;
|
||||
@@ -316,8 +208,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
|
||||
var maxDimension = Math.max(
|
||||
tileRangeSize.width * tileSize.width,
|
||||
tileRangeSize.height * tileSize.height);
|
||||
var framebufferDimension =
|
||||
Math.pow(2, Math.ceil(Math.log(maxDimension) / Math.log(2)));
|
||||
var framebufferDimension = ol.math.roundUpToPowerOfTwo(maxDimension);
|
||||
var framebufferExtentSize = new ol.Size(
|
||||
tileResolution * framebufferDimension,
|
||||
tileResolution * framebufferDimension);
|
||||
@@ -330,7 +221,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
|
||||
minX + framebufferExtentSize.width,
|
||||
minY + framebufferExtentSize.height);
|
||||
|
||||
this.bindFramebuffer_(frameState, framebufferDimension);
|
||||
this.bindFramebuffer(frameState, framebufferDimension);
|
||||
gl.viewport(0, 0, framebufferDimension, framebufferDimension);
|
||||
|
||||
gl.clearColor(0, 0, 0, 0);
|
||||
@@ -342,34 +233,21 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
|
||||
gl.useProgram(program);
|
||||
if (goog.isNull(this.locations_)) {
|
||||
this.locations_ = {
|
||||
aPosition: gl.getAttribLocation(program, 'aPosition'),
|
||||
aTexCoord: gl.getAttribLocation(program, 'aTexCoord'),
|
||||
uTileOffset: gl.getUniformLocation(program, 'uTileOffset'),
|
||||
uTexture: gl.getUniformLocation(program, 'uTexture')
|
||||
a_position: gl.getAttribLocation(program, 'a_position'),
|
||||
a_texCoord: gl.getAttribLocation(program, 'a_texCoord'),
|
||||
u_tileOffset: gl.getUniformLocation(program, 'u_tileOffset'),
|
||||
u_texture: gl.getUniformLocation(program, 'u_texture')
|
||||
};
|
||||
}
|
||||
|
||||
if (goog.isNull(this.arrayBuffer_)) {
|
||||
var arrayBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(goog.webgl.ARRAY_BUFFER, arrayBuffer);
|
||||
gl.bufferData(goog.webgl.ARRAY_BUFFER, new Float32Array([
|
||||
0, 0, 0, 1,
|
||||
1, 0, 1, 1,
|
||||
0, 1, 0, 0,
|
||||
1, 1, 1, 0
|
||||
]), goog.webgl.STATIC_DRAW);
|
||||
this.arrayBuffer_ = arrayBuffer;
|
||||
} else {
|
||||
gl.bindBuffer(goog.webgl.ARRAY_BUFFER, this.arrayBuffer_);
|
||||
}
|
||||
|
||||
gl.enableVertexAttribArray(this.locations_.aPosition);
|
||||
mapRenderer.bindBuffer(goog.webgl.ARRAY_BUFFER, this.arrayBuffer_);
|
||||
gl.enableVertexAttribArray(this.locations_.a_position);
|
||||
gl.vertexAttribPointer(
|
||||
this.locations_.aPosition, 2, goog.webgl.FLOAT, false, 16, 0);
|
||||
gl.enableVertexAttribArray(this.locations_.aTexCoord);
|
||||
this.locations_.a_position, 2, goog.webgl.FLOAT, false, 16, 0);
|
||||
gl.enableVertexAttribArray(this.locations_.a_texCoord);
|
||||
gl.vertexAttribPointer(
|
||||
this.locations_.aTexCoord, 2, goog.webgl.FLOAT, false, 16, 8);
|
||||
gl.uniform1i(this.locations_.uTexture, 0);
|
||||
this.locations_.a_texCoord, 2, goog.webgl.FLOAT, false, 16, 8);
|
||||
gl.uniform1i(this.locations_.u_texture, 0);
|
||||
|
||||
/**
|
||||
* @type {Object.<number, Object.<string, ol.Tile>>}
|
||||
@@ -424,7 +302,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
|
||||
/** @type {Array.<number>} */
|
||||
var zs = goog.array.map(goog.object.getKeys(tilesToDrawByZ), Number);
|
||||
goog.array.sort(zs);
|
||||
var uTileOffset = goog.vec.Vec4.createFloat32();
|
||||
var u_tileOffset = goog.vec.Vec4.createFloat32();
|
||||
goog.array.forEach(zs, function(z) {
|
||||
goog.object.forEach(tilesToDrawByZ[z], function(tile) {
|
||||
var tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord);
|
||||
@@ -434,8 +312,8 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
|
||||
framebufferExtentSize.width - 1;
|
||||
var ty = 2 * (tileExtent.minY - framebufferExtent.minY) /
|
||||
framebufferExtentSize.height - 1;
|
||||
goog.vec.Vec4.setFromValues(uTileOffset, sx, sy, tx, ty);
|
||||
gl.uniform4fv(this.locations_.uTileOffset, uTileOffset);
|
||||
goog.vec.Vec4.setFromValues(u_tileOffset, sx, sy, tx, ty);
|
||||
gl.uniform4fv(this.locations_.u_tileOffset, u_tileOffset);
|
||||
mapRenderer.bindTileTexture(tile, goog.webgl.LINEAR, goog.webgl.LINEAR);
|
||||
gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4);
|
||||
}, this);
|
||||
@@ -469,21 +347,22 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
|
||||
tileSource.useLowResolutionTiles(z, extent, tileGrid);
|
||||
this.scheduleExpireCache(frameState, tileSource);
|
||||
|
||||
goog.vec.Mat4.makeIdentity(this.texCoordMatrix_);
|
||||
goog.vec.Mat4.translate(this.texCoordMatrix_,
|
||||
var texCoordMatrix = this.texCoordMatrix;
|
||||
goog.vec.Mat4.makeIdentity(texCoordMatrix);
|
||||
goog.vec.Mat4.translate(texCoordMatrix,
|
||||
(center.x - framebufferExtent.minX) /
|
||||
(framebufferExtent.maxX - framebufferExtent.minX),
|
||||
(center.y - framebufferExtent.minY) /
|
||||
(framebufferExtent.maxY - framebufferExtent.minY),
|
||||
0);
|
||||
goog.vec.Mat4.rotateZ(this.texCoordMatrix_, view2DState.rotation);
|
||||
goog.vec.Mat4.scale(this.texCoordMatrix_,
|
||||
goog.vec.Mat4.rotateZ(texCoordMatrix, view2DState.rotation);
|
||||
goog.vec.Mat4.scale(texCoordMatrix,
|
||||
frameState.size.width * view2DState.resolution /
|
||||
(framebufferExtent.maxX - framebufferExtent.minX),
|
||||
frameState.size.height * view2DState.resolution /
|
||||
(framebufferExtent.maxY - framebufferExtent.minY),
|
||||
1);
|
||||
goog.vec.Mat4.translate(this.texCoordMatrix_,
|
||||
goog.vec.Mat4.translate(texCoordMatrix,
|
||||
-0.5,
|
||||
-0.5,
|
||||
0);
|
||||
|
||||
191
src/ol/structs/buffer.js
Normal file
191
src/ol/structs/buffer.js
Normal file
@@ -0,0 +1,191 @@
|
||||
goog.provide('ol.structs.Buffer');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.webgl');
|
||||
goog.require('ol.structs.IntegerSet');
|
||||
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
ol.structs.BufferUsage = {
|
||||
STATIC_DRAW: goog.webgl.STATIC_DRAW,
|
||||
STREAM_DRAW: goog.webgl.STREAM_DRAW,
|
||||
DYNAMIC_DRAW: goog.webgl.DYNAMIC_DRAW
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @define {boolean} Replace unused entries with NaNs.
|
||||
*/
|
||||
ol.BUFFER_REPLACE_UNUSED_ENTRIES_WITH_NANS = goog.DEBUG;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Array.<number>=} opt_arr Array.
|
||||
* @param {number=} opt_used Used.
|
||||
* @param {number=} opt_usage Usage.
|
||||
*/
|
||||
ol.structs.Buffer = function(opt_arr, opt_used, opt_usage) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.arr_ = goog.isDef(opt_arr) ? opt_arr : [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<ol.structs.IntegerSet>}
|
||||
*/
|
||||
this.dirtySets_ = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.structs.IntegerSet}
|
||||
*/
|
||||
this.freeSet_ = new ol.structs.IntegerSet();
|
||||
|
||||
var used = goog.isDef(opt_used) ? opt_used : this.arr_.length;
|
||||
if (used < this.arr_.length) {
|
||||
this.freeSet_.addRange(used, this.arr_.length);
|
||||
}
|
||||
if (ol.BUFFER_REPLACE_UNUSED_ENTRIES_WITH_NANS) {
|
||||
var arr = this.arr_;
|
||||
var n = arr.length;
|
||||
var i;
|
||||
for (i = used; i < n; ++i) {
|
||||
arr[i] = NaN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.usage_ = goog.isDef(opt_usage) ?
|
||||
opt_usage : ol.structs.BufferUsage.STATIC_DRAW;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Array.<number>} values Values.
|
||||
* @return {number} Offset.
|
||||
*/
|
||||
ol.structs.Buffer.prototype.add = function(values) {
|
||||
var size = values.length;
|
||||
goog.asserts.assert(size > 0);
|
||||
var offset = this.freeSet_.findRange(size);
|
||||
goog.asserts.assert(offset != -1); // FIXME
|
||||
this.freeSet_.removeRange(offset, offset + size);
|
||||
var i;
|
||||
for (i = 0; i < size; ++i) {
|
||||
this.arr_[offset + i] = values[i];
|
||||
}
|
||||
for (i = 0; i < this.dirtySets_.length; ++i) {
|
||||
this.dirtySets_[i].addRange(offset, offset + size);
|
||||
}
|
||||
return offset;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.structs.IntegerSet} dirtySet Dirty set.
|
||||
*/
|
||||
ol.structs.Buffer.prototype.addDirtySet = function(dirtySet) {
|
||||
goog.asserts.assert(!goog.array.contains(this.dirtySets_, dirtySet));
|
||||
this.dirtySets_.push(dirtySet);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {function(this: T, number, number)} f Callback.
|
||||
* @param {T=} opt_obj The object to be used as the value of 'this' within f.
|
||||
* @template T
|
||||
*/
|
||||
ol.structs.Buffer.prototype.forEachRange = function(f, opt_obj) {
|
||||
if (this.arr_.length !== 0) {
|
||||
this.freeSet_.forEachRangeInverted(0, this.arr_.length, f, opt_obj);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Array.<number>} Array.
|
||||
*/
|
||||
ol.structs.Buffer.prototype.getArray = function() {
|
||||
return this.arr_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} Count.
|
||||
*/
|
||||
ol.structs.Buffer.prototype.getCount = function() {
|
||||
return this.arr_.length - this.freeSet_.getSize();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {ol.structs.IntegerSet} Free set.
|
||||
*/
|
||||
ol.structs.Buffer.prototype.getFreeSet = function() {
|
||||
return this.freeSet_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} Usage.
|
||||
*/
|
||||
ol.structs.Buffer.prototype.getUsage = function() {
|
||||
return this.usage_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} size Size.
|
||||
* @param {number} offset Offset.
|
||||
*/
|
||||
ol.structs.Buffer.prototype.remove = function(size, offset) {
|
||||
var i;
|
||||
this.freeSet_.addRange(offset, offset + size);
|
||||
for (i = 0; i < this.dirtySets_.length; ++i) {
|
||||
this.dirtySets_[i].removeRange(offset, offset + size);
|
||||
}
|
||||
if (ol.BUFFER_REPLACE_UNUSED_ENTRIES_WITH_NANS) {
|
||||
var arr = this.arr_;
|
||||
for (i = 0; i < size; ++i) {
|
||||
arr[offset + i] = NaN;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.structs.IntegerSet} dirtySet Dirty set.
|
||||
*/
|
||||
ol.structs.Buffer.prototype.removeDirtySet = function(dirtySet) {
|
||||
var removed = goog.array.remove(this.dirtySets_, dirtySet);
|
||||
goog.asserts.assert(removed);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Array.<number>} values Values.
|
||||
* @param {number} offset Offset.
|
||||
*/
|
||||
ol.structs.Buffer.prototype.set = function(values, offset) {
|
||||
var arr = this.arr_;
|
||||
var n = values.length;
|
||||
goog.asserts.assert(0 <= offset && offset + n <= arr.length);
|
||||
var i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
arr[offset + i] = values[i];
|
||||
}
|
||||
for (i = 0; i < this.dirtySets_.length; ++i) {
|
||||
this.dirtySets_[i].addRange(offset, offset + n);
|
||||
}
|
||||
};
|
||||
330
src/ol/structs/integerset.js
Normal file
330
src/ol/structs/integerset.js
Normal file
@@ -0,0 +1,330 @@
|
||||
goog.provide('ol.structs.IntegerSet');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A set of integers represented as a set of integer ranges.
|
||||
* This implementation is designed for the case when the number of distinct
|
||||
* integer ranges is small.
|
||||
* @constructor
|
||||
* @param {Array.<number>=} opt_arr Array.
|
||||
*/
|
||||
ol.structs.IntegerSet = function(opt_arr) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.arr_ = goog.isDef(opt_arr) ? opt_arr : [];
|
||||
|
||||
if (goog.DEBUG) {
|
||||
this.assertValid();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} addStart Start.
|
||||
* @param {number} addStop Stop.
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.addRange = function(addStart, addStop) {
|
||||
goog.asserts.assert(addStart <= addStop);
|
||||
if (addStart == addStop) {
|
||||
return;
|
||||
}
|
||||
var arr = this.arr_;
|
||||
var n = arr.length;
|
||||
var i;
|
||||
for (i = 0; i < n; i += 2) {
|
||||
if (addStart <= arr[i]) {
|
||||
// FIXME check if splice is really needed
|
||||
arr.splice(i, 0, addStart, addStop);
|
||||
this.compactRanges_();
|
||||
return;
|
||||
}
|
||||
}
|
||||
arr.push(addStart, addStop);
|
||||
this.compactRanges_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* FIXME empty description for jsdoc
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.assertValid = function() {
|
||||
var arr = this.arr_;
|
||||
var n = arr.length;
|
||||
goog.asserts.assert(n % 2 === 0);
|
||||
var i;
|
||||
for (i = 1; i < n; ++i) {
|
||||
goog.asserts.assert(arr[i] > arr[i - 1]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* FIXME empty description for jsdoc
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.clear = function() {
|
||||
this.arr_.length = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.compactRanges_ = function() {
|
||||
var arr = this.arr_;
|
||||
var n = arr.length;
|
||||
var rangeIndex = 0;
|
||||
var lastRange = null;
|
||||
var i;
|
||||
for (i = 0; i < n; i += 2) {
|
||||
if (arr[i] == arr[i + 1]) {
|
||||
// pass
|
||||
} else if (rangeIndex > 0 &&
|
||||
arr[rangeIndex - 2] <= arr[i] &&
|
||||
arr[i] <= arr[rangeIndex - 1]) {
|
||||
arr[rangeIndex - 1] = Math.max(arr[rangeIndex - 1], arr[i + 1]);
|
||||
} else {
|
||||
arr[rangeIndex++] = arr[i];
|
||||
arr[rangeIndex++] = arr[i + 1];
|
||||
}
|
||||
}
|
||||
arr.length = rangeIndex;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Finds the start of smallest range that is at least of length minSize, or -1
|
||||
* if no such range exists.
|
||||
* @param {number} minSize Minimum size.
|
||||
* @return {number} Index.
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.findRange = function(minSize) {
|
||||
goog.asserts.assert(minSize > 0);
|
||||
var arr = this.arr_;
|
||||
var n = arr.length;
|
||||
var bestIndex = -1;
|
||||
var bestSize, i, size;
|
||||
for (i = 0; i < n; i += 2) {
|
||||
size = arr[i + 1] - arr[i];
|
||||
if (size == minSize) {
|
||||
return arr[i];
|
||||
} else if (size > minSize && (bestIndex == -1 || size < bestSize)) {
|
||||
bestIndex = arr[i];
|
||||
bestSize = size;
|
||||
}
|
||||
}
|
||||
return bestIndex;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls f with each integer range.
|
||||
* @param {function(this: T, number, number)} f Callback.
|
||||
* @param {T=} opt_obj The object to be used as the value of 'this' within f.
|
||||
* @template T
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.forEachRange = function(f, opt_obj) {
|
||||
var arr = this.arr_;
|
||||
var n = arr.length;
|
||||
var i;
|
||||
for (i = 0; i < n; i += 2) {
|
||||
f.call(opt_obj, arr[i], arr[i + 1]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls f with each integer range not in [start, stop) - 'this'.
|
||||
* @param {number} start Start.
|
||||
* @param {number} stop Stop.
|
||||
* @param {function(this: T, number, number)} f Callback.
|
||||
* @param {T=} opt_obj The object to be used as the value of 'this' within f.
|
||||
* @template T
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.forEachRangeInverted =
|
||||
function(start, stop, f, opt_obj) {
|
||||
goog.asserts.assert(start < stop);
|
||||
var arr = this.arr_;
|
||||
var n = arr.length;
|
||||
if (n === 0) {
|
||||
f.call(opt_obj, start, stop);
|
||||
} else {
|
||||
if (start < arr[0]) {
|
||||
f.call(opt_obj, start, arr[0]);
|
||||
}
|
||||
var i;
|
||||
for (i = 1; i < n - 1; i += 2) {
|
||||
f.call(opt_obj, arr[i], arr[i + 1]);
|
||||
}
|
||||
if (arr[n - 1] < stop) {
|
||||
f.call(opt_obj, arr[n - 1], stop);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Array.<number>} Array.
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.getArray = function() {
|
||||
return this.arr_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first element in the set, or -1 if the set is empty.
|
||||
* @return {number} Start.
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.getFirst = function() {
|
||||
return this.arr_.length === 0 ? -1 : this.arr_[0];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first integer after the last element in the set, or -1 if the
|
||||
* set is empty.
|
||||
* @return {number} Last.
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.getLast = function() {
|
||||
var n = this.arr_.length;
|
||||
return n === 0 ? -1 : this.arr_[n - 1];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of integers in the set.
|
||||
* @return {number} Size.
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.getSize = function() {
|
||||
var arr = this.arr_;
|
||||
var n = arr.length;
|
||||
var size = 0;
|
||||
var i;
|
||||
for (i = 0; i < n; i += 2) {
|
||||
size += arr[i + 1] - arr[i];
|
||||
}
|
||||
return size;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} start Start.
|
||||
* @param {number} stop Stop.
|
||||
* @return {boolean} Intersects range.
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.intersectsRange = function(start, stop) {
|
||||
goog.asserts.assert(start <= stop);
|
||||
if (start == stop) {
|
||||
return false;
|
||||
} else {
|
||||
var arr = this.arr_;
|
||||
var n = arr.length;
|
||||
var i = 0;
|
||||
for (i = 0; i < n; i += 2) {
|
||||
if (arr[i] <= start && start < arr[i + 1] ||
|
||||
arr[i] < stop && stop - 1 < arr[i + 1] ||
|
||||
start < arr[i] && arr[i + 1] <= stop) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Is empty.
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.isEmpty = function() {
|
||||
return this.arr_.length === 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Array.<number>} Array.
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.pack = function() {
|
||||
return this.arr_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} removeStart Start.
|
||||
* @param {number} removeStop Stop.
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.removeRange =
|
||||
function(removeStart, removeStop) {
|
||||
// FIXME this could be more efficient
|
||||
goog.asserts.assert(removeStart <= removeStop);
|
||||
var arr = this.arr_;
|
||||
var n = arr.length;
|
||||
var i;
|
||||
for (i = 0; i < n; i += 2) {
|
||||
if (removeStop < arr[i] || arr[i + 1] < removeStart) {
|
||||
continue;
|
||||
} else if (arr[i] > removeStop) {
|
||||
break;
|
||||
}
|
||||
if (removeStart < arr[i]) {
|
||||
if (removeStop == arr[i]) {
|
||||
break;
|
||||
} else if (removeStop < arr[i + 1]) {
|
||||
arr[i] = Math.max(arr[i], removeStop);
|
||||
break;
|
||||
} else {
|
||||
arr.splice(i, 2);
|
||||
i -= 2;
|
||||
n -= 2;
|
||||
}
|
||||
} else if (removeStart == arr[i]) {
|
||||
if (removeStop < arr[i + 1]) {
|
||||
arr[i] = removeStop;
|
||||
break;
|
||||
} else if (removeStop == arr[i + 1]) {
|
||||
arr.splice(i, 2);
|
||||
break;
|
||||
} else {
|
||||
arr.splice(i, 2);
|
||||
i -= 2;
|
||||
n -= 2;
|
||||
}
|
||||
} else {
|
||||
if (removeStop < arr[i + 1]) {
|
||||
arr.splice(i, 2, arr[i], removeStart, removeStop, arr[i + 1]);
|
||||
break;
|
||||
} else if (removeStop == arr[i + 1]) {
|
||||
arr[i + 1] = removeStart;
|
||||
break;
|
||||
} else {
|
||||
arr[i + 1] = removeStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.compactRanges_();
|
||||
};
|
||||
|
||||
|
||||
if (goog.DEBUG) {
|
||||
|
||||
/**
|
||||
* @return {string} String.
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.toString = function() {
|
||||
var arr = this.arr_;
|
||||
var n = arr.length;
|
||||
var result = new Array(n / 2);
|
||||
var resultIndex = 0;
|
||||
var i;
|
||||
for (i = 0; i < n; i += 2) {
|
||||
result[resultIndex++] = arr[i] + '-' + arr[i + 1];
|
||||
}
|
||||
return result.join(', ');
|
||||
};
|
||||
|
||||
}
|
||||
@@ -522,7 +522,8 @@
|
||||
if (equal) {
|
||||
var j = 0;
|
||||
for (j = 0; j < other.length; ++j) {
|
||||
if (this.obj[j] !== other[j]) {
|
||||
if (this.obj[j] !== other[j] &&
|
||||
!(isNaN(this.obj[j]) && isNaN(other[j]))) {
|
||||
equal = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,46 @@ describe('ol.math.csch', function() {
|
||||
});
|
||||
|
||||
|
||||
describe('ol.math.roundUpToPowerOfTwo', function() {
|
||||
|
||||
it('raises an exception when x is negative', function() {
|
||||
expect(function() {
|
||||
ol.math.roundUpToPowerOfTwo(-1);
|
||||
}).to.throwException();
|
||||
});
|
||||
|
||||
it('raises an exception when x is zero', function() {
|
||||
expect(function() {
|
||||
ol.math.roundUpToPowerOfTwo(0);
|
||||
}).to.throwException();
|
||||
});
|
||||
|
||||
it('returns the expected value for simple powers of two', function() {
|
||||
expect(ol.math.roundUpToPowerOfTwo(1)).to.be(1);
|
||||
expect(ol.math.roundUpToPowerOfTwo(2)).to.be(2);
|
||||
expect(ol.math.roundUpToPowerOfTwo(4)).to.be(4);
|
||||
expect(ol.math.roundUpToPowerOfTwo(8)).to.be(8);
|
||||
expect(ol.math.roundUpToPowerOfTwo(16)).to.be(16);
|
||||
expect(ol.math.roundUpToPowerOfTwo(32)).to.be(32);
|
||||
expect(ol.math.roundUpToPowerOfTwo(64)).to.be(64);
|
||||
expect(ol.math.roundUpToPowerOfTwo(128)).to.be(128);
|
||||
expect(ol.math.roundUpToPowerOfTwo(256)).to.be(256);
|
||||
});
|
||||
|
||||
it('returns the expected value for simple powers of ten', function() {
|
||||
expect(ol.math.roundUpToPowerOfTwo(1)).to.be(1);
|
||||
expect(ol.math.roundUpToPowerOfTwo(10)).to.be(16);
|
||||
expect(ol.math.roundUpToPowerOfTwo(100)).to.be(128);
|
||||
expect(ol.math.roundUpToPowerOfTwo(1000)).to.be(1024);
|
||||
expect(ol.math.roundUpToPowerOfTwo(10000)).to.be(16384);
|
||||
expect(ol.math.roundUpToPowerOfTwo(100000)).to.be(131072);
|
||||
expect(ol.math.roundUpToPowerOfTwo(1000000)).to.be(1048576);
|
||||
expect(ol.math.roundUpToPowerOfTwo(10000000)).to.be(16777216);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('ol.math.sech', function() {
|
||||
|
||||
it('returns the correct value at -Infinity', function() {
|
||||
|
||||
262
test/spec/ol/structs/buffer.test.js
Normal file
262
test/spec/ol/structs/buffer.test.js
Normal file
@@ -0,0 +1,262 @@
|
||||
goog.provide('ol.test.structs.Buffer');
|
||||
|
||||
|
||||
describe('ol.structs.Buffer', function() {
|
||||
|
||||
describe('constructor', function() {
|
||||
|
||||
describe('without an argument', function() {
|
||||
|
||||
var b;
|
||||
beforeEach(function() {
|
||||
b = new ol.structs.Buffer();
|
||||
});
|
||||
|
||||
it('constructs an empty instance', function() {
|
||||
expect(b.getArray()).to.be.empty();
|
||||
expect(b.getCount()).to.be(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with a single array argument', function() {
|
||||
|
||||
var b;
|
||||
beforeEach(function() {
|
||||
b = new ol.structs.Buffer([0, 1, 2, 3]);
|
||||
});
|
||||
|
||||
it('constructs a populated instance', function() {
|
||||
expect(b.getArray()).to.equalArray([0, 1, 2, 3]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with an empty instance', function() {
|
||||
|
||||
var b;
|
||||
beforeEach(function() {
|
||||
b = new ol.structs.Buffer();
|
||||
});
|
||||
|
||||
describe('forEachRange', function() {
|
||||
|
||||
it('does not call the callback', function() {
|
||||
var callback = sinon.spy();
|
||||
b.forEachRange(callback);
|
||||
expect(callback).not.to.be.called();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getArray', function() {
|
||||
|
||||
it('returns an empty array', function() {
|
||||
expect(b.getArray()).to.be.empty();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getCount', function() {
|
||||
|
||||
it('returns 0', function() {
|
||||
expect(b.getCount()).to.be(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with an empty instance with spare capacity', function() {
|
||||
|
||||
var b;
|
||||
beforeEach(function() {
|
||||
b = new ol.structs.Buffer(new Array(4), 0);
|
||||
});
|
||||
|
||||
describe('add', function() {
|
||||
|
||||
it('allows elements to be added', function() {
|
||||
expect(b.add([0, 1, 2, 3])).to.be(0);
|
||||
expect(b.getArray()).to.equalArray([0, 1, 2, 3]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('forEachRange', function() {
|
||||
|
||||
it('does not call the callback', function() {
|
||||
var callback = sinon.spy();
|
||||
b.forEachRange(callback);
|
||||
expect(callback).not.to.be.called();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getCount', function() {
|
||||
|
||||
it('returns 0', function() {
|
||||
expect(b.getCount()).to.be(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with an instance with no spare capacity', function() {
|
||||
|
||||
var b;
|
||||
beforeEach(function() {
|
||||
b = new ol.structs.Buffer([0, 1, 2, 3]);
|
||||
});
|
||||
|
||||
describe('add', function() {
|
||||
|
||||
it('throws an exception', function() {
|
||||
expect(function() {
|
||||
b.add([4, 5]);
|
||||
}).to.throwException();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('forEachRange', function() {
|
||||
|
||||
it('calls the callback', function() {
|
||||
var callback = sinon.spy();
|
||||
b.forEachRange(callback);
|
||||
expect(callback.calledOnce).to.be(true);
|
||||
expect(callback.args[0]).to.equalArray([0, 4]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getCount', function() {
|
||||
|
||||
it('returns the expected value', function() {
|
||||
expect(b.getCount()).to.be(4);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('remove', function() {
|
||||
|
||||
it('allows items to be removes', function() {
|
||||
expect(function() {
|
||||
b.remove(4, 2);
|
||||
}).to.not.throwException();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('set', function() {
|
||||
|
||||
it('updates the items', function() {
|
||||
b.set([5, 6], 2);
|
||||
expect(b.getArray()).to.equalArray([0, 1, 5, 6]);
|
||||
});
|
||||
|
||||
it('marks the set items as dirty', function() {
|
||||
var dirtySet = new ol.structs.IntegerSet();
|
||||
b.addDirtySet(dirtySet);
|
||||
expect(dirtySet.isEmpty()).to.be(true);
|
||||
b.set([5, 6], 2);
|
||||
expect(dirtySet.isEmpty()).to.be(false);
|
||||
expect(dirtySet.getArray()).to.equalArray([2, 4]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with an instance with spare capacity', function() {
|
||||
|
||||
var b;
|
||||
beforeEach(function() {
|
||||
var arr = [0, 1, 2, 3];
|
||||
arr.length = 8;
|
||||
b = new ol.structs.Buffer(arr, 4);
|
||||
});
|
||||
|
||||
describe('add', function() {
|
||||
|
||||
it('allows more items to be added', function() {
|
||||
expect(b.add([4, 5, 6, 7])).to.be(4);
|
||||
expect(b.getArray()).to.equalArray([0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('forEachRange', function() {
|
||||
|
||||
it('calls the callback with the expected values', function() {
|
||||
var callback = sinon.spy();
|
||||
b.forEachRange(callback);
|
||||
expect(callback.calledOnce).to.be(true);
|
||||
expect(callback.args[0]).to.equalArray([0, 4]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getCount', function() {
|
||||
|
||||
it('returns the expected value', function() {
|
||||
expect(b.getCount()).to.be(4);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getFreeSet', function() {
|
||||
|
||||
it('returns the expected set', function() {
|
||||
var freeSet = b.getFreeSet();
|
||||
expect(freeSet.isEmpty()).to.be(false);
|
||||
expect(freeSet.getArray()).to.equalArray([4, 8]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('usage tests', function() {
|
||||
|
||||
it('allows multiple adds and removes', function() {
|
||||
var b = new ol.structs.Buffer(new Array(8), 0);
|
||||
expect(b.add([0, 1])).to.be(0);
|
||||
expect(b.getArray()).to.equalArray([0, 1, NaN, NaN, NaN, NaN, NaN, NaN]);
|
||||
expect(b.getCount()).to.be(2);
|
||||
expect(b.add([2, 3, 4, 5])).to.be(2);
|
||||
expect(b.getArray()).to.equalArray([0, 1, 2, 3, 4, 5, NaN, NaN]);
|
||||
expect(b.getCount()).to.be(6);
|
||||
expect(b.add([6, 7])).to.be(6);
|
||||
expect(b.getArray()).to.equalArray([0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
expect(b.getCount()).to.be(8);
|
||||
b.remove(2, 2);
|
||||
expect(b.getArray()).to.equalArray([0, 1, NaN, NaN, 4, 5, 6, 7]);
|
||||
expect(b.getCount()).to.be(6);
|
||||
expect(b.add([8, 9])).to.be(2);
|
||||
expect(b.getArray()).to.equalArray([0, 1, 8, 9, 4, 5, 6, 7]);
|
||||
expect(b.getCount()).to.be(8);
|
||||
b.remove(1, 1);
|
||||
expect(b.getArray()).to.equalArray([0, NaN, 8, 9, 4, 5, 6, 7]);
|
||||
expect(b.getCount()).to.be(7);
|
||||
b.remove(4, 4);
|
||||
expect(b.getArray()).to.equalArray([0, NaN, 8, 9, NaN, NaN, NaN, NaN]);
|
||||
expect(b.getCount()).to.be(3);
|
||||
expect(b.add([10, 11, 12])).to.be(4);
|
||||
expect(b.getArray()).to.equalArray([0, NaN, 8, 9, 10, 11, 12, NaN]);
|
||||
expect(b.getCount()).to.be(6);
|
||||
expect(b.add([13])).to.be(1);
|
||||
expect(b.getArray()).to.equalArray([0, 13, 8, 9, 10, 11, 12, NaN]);
|
||||
expect(b.getCount()).to.be(7);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
goog.require('ol.structs.Buffer');
|
||||
goog.require('ol.structs.IntegerSet');
|
||||
619
test/spec/ol/structs/integerset.test.js
Normal file
619
test/spec/ol/structs/integerset.test.js
Normal file
@@ -0,0 +1,619 @@
|
||||
goog.provide('ol.test.structs.IntegerSet');
|
||||
|
||||
|
||||
describe('ol.structs.IntegerSet', function() {
|
||||
|
||||
describe('constructor', function() {
|
||||
|
||||
describe('without an argument', function() {
|
||||
|
||||
it('constructs an empty instance', function() {
|
||||
var is = new ol.structs.IntegerSet();
|
||||
expect(is).to.be.an(ol.structs.IntegerSet);
|
||||
expect(is.getArray()).to.be.empty();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with an argument', function() {
|
||||
|
||||
it('constructs with a valid array', function() {
|
||||
var is = new ol.structs.IntegerSet([0, 2, 4, 6]);
|
||||
expect(is).to.be.an(ol.structs.IntegerSet);
|
||||
expect(is.getArray()).to.equalArray([0, 2, 4, 6]);
|
||||
});
|
||||
|
||||
it('throws an exception with an odd number of elements', function() {
|
||||
expect(function() {
|
||||
var is = new ol.structs.IntegerSet([0, 2, 4]);
|
||||
}).to.throwException();
|
||||
});
|
||||
|
||||
it('throws an exception with out-of-order elements', function() {
|
||||
expect(function() {
|
||||
var is = new ol.structs.IntegerSet([0, 2, 2, 4]);
|
||||
}).to.throwException();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with an empty instance', function() {
|
||||
|
||||
var is;
|
||||
beforeEach(function() {
|
||||
is = new ol.structs.IntegerSet();
|
||||
});
|
||||
|
||||
describe('addRange', function() {
|
||||
|
||||
it('creates a new element', function() {
|
||||
is.addRange(0, 2);
|
||||
expect(is.getArray()).to.equalArray([0, 2]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('findRange', function() {
|
||||
|
||||
it('returns -1', function() {
|
||||
expect(is.findRange(2)).to.be(-1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('forEachRange', function() {
|
||||
|
||||
it('does not call the callback', function() {
|
||||
var callback = sinon.spy();
|
||||
is.forEachRange(callback);
|
||||
expect(callback).to.not.be.called();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('forEachRangeInverted', function() {
|
||||
|
||||
it('does call the callback', function() {
|
||||
var callback = sinon.spy();
|
||||
is.forEachRangeInverted(0, 8, callback);
|
||||
expect(callback.calledOnce).to.be(true);
|
||||
expect(callback.args[0]).to.equalArray([0, 8]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getFirst', function() {
|
||||
|
||||
it('returns -1', function() {
|
||||
expect(is.getFirst()).to.be(-1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getLast', function() {
|
||||
|
||||
it('returns -1', function() {
|
||||
expect(is.getLast()).to.be(-1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getSize', function() {
|
||||
|
||||
it('returns 0', function() {
|
||||
expect(is.getSize()).to.be(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('intersectsRange', function() {
|
||||
|
||||
it('returns false', function() {
|
||||
expect(is.intersectsRange(0, 0)).to.be(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('isEmpty', function() {
|
||||
|
||||
it('returns true', function() {
|
||||
expect(is.isEmpty()).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('toString', function() {
|
||||
|
||||
it('returns an empty string', function() {
|
||||
expect(is.toString()).to.be.empty();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with a populated instance', function() {
|
||||
|
||||
var is;
|
||||
beforeEach(function() {
|
||||
is = new ol.structs.IntegerSet([4, 6, 8, 10, 12, 14]);
|
||||
});
|
||||
|
||||
describe('addRange', function() {
|
||||
|
||||
it('inserts before the first element', function() {
|
||||
is.addRange(0, 2);
|
||||
expect(is.getArray()).to.equalArray([0, 2, 4, 6, 8, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('extends the first element to the left', function() {
|
||||
is.addRange(0, 4);
|
||||
expect(is.getArray()).to.equalArray([0, 6, 8, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('extends the first element to the right', function() {
|
||||
is.addRange(6, 7);
|
||||
expect(is.getArray()).to.equalArray([4, 7, 8, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('merges the first two elements', function() {
|
||||
is.addRange(6, 8);
|
||||
expect(is.getArray()).to.equalArray([4, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('extends middle elements to the left', function() {
|
||||
is.addRange(7, 8);
|
||||
expect(is.getArray()).to.equalArray([4, 6, 7, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('extends middle elements to the right', function() {
|
||||
is.addRange(10, 11);
|
||||
expect(is.getArray()).to.equalArray([4, 6, 8, 11, 12, 14]);
|
||||
});
|
||||
|
||||
it('merges the last two elements', function() {
|
||||
is.addRange(10, 12);
|
||||
expect(is.getArray()).to.equalArray([4, 6, 8, 14]);
|
||||
});
|
||||
|
||||
it('extends the last element to the left', function() {
|
||||
is.addRange(11, 12);
|
||||
expect(is.getArray()).to.equalArray([4, 6, 8, 10, 11, 14]);
|
||||
});
|
||||
|
||||
it('extends the last element to the right', function() {
|
||||
is.addRange(14, 15);
|
||||
expect(is.getArray()).to.equalArray([4, 6, 8, 10, 12, 15]);
|
||||
});
|
||||
|
||||
it('inserts after the last element', function() {
|
||||
is.addRange(16, 18);
|
||||
expect(is.getArray()).to.equalArray([4, 6, 8, 10, 12, 14, 16, 18]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('clear', function() {
|
||||
|
||||
it('clears the instance', function() {
|
||||
is.clear();
|
||||
expect(is.getArray()).to.be.empty();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('findRange', function() {
|
||||
|
||||
it('throws an exception when passed a negative size', function() {
|
||||
expect(function() {
|
||||
is.findRange(-1);
|
||||
}).to.throwException();
|
||||
});
|
||||
|
||||
it('throws an exception when passed a zero size', function() {
|
||||
expect(function() {
|
||||
is.findRange(0);
|
||||
}).to.throwException();
|
||||
});
|
||||
|
||||
it('finds the first range of size 1', function() {
|
||||
expect(is.findRange(1)).to.be(4);
|
||||
});
|
||||
|
||||
it('finds the first range of size 2', function() {
|
||||
expect(is.findRange(2)).to.be(4);
|
||||
});
|
||||
|
||||
it('returns -1 when no range can be found', function() {
|
||||
expect(is.findRange(3)).to.be(-1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('forEachRange', function() {
|
||||
|
||||
it('calls the callback', function() {
|
||||
var callback = sinon.spy();
|
||||
is.forEachRange(callback);
|
||||
expect(callback).to.be.called();
|
||||
expect(callback.calledThrice).to.be(true);
|
||||
expect(callback.args[0]).to.equalArray([4, 6]);
|
||||
expect(callback.args[1]).to.equalArray([8, 10]);
|
||||
expect(callback.args[2]).to.equalArray([12, 14]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('forEachRangeInverted', function() {
|
||||
|
||||
it('does call the callback', function() {
|
||||
var callback = sinon.spy();
|
||||
is.forEachRangeInverted(0, 16, callback);
|
||||
expect(callback.callCount).to.be(4);
|
||||
expect(callback.args[0]).to.equalArray([0, 4]);
|
||||
expect(callback.args[1]).to.equalArray([6, 8]);
|
||||
expect(callback.args[2]).to.equalArray([10, 12]);
|
||||
expect(callback.args[3]).to.equalArray([14, 16]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('getFirst', function() {
|
||||
|
||||
it('returns the expected value', function() {
|
||||
expect(is.getFirst()).to.be(4);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getLast', function() {
|
||||
|
||||
it('returns the expected value', function() {
|
||||
expect(is.getLast()).to.be(14);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getSize', function() {
|
||||
|
||||
it('returns the expected value', function() {
|
||||
expect(is.getSize()).to.be(6);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('intersectsRange', function() {
|
||||
|
||||
it('returns the expected value for small ranges', function() {
|
||||
expect(is.intersectsRange(1, 3)).to.be(false);
|
||||
expect(is.intersectsRange(2, 4)).to.be(false);
|
||||
expect(is.intersectsRange(3, 5)).to.be(true);
|
||||
expect(is.intersectsRange(4, 6)).to.be(true);
|
||||
expect(is.intersectsRange(5, 7)).to.be(true);
|
||||
expect(is.intersectsRange(6, 8)).to.be(false);
|
||||
expect(is.intersectsRange(7, 9)).to.be(true);
|
||||
expect(is.intersectsRange(8, 10)).to.be(true);
|
||||
expect(is.intersectsRange(9, 11)).to.be(true);
|
||||
expect(is.intersectsRange(10, 12)).to.be(false);
|
||||
expect(is.intersectsRange(11, 13)).to.be(true);
|
||||
expect(is.intersectsRange(12, 14)).to.be(true);
|
||||
expect(is.intersectsRange(13, 15)).to.be(true);
|
||||
expect(is.intersectsRange(14, 16)).to.be(false);
|
||||
expect(is.intersectsRange(15, 17)).to.be(false);
|
||||
});
|
||||
|
||||
it('returns the expected value for large ranges', function() {
|
||||
expect(is.intersectsRange(-3, 1)).to.be(false);
|
||||
expect(is.intersectsRange(1, 5)).to.be(true);
|
||||
expect(is.intersectsRange(1, 9)).to.be(true);
|
||||
expect(is.intersectsRange(1, 13)).to.be(true);
|
||||
expect(is.intersectsRange(1, 17)).to.be(true);
|
||||
expect(is.intersectsRange(5, 9)).to.be(true);
|
||||
expect(is.intersectsRange(5, 13)).to.be(true);
|
||||
expect(is.intersectsRange(5, 17)).to.be(true);
|
||||
expect(is.intersectsRange(9, 13)).to.be(true);
|
||||
expect(is.intersectsRange(9, 17)).to.be(true);
|
||||
expect(is.intersectsRange(13, 17)).to.be(true);
|
||||
expect(is.intersectsRange(17, 21)).to.be(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('isEmpty', function() {
|
||||
|
||||
it('returns false', function() {
|
||||
expect(is.isEmpty()).to.be(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('removeRange', function() {
|
||||
|
||||
it('removes the first part of the first element', function() {
|
||||
is.removeRange(4, 5);
|
||||
expect(is.getArray()).to.equalArray([5, 6, 8, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('removes the last part of the first element', function() {
|
||||
is.removeRange(5, 6);
|
||||
expect(is.getArray()).to.equalArray([4, 5, 8, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('removes the first element', function() {
|
||||
is.removeRange(4, 6);
|
||||
expect(is.getArray()).to.equalArray([8, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('removes the first part of a middle element', function() {
|
||||
is.removeRange(8, 9);
|
||||
expect(is.getArray()).to.equalArray([4, 6, 9, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('removes the last part of a middle element', function() {
|
||||
is.removeRange(9, 10);
|
||||
expect(is.getArray()).to.equalArray([4, 6, 8, 9, 12, 14]);
|
||||
});
|
||||
|
||||
it('removes a middle element', function() {
|
||||
is.removeRange(8, 10);
|
||||
expect(is.getArray()).to.equalArray([4, 6, 12, 14]);
|
||||
});
|
||||
|
||||
it('removes the first part of the last element', function() {
|
||||
is.removeRange(12, 13);
|
||||
expect(is.getArray()).to.equalArray([4, 6, 8, 10, 13, 14]);
|
||||
});
|
||||
|
||||
it('removes the last part of the last element', function() {
|
||||
is.removeRange(13, 14);
|
||||
expect(is.getArray()).to.equalArray([4, 6, 8, 10, 12, 13]);
|
||||
});
|
||||
|
||||
it('removes the last element', function() {
|
||||
is.removeRange(12, 14);
|
||||
expect(is.getArray()).to.equalArray([4, 6, 8, 10]);
|
||||
});
|
||||
|
||||
it('can remove multiple ranges near the start', function() {
|
||||
is.removeRange(3, 11);
|
||||
expect(is.getArray()).to.equalArray([12, 14]);
|
||||
});
|
||||
|
||||
it('can remove multiple ranges near the start', function() {
|
||||
is.removeRange(7, 15);
|
||||
expect(is.getArray()).to.equalArray([4, 6]);
|
||||
});
|
||||
|
||||
it('throws an exception when passed an invalid range', function() {
|
||||
expect(function() {
|
||||
is.removeRange(2, 0);
|
||||
}).to.throwException();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('toString', function() {
|
||||
|
||||
it('returns the expected value', function() {
|
||||
expect(is.toString()).to.be('4-6, 8-10, 12-14');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with fragmentation', function() {
|
||||
|
||||
var is;
|
||||
beforeEach(function() {
|
||||
is = new ol.structs.IntegerSet([0, 1, 2, 4, 5, 8, 9, 12, 13, 15, 16, 17]);
|
||||
});
|
||||
|
||||
describe('findRange', function() {
|
||||
|
||||
it('finds the first range of size 1', function() {
|
||||
expect(is.findRange(1)).to.be(0);
|
||||
});
|
||||
|
||||
it('finds the first range of size 2', function() {
|
||||
expect(is.findRange(2)).to.be(2);
|
||||
});
|
||||
|
||||
it('finds the first range of size 3', function() {
|
||||
expect(is.findRange(3)).to.be(5);
|
||||
});
|
||||
|
||||
it('returns -1 when no range can be found', function() {
|
||||
expect(is.findRange(4)).to.be(-1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getFirst', function() {
|
||||
|
||||
it('returns the expected value', function() {
|
||||
expect(is.getFirst()).to.be(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getLast', function() {
|
||||
|
||||
it('returns the expected value', function() {
|
||||
expect(is.getLast()).to.be(17);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getSize', function() {
|
||||
|
||||
it('returns the expected value', function() {
|
||||
expect(is.getSize()).to.be(12);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('removeRange', function() {
|
||||
|
||||
it('removing an empty range has no effect', function() {
|
||||
is.removeRange(0, 0);
|
||||
expect(is.getArray()).to.equalArray(
|
||||
[0, 1, 2, 4, 5, 8, 9, 12, 13, 15, 16, 17]);
|
||||
});
|
||||
|
||||
it('can remove elements from the middle of range', function() {
|
||||
is.removeRange(6, 7);
|
||||
expect(is.getArray()).to.equalArray(
|
||||
[0, 1, 2, 4, 5, 6, 7, 8, 9, 12, 13, 15, 16, 17]);
|
||||
});
|
||||
|
||||
it('can remove multiple ranges', function() {
|
||||
is.removeRange(2, 12);
|
||||
expect(is.getArray()).to.equalArray([0, 1, 13, 15, 16, 17]);
|
||||
});
|
||||
|
||||
it('can remove multiple ranges and reduce others', function() {
|
||||
is.removeRange(0, 10);
|
||||
expect(is.getArray()).to.equalArray([10, 12, 13, 15, 16, 17]);
|
||||
});
|
||||
|
||||
it('can remove all ranges', function() {
|
||||
is.removeRange(0, 18);
|
||||
expect(is.getArray()).to.equalArray([]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('toString', function() {
|
||||
|
||||
it('returns the expected value', function() {
|
||||
expect(is.toString()).to.be('0-1, 2-4, 5-8, 9-12, 13-15, 16-17');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('compared to a slow reference implementation', function() {
|
||||
|
||||
var SimpleIntegerSet = function() {
|
||||
this.integers_ = {};
|
||||
};
|
||||
|
||||
SimpleIntegerSet.prototype.addRange = function(addStart, addStop) {
|
||||
var i;
|
||||
for (i = addStart; i < addStop; ++i) {
|
||||
this.integers_[i.toString()] = true;
|
||||
}
|
||||
};
|
||||
|
||||
SimpleIntegerSet.prototype.clear = function() {
|
||||
this.integers_ = {};
|
||||
};
|
||||
|
||||
SimpleIntegerSet.prototype.getArray = function() {
|
||||
var integers = goog.array.map(
|
||||
goog.object.getKeys(this.integers_), Number);
|
||||
goog.array.sort(integers);
|
||||
var arr = [];
|
||||
var start = -1, stop;
|
||||
var i;
|
||||
for (i = 0; i < integers.length; ++i) {
|
||||
if (start == -1) {
|
||||
start = stop = integers[i];
|
||||
} else if (integers[i] == stop + 1) {
|
||||
++stop;
|
||||
} else {
|
||||
arr.push(start, stop + 1);
|
||||
start = stop = integers[i];
|
||||
}
|
||||
}
|
||||
if (start != -1) {
|
||||
arr.push(start, stop + 1);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
SimpleIntegerSet.prototype.removeRange = function(removeStart, removeStop) {
|
||||
var i;
|
||||
for (i = removeStart; i < removeStop; ++i) {
|
||||
delete this.integers_[i.toString()];
|
||||
}
|
||||
};
|
||||
|
||||
var is, sis;
|
||||
beforeEach(function() {
|
||||
is = new ol.structs.IntegerSet();
|
||||
sis = new SimpleIntegerSet();
|
||||
});
|
||||
|
||||
it('behaves identically with random adds', function() {
|
||||
var addStart, addStop, i;
|
||||
for (i = 0; i < 64; ++i) {
|
||||
addStart = goog.math.randomInt(128);
|
||||
addStop = addStart + goog.math.randomInt(16);
|
||||
is.addRange(addStart, addStop);
|
||||
sis.addRange(addStart, addStop);
|
||||
expect(is.getArray()).to.equalArray(sis.getArray());
|
||||
}
|
||||
});
|
||||
|
||||
it('behaves identically with random removes', function() {
|
||||
is.addRange(0, 128);
|
||||
sis.addRange(0, 128);
|
||||
var i, removeStart, removeStop;
|
||||
for (i = 0; i < 64; ++i) {
|
||||
removeStart = goog.math.randomInt(128);
|
||||
removeStop = removeStart + goog.math.randomInt(16);
|
||||
is.removeRange(removeStart, removeStop);
|
||||
sis.removeRange(removeStart, removeStop);
|
||||
expect(is.getArray()).to.equalArray(sis.getArray());
|
||||
}
|
||||
});
|
||||
|
||||
it('behaves identically with random adds and removes', function() {
|
||||
var i, start, stop;
|
||||
for (i = 0; i < 64; ++i) {
|
||||
start = goog.math.randomInt(128);
|
||||
stop = start + goog.math.randomInt(16);
|
||||
if (Math.random() < 0.5) {
|
||||
is.addRange(start, stop);
|
||||
sis.addRange(start, stop);
|
||||
} else {
|
||||
is.removeRange(start, stop);
|
||||
sis.removeRange(start, stop);
|
||||
}
|
||||
expect(is.getArray()).to.equalArray(sis.getArray());
|
||||
}
|
||||
});
|
||||
|
||||
it('behaves identically with random adds, removes, and clears', function() {
|
||||
var i, p, start, stop;
|
||||
for (i = 0; i < 64; ++i) {
|
||||
start = goog.math.randomInt(128);
|
||||
stop = start + goog.math.randomInt(16);
|
||||
p = Math.random();
|
||||
if (p < 0.45) {
|
||||
is.addRange(start, stop);
|
||||
sis.addRange(start, stop);
|
||||
} else if (p < 0.9) {
|
||||
is.removeRange(start, stop);
|
||||
sis.removeRange(start, stop);
|
||||
} else {
|
||||
is.clear();
|
||||
sis.clear();
|
||||
}
|
||||
expect(is.getArray()).to.equalArray(sis.getArray());
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.object');
|
||||
goog.require('ol.structs.IntegerSet');
|
||||
Reference in New Issue
Block a user