diff --git a/examples/ten-thousand-points.html b/examples/ten-thousand-points.html
new file mode 100644
index 0000000000..b7953bcb89
--- /dev/null
+++ b/examples/ten-thousand-points.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+ Ten thousand points example
+
+
+
+
+
+
+
+
+
+
+
+
+
Ten thousand points example
+
Example of a map with ten thousand points.
+
+
points, vector, openstreetmap
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/ten-thousand-points.js b/examples/ten-thousand-points.js
new file mode 100644
index 0000000000..87552e26e0
--- /dev/null
+++ b/examples/ten-thousand-points.js
@@ -0,0 +1,68 @@
+goog.require('ol.Map');
+goog.require('ol.RendererHint');
+goog.require('ol.View2D');
+goog.require('ol.control.MousePosition');
+goog.require('ol.control.defaults');
+goog.require('ol.geom2.LineStringCollection');
+goog.require('ol.geom2.PointCollection');
+goog.require('ol.layer.TileLayer');
+goog.require('ol.layer.VectorLayer2');
+goog.require('ol.source.OSM');
+goog.require('ol.source.VectorSource2');
+
+
+// WARNING
+// This example is an experimental testbed for WebGL vector work. The function
+// calls used here are internal and low-level and are not representative of the
+// final API.
+
+
+var pointCollection = ol.geom2.PointCollection.createEmpty(101 * 101);
+var i, j, x, y;
+for (i = 0; i < 101; ++i) {
+ for (j = 0; j < 101; ++j) {
+ x = 20000000 * (i - 50) / 50;
+ y = 20000000 * (j - 50) / 50;
+ pointCollection.add([x, y]);
+ }
+}
+
+var k = 1000000;
+var lineStringCollection = ol.geom2.LineStringCollection.pack([
+ [[-20 * k, -20 * k], [20 * k, 20 * k]],
+ [[-20 * k, 20 * k], [20 * k, -20 * k]],
+ [[0 * k, 15 * k],
+ [10 * k, 5 * k],
+ [5 * k, 5 * k],
+ [5 * k, -15 * k],
+ [-5 * k, -15 * k],
+ [-5 * k, 5 * k],
+ [-10 * k, 5 * k],
+ [0 * k, 15 * k]]
+]);
+
+var map = new ol.Map({
+ controls: ol.control.defaults({}, [
+ new ol.control.MousePosition({
+ undefinedHTML: ' '
+ })
+ ]),
+ layers: [
+ new ol.layer.TileLayer({
+ source: new ol.source.OSM()
+ }),
+ new ol.layer.VectorLayer2({
+ source: new ol.source.VectorSource2({
+ lineStringCollections: [lineStringCollection],
+ projection: 'EPSG:3857',
+ pointCollections: [pointCollection]
+ })
+ })
+ ],
+ renderer: ol.RendererHint.WEBGL,
+ target: 'map',
+ view: new ol.View2D({
+ center: [0, 0],
+ zoom: 0
+ })
+});
diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc
index f5be363af5..e2e1245013 100644
--- a/src/objectliterals.jsdoc
+++ b/src/objectliterals.jsdoc
@@ -486,6 +486,17 @@
* of `url` when the WMS supports multiple urls for GetMap requests.
*/
+/**
+ * @typedef {Object} ol.source.VectorSource2Options
+ * @property {Array.|undefined} attributions Attributions.
+ * @property {ol.Extent|undefined} extent Extent.
+ * @property {Array.|undefined}
+ * lineStringCollections Line string collections.
+ * @property {Array.|undefined} pointCollections
+ * Point collections.
+ * @property {ol.ProjectionLike} projection Projection.
+ */
+
/**
* @typedef {Object} ol.source.WMTSOptions
* @property {Array.|undefined} attributions Attributions.
diff --git a/src/ol/geom2/geom2.js b/src/ol/geom2/geom2.js
new file mode 100644
index 0000000000..a2ce5de71d
--- /dev/null
+++ b/src/ol/geom2/geom2.js
@@ -0,0 +1,71 @@
+goog.provide('ol.geom2');
+
+goog.require('goog.asserts');
+goog.require('ol.Extent');
+
+
+/**
+ * @param {ol.structs.Buffer} buf Buffer.
+ * @param {number} dim Dimension.
+ * @return {ol.Extent} Extent.
+ */
+ol.geom2.getExtent = function(buf, dim) {
+ var extent = new Array(2 * dim);
+ var extentIndex = 0;
+ var i;
+ for (i = 0; i < dim; ++i) {
+ extent[extentIndex++] = Infinity;
+ extent[extentIndex++] = -Infinity;
+ }
+ var bufArr = buf.getArray();
+ buf.forEachRange(function(start, stop) {
+ var extentIndex, i, j;
+ for (i = start; i < stop; i += dim) {
+ extentIndex = 0;
+ for (j = 0; j < dim; ++j) {
+ extent[extentIndex++] = Math.min(extent[2 * j], bufArr[i + j]);
+ extent[extentIndex++] = Math.max(extent[2 * j + 1], bufArr[i + j]);
+ }
+ }
+ });
+ return extent;
+};
+
+
+/**
+ * @param {Array.} arr Array.
+ * @param {number} offset Offset.
+ * @param {Array.>} unpackedPoints Unpacked points.
+ * @param {number} dim Dimension.
+ * @return {number} Offset.
+ */
+ol.geom2.packPoints = function(arr, offset, unpackedPoints, dim) {
+ var n = unpackedPoints.length;
+ var i, j, point;
+ for (i = 0; i < n; ++i) {
+ point = unpackedPoints[i];
+ goog.asserts.assert(point.length == dim);
+ for (j = 0; j < dim; ++j) {
+ arr[offset++] = point[j];
+ }
+ }
+ return offset;
+};
+
+
+/**
+ * @param {Array.} arr Array.
+ * @param {number} offset Offset.
+ * @param {number} end End.
+ * @param {number} dim Dimension.
+ * @return {Array.>} Unpacked points.
+ */
+ol.geom2.unpackPoints = function(arr, offset, end, dim) {
+ var unpackedPoints = new Array((end - offset) / dim);
+ var i = 0;
+ var j;
+ for (j = offset; j < end; j += dim) {
+ unpackedPoints[i++] = arr.slice(j, j + dim);
+ }
+ return unpackedPoints;
+};
diff --git a/src/ol/geom2/linestringcollection.exports b/src/ol/geom2/linestringcollection.exports
new file mode 100644
index 0000000000..6f7b393e71
--- /dev/null
+++ b/src/ol/geom2/linestringcollection.exports
@@ -0,0 +1,2 @@
+@exportSymbol ol.geom2.LineStringCollection
+@exportSymbol ol.geom2.LineStringCollection.pack
diff --git a/src/ol/geom2/linestringcollection.js b/src/ol/geom2/linestringcollection.js
new file mode 100644
index 0000000000..06a49b13a9
--- /dev/null
+++ b/src/ol/geom2/linestringcollection.js
@@ -0,0 +1,208 @@
+goog.provide('ol.geom2.LineString');
+goog.provide('ol.geom2.LineStringCollection');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.object');
+goog.require('ol.geom2');
+goog.require('ol.structs.Buffer');
+
+
+/**
+ * @typedef {Array.>}
+ */
+ol.geom2.LineString;
+
+
+
+/**
+ * This is an internal class that will be removed from the API.
+ * @constructor
+ * @param {ol.structs.Buffer} buf Buffer.
+ * @param {Object.>=} opt_ranges Ranges.
+ * @param {number=} opt_dim Dimension.
+ */
+ol.geom2.LineStringCollection = function(buf, opt_ranges, opt_dim) {
+
+ /**
+ * @type {ol.structs.Buffer}
+ */
+ this.buf = buf;
+
+ /**
+ * @type {Object.>}
+ */
+ this.ranges = goog.isDef(opt_ranges) ? opt_ranges : {};
+
+ /**
+ * @type {number}
+ */
+ this.dim = goog.isDef(opt_dim) ? opt_dim : 2;
+
+};
+
+
+/**
+ * @param {number} capacity Capacity.
+ * @param {number=} opt_dim Dimension.
+ * @return {ol.geom2.LineStringCollection} Line string collection.
+ */
+ol.geom2.LineStringCollection.createEmpty = function(capacity, opt_dim) {
+ var dim = goog.isDef(opt_dim) ? opt_dim : 2;
+ var buf = new ol.structs.Buffer(new Array(capacity * dim), 0);
+ return new ol.geom2.LineStringCollection(buf, undefined, dim);
+};
+
+
+/**
+ * This is an internal function that will be removed from the API.
+ * @param {Array.} unpackedLineStrings Unpacked line
+ * strings.
+ * @param {number=} opt_capacity Capacity.
+ * @param {number=} opt_dim Dimension.
+ * @return {ol.geom2.LineStringCollection} Line string collection.
+ */
+ol.geom2.LineStringCollection.pack =
+ function(unpackedLineStrings, opt_capacity, opt_dim) {
+ var i;
+ var n = unpackedLineStrings.length;
+ var dim = goog.isDef(opt_dim) ? opt_dim :
+ n > 0 ? unpackedLineStrings[0][0].length : 2;
+ var capacity;
+ if (goog.isDef(opt_capacity)) {
+ capacity = opt_capacity;
+ } else {
+ capacity = 0;
+ for (i = 0; i < n; ++i) {
+ capacity += unpackedLineStrings[i].length;
+ }
+ }
+ capacity *= dim;
+ var arr = new Array(capacity);
+ /** @type {Object.>} */
+ var ranges = {};
+ var offset = 0;
+ var start;
+ for (i = 0; i < n; ++i) {
+ goog.asserts.assert(unpackedLineStrings[i].length > 1);
+ start = offset;
+ offset = ol.geom2.packPoints(arr, offset, unpackedLineStrings[i], dim);
+ ranges[start] = [start, offset];
+ }
+ goog.asserts.assert(offset <= capacity);
+ var buf = new ol.structs.Buffer(arr, offset);
+ return new ol.geom2.LineStringCollection(buf, ranges, dim);
+};
+
+
+/**
+ * @param {ol.geom2.LineString} lineString Line string.
+ * @return {number} Offset.
+ */
+ol.geom2.LineStringCollection.prototype.add = function(lineString) {
+ var n = lineString.length * this.dim;
+ var offset = this.buf.allocate(n);
+ goog.asserts.assert(offset != -1);
+ this.ranges[offset] = [offset, offset + n];
+ ol.geom2.packPoints(this.buf.getArray(), offset, lineString, this.dim);
+ return offset;
+};
+
+
+/**
+ * @param {number} offset Offset.
+ * @return {ol.geom2.LineString} Line string.
+ */
+ol.geom2.LineStringCollection.prototype.get = function(offset) {
+ goog.asserts.assert(offset in this.ranges);
+ var range = this.ranges[offset];
+ return ol.geom2.unpackPoints(
+ this.buf.getArray(), range[0], range[1], this.dim);
+};
+
+
+/**
+ * @return {number} Count.
+ */
+ol.geom2.LineStringCollection.prototype.getCount = function() {
+ return goog.object.getCount(this.ranges);
+};
+
+
+/**
+ * @return {ol.Extent} Extent.
+ */
+ol.geom2.LineStringCollection.prototype.getExtent = function() {
+ return ol.geom2.getExtent(this.buf, this.dim);
+};
+
+
+/**
+ * @return {Uint16Array} Indices.
+ */
+ol.geom2.LineStringCollection.prototype.getIndices = function() {
+ // FIXME cache and track dirty / track output length
+ var dim = this.dim;
+ var offsets = goog.array.map(goog.object.getKeys(this.ranges), Number);
+ goog.array.sort(offsets);
+ var n = offsets.length;
+ var indices = [];
+ var i, j, range, stop;
+ for (i = 0; i < n; ++i) {
+ range = this.ranges[offsets[i]];
+ stop = range[1] / dim - 1;
+ for (j = range[0] / dim; j < stop; ++j) {
+ indices.push(j, j + 1);
+ }
+ }
+ return new Uint16Array(indices);
+};
+
+
+/**
+ * @param {number} offset Offset.
+ */
+ol.geom2.LineStringCollection.prototype.remove = function(offset) {
+ goog.asserts.assert(offset in this.ranges);
+ var range = this.ranges[offset];
+ this.buf.remove(range[1] - range[0], range[0]);
+ delete this.ranges[offset];
+};
+
+
+/**
+ * @param {number} offset Offset.
+ * @param {ol.geom2.LineString} lineString Line string.
+ * @return {number} Offset.
+ */
+ol.geom2.LineStringCollection.prototype.set = function(offset, lineString) {
+ var dim = this.dim;
+ goog.asserts.assert(offset in this.ranges);
+ var range = this.ranges[offset];
+ if (lineString.length * dim == range[1] - range[0]) {
+ ol.geom2.packPoints(this.buf.getArray(), range[0], lineString, dim);
+ this.buf.markDirty(range[1] - range[0], range[0]);
+ return offset;
+ } else {
+ this.remove(offset);
+ return this.add(lineString);
+ }
+};
+
+
+/**
+ * @return {Array.} Line strings.
+ */
+ol.geom2.LineStringCollection.prototype.unpack = function() {
+ var dim = this.dim;
+ var n = this.getCount();
+ var lineStrings = new Array(n);
+ var i = 0;
+ var offset, range;
+ for (offset in this.ranges) {
+ range = this.ranges[Number(offset)];
+ lineStrings[i++] = ol.geom2.unpackPoints(
+ this.buf.getArray(), range[0], range[1], dim);
+ }
+ return lineStrings;
+};
diff --git a/src/ol/geom2/pointcollection.exports b/src/ol/geom2/pointcollection.exports
new file mode 100644
index 0000000000..7a7a485279
--- /dev/null
+++ b/src/ol/geom2/pointcollection.exports
@@ -0,0 +1,4 @@
+@exportSymbol ol.geom2.PointCollection
+@exportSymbol ol.geom2.PointCollection.createEmpty
+@exportSymbol ol.geom2.PointCollection.pack
+@exportProperty ol.geom2.PointCollection.prototype.add
diff --git a/src/ol/geom2/pointcollection.js b/src/ol/geom2/pointcollection.js
new file mode 100644
index 0000000000..c9ba6e8336
--- /dev/null
+++ b/src/ol/geom2/pointcollection.js
@@ -0,0 +1,145 @@
+goog.provide('ol.geom2.Point');
+goog.provide('ol.geom2.PointCollection');
+
+goog.require('goog.asserts');
+goog.require('ol.Extent');
+goog.require('ol.geom2');
+goog.require('ol.structs.Buffer');
+
+
+/**
+ * @typedef {Array.}
+ */
+ol.geom2.Point;
+
+
+
+/**
+ * This is an internal class that will be removed from the API.
+ * @constructor
+ * @param {ol.structs.Buffer} buf Buffer.
+ * @param {number=} opt_dim Dimension.
+ */
+ol.geom2.PointCollection = function(buf, opt_dim) {
+
+ /**
+ * @type {ol.structs.Buffer}
+ */
+ this.buf = buf;
+
+ /**
+ * @type {number}
+ */
+ this.dim = goog.isDef(opt_dim) ? opt_dim : 2;
+
+};
+
+
+/**
+ * This is an internal function that will be removed from the API.
+ * @param {number} capacity Capacity.
+ * @param {number=} opt_dim Dimension.
+ * @return {ol.geom2.PointCollection} Point collection.
+ */
+ol.geom2.PointCollection.createEmpty = function(capacity, opt_dim) {
+ var dim = goog.isDef(opt_dim) ? opt_dim : 2;
+ var buf = new ol.structs.Buffer(new Array(capacity * dim), 0);
+ return new ol.geom2.PointCollection(buf, dim);
+};
+
+
+/**
+ * This is an internal function that will be removed from the API.
+ * @param {Array.} unpackedPoints Unpacked points.
+ * @param {number=} opt_capacity Capacity.
+ * @param {number=} opt_dim Dimension.
+ * @return {ol.geom2.PointCollection} Point collection.
+ */
+ol.geom2.PointCollection.pack =
+ function(unpackedPoints, opt_capacity, opt_dim) {
+ var n = unpackedPoints.length;
+ var dim = goog.isDef(opt_dim) ? opt_dim :
+ n > 0 ? unpackedPoints[0].length : 2;
+ var capacity = goog.isDef(opt_capacity) ? opt_capacity : n * dim;
+ goog.asserts.assert(capacity >= n * dim);
+ var arr = new Array(capacity);
+ ol.geom2.packPoints(arr, 0, unpackedPoints, dim);
+ var buf = new ol.structs.Buffer(arr, n * dim);
+ return new ol.geom2.PointCollection(buf, dim);
+};
+
+
+/**
+ * @param {ol.geom2.Point} point Point.
+ * @return {number} Offset.
+ */
+ol.geom2.PointCollection.prototype.add = function(point) {
+ goog.asserts.assert(point.length == this.dim);
+ return this.buf.add(point);
+};
+
+
+/**
+ * @param {number} offset Offset.
+ * @return {ol.geom2.Point} Point.
+ */
+ol.geom2.PointCollection.prototype.get = function(offset) {
+ var arr = this.buf.getArray();
+ var dim = this.dim;
+ goog.asserts.assert(0 <= offset && offset + dim < arr.length);
+ goog.asserts.assert(offset % dim === 0);
+ return arr.slice(offset, offset + dim);
+};
+
+
+/**
+ * @return {number} Count.
+ */
+ol.geom2.PointCollection.prototype.getCount = function() {
+ return this.buf.getCount() / this.dim;
+};
+
+
+/**
+ * @return {ol.Extent} Extent.
+ */
+ol.geom2.PointCollection.prototype.getExtent = function() {
+ return ol.geom2.getExtent(this.buf, this.dim);
+};
+
+
+/**
+ * @param {number} offset Offset.
+ */
+ol.geom2.PointCollection.prototype.remove = function(offset) {
+ this.buf.remove(this.dim, offset);
+};
+
+
+/**
+ * @param {number} offset Offset.
+ * @param {ol.geom2.Point} point Point.
+ */
+ol.geom2.PointCollection.prototype.set = function(offset, point) {
+ this.buf.set(point, offset);
+};
+
+
+/**
+ * @return {Array.} Points.
+ */
+ol.geom2.PointCollection.prototype.unpack = function() {
+ var dim = this.dim;
+ var n = this.getCount();
+ var points = new Array(n);
+ var i = 0;
+ var bufArr = this.buf.getArray();
+ this.buf.forEachRange(function(start, stop) {
+ var j;
+ for (j = start; j < stop; j += dim) {
+ points[i++] = bufArr.slice(j, j + dim);
+ }
+ });
+ goog.asserts.assert(i == n);
+ return points;
+};
diff --git a/src/ol/layer/vectorlayer2.exports b/src/ol/layer/vectorlayer2.exports
new file mode 100644
index 0000000000..84c34826f8
--- /dev/null
+++ b/src/ol/layer/vectorlayer2.exports
@@ -0,0 +1 @@
+@exportSymbol ol.layer.VectorLayer2
diff --git a/src/ol/layer/vectorlayer2.js b/src/ol/layer/vectorlayer2.js
new file mode 100644
index 0000000000..471fdb8b75
--- /dev/null
+++ b/src/ol/layer/vectorlayer2.js
@@ -0,0 +1,25 @@
+goog.provide('ol.layer.VectorLayer2');
+
+goog.require('ol.layer.Layer');
+goog.require('ol.source.VectorSource2');
+
+
+
+/**
+ * This is an internal class that will be removed from the API.
+ * @constructor
+ * @extends {ol.layer.Layer}
+ * @param {ol.layer.LayerOptions} options Options.
+ */
+ol.layer.VectorLayer2 = function(options) {
+ goog.base(this, options);
+};
+goog.inherits(ol.layer.VectorLayer2, ol.layer.Layer);
+
+
+/**
+ * @return {ol.source.VectorSource2} Source.
+ */
+ol.layer.VectorLayer2.prototype.getVectorSource = function() {
+ return /** @type {ol.source.VectorSource2} */ (this.getSource());
+};
diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js
index 68126fe41c..d250365299 100644
--- a/src/ol/renderer/webgl/webglmaprenderer.js
+++ b/src/ol/renderer/webgl/webglmaprenderer.js
@@ -18,9 +18,11 @@ goog.require('ol.Tile');
goog.require('ol.css');
goog.require('ol.layer.ImageLayer');
goog.require('ol.layer.TileLayer');
+goog.require('ol.layer.VectorLayer2');
goog.require('ol.renderer.Map');
goog.require('ol.renderer.webgl.ImageLayer');
goog.require('ol.renderer.webgl.TileLayer');
+goog.require('ol.renderer.webgl.VectorLayer2');
goog.require('ol.renderer.webgl.map.shader.Color');
goog.require('ol.renderer.webgl.map.shader.Default');
goog.require('ol.size');
@@ -300,6 +302,8 @@ ol.renderer.webgl.Map.prototype.createLayerRenderer = function(layer) {
return new ol.renderer.webgl.TileLayer(this, layer);
} else if (layer instanceof ol.layer.ImageLayer) {
return new ol.renderer.webgl.ImageLayer(this, layer);
+ } else if (layer instanceof ol.layer.VectorLayer2) {
+ return new ol.renderer.webgl.VectorLayer2(this, layer);
} else {
goog.asserts.fail();
return null;
diff --git a/src/ol/renderer/webgl/webglvectorlayer2linestringcollection.glsl b/src/ol/renderer/webgl/webglvectorlayer2linestringcollection.glsl
new file mode 100644
index 0000000000..0877de55b1
--- /dev/null
+++ b/src/ol/renderer/webgl/webglvectorlayer2linestringcollection.glsl
@@ -0,0 +1,19 @@
+//! NAMESPACE=ol.renderer.webgl.vectorlayer2.shader.LineStringCollection
+//! CLASS=ol.renderer.webgl.vectorlayer2.shader.LineStringCollection
+
+
+//! VERTEX
+attribute vec2 a_position;
+uniform mat4 u_modelViewMatrix;
+
+void main(void) {
+ gl_Position = u_modelViewMatrix * vec4(a_position, 0., 1.);
+}
+
+
+//! FRAGMENT
+uniform vec4 u_color;
+
+void main(void) {
+ gl_FragColor = u_color;
+}
diff --git a/src/ol/renderer/webgl/webglvectorlayer2linestringcollectionshader.js b/src/ol/renderer/webgl/webglvectorlayer2linestringcollectionshader.js
new file mode 100644
index 0000000000..209412877e
--- /dev/null
+++ b/src/ol/renderer/webgl/webglvectorlayer2linestringcollectionshader.js
@@ -0,0 +1,77 @@
+// This file is automatically generated, do not edit
+goog.provide('ol.renderer.webgl.vectorlayer2.shader.LineStringCollection');
+goog.require('ol.webgl.shader');
+/**
+ * @constructor
+ * @extends {ol.webgl.shader.Fragment}
+ */
+ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionFragment = function() {
+ goog.base(this, ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionFragment.SOURCE);
+};
+goog.inherits(ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionFragment, ol.webgl.shader.Fragment);
+goog.addSingletonGetter(ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionFragment);
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionFragment.DEBUG_SOURCE = 'precision mediump float;\n//! NAMESPACE=ol.renderer.webgl.vectorlayer2.shader.LineStringCollection\n//! CLASS=ol.renderer.webgl.vectorlayer2.shader.LineStringCollection\n\n\n//! FRAGMENT\nuniform vec4 u_color;\n\nvoid main(void) {\n gl_FragColor = u_color;\n}\n\n';
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionFragment.OPTIMIZED_SOURCE = 'precision mediump float;uniform vec4 b;void main(){gl_FragColor=b;}';
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionFragment.SOURCE = goog.DEBUG ?
+ ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionFragment.DEBUG_SOURCE :
+ ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionFragment.OPTIMIZED_SOURCE;
+/**
+ * @constructor
+ * @extends {ol.webgl.shader.Vertex}
+ */
+ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionVertex = function() {
+ goog.base(this, ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionVertex.SOURCE);
+};
+goog.inherits(ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionVertex, ol.webgl.shader.Vertex);
+goog.addSingletonGetter(ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionVertex);
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionVertex.DEBUG_SOURCE = '//! NAMESPACE=ol.renderer.webgl.vectorlayer2.shader.LineStringCollection\n//! CLASS=ol.renderer.webgl.vectorlayer2.shader.LineStringCollection\n\n\n//! VERTEX\nattribute vec2 a_position;\nuniform mat4 u_modelViewMatrix;\n\nvoid main(void) {\n gl_Position = u_modelViewMatrix * vec4(a_position, 0., 1.);\n}\n\n\n';
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionVertex.OPTIMIZED_SOURCE = 'attribute vec2 b;uniform mat4 a;void main(){gl_Position=a*vec4(b,0,1);}';
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionVertex.SOURCE = goog.DEBUG ?
+ ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionVertex.DEBUG_SOURCE :
+ ol.renderer.webgl.vectorlayer2.shader.LineStringCollectionVertex.OPTIMIZED_SOURCE;
+/**
+ * @constructor
+ * @param {WebGLRenderingContext} gl GL.
+ * @param {WebGLProgram} program Program.
+ */
+ol.renderer.webgl.vectorlayer2.shader.LineStringCollection.Locations = function(gl, program) {
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_modelViewMatrix = gl.getUniformLocation(
+ program, goog.DEBUG ? 'u_modelViewMatrix' : 'a');
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_color = gl.getUniformLocation(
+ program, goog.DEBUG ? 'u_color' : 'b');
+ /**
+ * @type {number}
+ */
+ this.a_position = gl.getAttribLocation(
+ program, goog.DEBUG ? 'a_position' : 'b');
+};
diff --git a/src/ol/renderer/webgl/webglvectorlayer2pointcollection.glsl b/src/ol/renderer/webgl/webglvectorlayer2pointcollection.glsl
new file mode 100644
index 0000000000..07f09ef42e
--- /dev/null
+++ b/src/ol/renderer/webgl/webglvectorlayer2pointcollection.glsl
@@ -0,0 +1,21 @@
+//! NAMESPACE=ol.renderer.webgl.vectorlayer2.shader.PointCollection
+//! CLASS=ol.renderer.webgl.vectorlayer2.shader.PointCollection
+
+
+//! VERTEX
+attribute vec2 a_position;
+uniform float u_pointSize;
+uniform mat4 u_modelViewMatrix;
+
+void main(void) {
+ gl_Position = u_modelViewMatrix * vec4(a_position, 0., 1.);
+ gl_PointSize = u_pointSize;
+}
+
+
+//! FRAGMENT
+uniform vec4 u_color;
+
+void main(void) {
+ gl_FragColor = u_color;
+}
diff --git a/src/ol/renderer/webgl/webglvectorlayer2pointcollectionshader.js b/src/ol/renderer/webgl/webglvectorlayer2pointcollectionshader.js
new file mode 100644
index 0000000000..825697a28f
--- /dev/null
+++ b/src/ol/renderer/webgl/webglvectorlayer2pointcollectionshader.js
@@ -0,0 +1,82 @@
+// This file is automatically generated, do not edit
+goog.provide('ol.renderer.webgl.vectorlayer2.shader.PointCollection');
+goog.require('ol.webgl.shader');
+/**
+ * @constructor
+ * @extends {ol.webgl.shader.Fragment}
+ */
+ol.renderer.webgl.vectorlayer2.shader.PointCollectionFragment = function() {
+ goog.base(this, ol.renderer.webgl.vectorlayer2.shader.PointCollectionFragment.SOURCE);
+};
+goog.inherits(ol.renderer.webgl.vectorlayer2.shader.PointCollectionFragment, ol.webgl.shader.Fragment);
+goog.addSingletonGetter(ol.renderer.webgl.vectorlayer2.shader.PointCollectionFragment);
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.vectorlayer2.shader.PointCollectionFragment.DEBUG_SOURCE = 'precision mediump float;\n//! NAMESPACE=ol.renderer.webgl.vectorlayer2.shader.PointCollection\n//! CLASS=ol.renderer.webgl.vectorlayer2.shader.PointCollection\n\n\n//! FRAGMENT\nuniform vec4 u_color;\n\nvoid main(void) {\n gl_FragColor = u_color;\n}\n\n';
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.vectorlayer2.shader.PointCollectionFragment.OPTIMIZED_SOURCE = 'precision mediump float;uniform vec4 c;void main(){gl_FragColor=c;}';
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.vectorlayer2.shader.PointCollectionFragment.SOURCE = goog.DEBUG ?
+ ol.renderer.webgl.vectorlayer2.shader.PointCollectionFragment.DEBUG_SOURCE :
+ ol.renderer.webgl.vectorlayer2.shader.PointCollectionFragment.OPTIMIZED_SOURCE;
+/**
+ * @constructor
+ * @extends {ol.webgl.shader.Vertex}
+ */
+ol.renderer.webgl.vectorlayer2.shader.PointCollectionVertex = function() {
+ goog.base(this, ol.renderer.webgl.vectorlayer2.shader.PointCollectionVertex.SOURCE);
+};
+goog.inherits(ol.renderer.webgl.vectorlayer2.shader.PointCollectionVertex, ol.webgl.shader.Vertex);
+goog.addSingletonGetter(ol.renderer.webgl.vectorlayer2.shader.PointCollectionVertex);
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.vectorlayer2.shader.PointCollectionVertex.DEBUG_SOURCE = '//! NAMESPACE=ol.renderer.webgl.vectorlayer2.shader.PointCollection\n//! CLASS=ol.renderer.webgl.vectorlayer2.shader.PointCollection\n\n\n//! VERTEX\nattribute vec2 a_position;\nuniform float u_pointSize;\nuniform mat4 u_modelViewMatrix;\n\nvoid main(void) {\n gl_Position = u_modelViewMatrix * vec4(a_position, 0., 1.);\n gl_PointSize = u_pointSize;\n}\n\n\n';
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.vectorlayer2.shader.PointCollectionVertex.OPTIMIZED_SOURCE = 'attribute vec2 c;uniform float a;uniform mat4 b;void main(){gl_Position=b*vec4(c,0,1);gl_PointSize=a;}';
+/**
+ * @const
+ * @type {string}
+ */
+ol.renderer.webgl.vectorlayer2.shader.PointCollectionVertex.SOURCE = goog.DEBUG ?
+ ol.renderer.webgl.vectorlayer2.shader.PointCollectionVertex.DEBUG_SOURCE :
+ ol.renderer.webgl.vectorlayer2.shader.PointCollectionVertex.OPTIMIZED_SOURCE;
+/**
+ * @constructor
+ * @param {WebGLRenderingContext} gl GL.
+ * @param {WebGLProgram} program Program.
+ */
+ol.renderer.webgl.vectorlayer2.shader.PointCollection.Locations = function(gl, program) {
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_pointSize = gl.getUniformLocation(
+ program, goog.DEBUG ? 'u_pointSize' : 'a');
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_modelViewMatrix = gl.getUniformLocation(
+ program, goog.DEBUG ? 'u_modelViewMatrix' : 'b');
+ /**
+ * @type {WebGLUniformLocation}
+ */
+ this.u_color = gl.getUniformLocation(
+ program, goog.DEBUG ? 'u_color' : 'c');
+ /**
+ * @type {number}
+ */
+ this.a_position = gl.getAttribLocation(
+ program, goog.DEBUG ? 'a_position' : 'c');
+};
diff --git a/src/ol/renderer/webgl/webglvectorlayer2renderer.js b/src/ol/renderer/webgl/webglvectorlayer2renderer.js
new file mode 100644
index 0000000000..436732107c
--- /dev/null
+++ b/src/ol/renderer/webgl/webglvectorlayer2renderer.js
@@ -0,0 +1,219 @@
+goog.provide('ol.renderer.webgl.VectorLayer2');
+
+goog.require('goog.vec.Mat4');
+goog.require('goog.webgl');
+goog.require('ol.math');
+goog.require('ol.renderer.webgl.Layer');
+goog.require('ol.renderer.webgl.vectorlayer2.shader.LineStringCollection');
+goog.require('ol.renderer.webgl.vectorlayer2.shader.PointCollection');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.webgl.Layer}
+ * @param {ol.renderer.Map} mapRenderer Map renderer.
+ * @param {ol.layer.VectorLayer2} vectorLayer2 Vector layer.
+ */
+ol.renderer.webgl.VectorLayer2 = function(mapRenderer, vectorLayer2) {
+
+ goog.base(this, mapRenderer, vectorLayer2);
+
+ goog.vec.Mat4.makeIdentity(this.projectionMatrix);
+
+ /**
+ * @private
+ * @type {!goog.vec.Mat4.Number}
+ */
+ this.modelViewMatrix_ = goog.vec.Mat4.createNumberIdentity();
+
+ /**
+ * @private
+ * @type
+ * {ol.renderer.webgl.vectorlayer2.shader.LineStringCollection.Locations}
+ */
+ this.lineStringCollectionLocations_ = null;
+
+ /**
+ * @private
+ * @type {ol.renderer.webgl.vectorlayer2.shader.PointCollection.Locations}
+ */
+ this.pointCollectionLocations_ = null;
+
+};
+goog.inherits(ol.renderer.webgl.VectorLayer2, ol.renderer.webgl.Layer);
+
+
+/**
+ * @return {ol.layer.VectorLayer2} Vector layer.
+ */
+ol.renderer.webgl.VectorLayer2.prototype.getVectorLayer = function() {
+ return /** @type {ol.layer.VectorLayer2} */ (this.getLayer());
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.VectorLayer2.prototype.handleWebGLContextLost = function() {
+ goog.base(this, 'handleWebGLContextLost');
+ this.pointCollectionLocations_ = null;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.VectorLayer2.prototype.renderFrame =
+ function(frameState, layerState) {
+
+ var mapRenderer = this.getWebGLMapRenderer();
+ var gl = mapRenderer.getGL();
+
+ var view2DState = frameState.view2DState;
+
+ var vectorLayer = this.getVectorLayer();
+ var vectorSource = vectorLayer.getVectorSource();
+
+ var size = frameState.size;
+ var framebufferDimension = ol.math.roundUpToPowerOfTwo(
+ Math.max(size[0], size[1]));
+
+ this.bindFramebuffer(frameState, framebufferDimension);
+ gl.viewport(0, 0, framebufferDimension, framebufferDimension);
+
+ gl.clearColor(0, 0, 0, 0);
+ gl.clear(goog.webgl.COLOR_BUFFER_BIT);
+ gl.enable(goog.webgl.BLEND);
+
+ goog.vec.Mat4.makeIdentity(this.modelViewMatrix_);
+ if (view2DState.rotation !== 0) {
+ goog.vec.Mat4.rotateZ(this.modelViewMatrix_, -view2DState.rotation);
+ }
+ goog.vec.Mat4.scale(this.modelViewMatrix_,
+ 2 / (framebufferDimension * view2DState.resolution),
+ 2 / (framebufferDimension * view2DState.resolution),
+ 1);
+ goog.vec.Mat4.translate(this.modelViewMatrix_,
+ -view2DState.center[0],
+ -view2DState.center[1],
+ 0);
+
+ var pointCollections = vectorSource.getPointCollections();
+ if (pointCollections.length > 0) {
+ this.renderPointCollections(pointCollections);
+ }
+ var lineStringCollections = vectorSource.getLineStringCollections();
+ if (lineStringCollections.length > 0) {
+ this.renderLineStringCollections(lineStringCollections);
+ }
+
+ goog.vec.Mat4.makeIdentity(this.texCoordMatrix);
+ goog.vec.Mat4.translate(this.texCoordMatrix,
+ 0.5,
+ 0.5,
+ 0);
+ goog.vec.Mat4.scale(this.texCoordMatrix,
+ size[0] / framebufferDimension,
+ size[1] / framebufferDimension,
+ 1);
+ goog.vec.Mat4.translate(this.texCoordMatrix,
+ -0.5,
+ -0.5,
+ 0);
+
+};
+
+
+/**
+ * @param {Array.} lineStringCollections Line
+ * string collections.
+ */
+ol.renderer.webgl.VectorLayer2.prototype.renderLineStringCollections =
+ function(lineStringCollections) {
+
+ var mapRenderer = this.getWebGLMapRenderer();
+ var gl = mapRenderer.getGL();
+
+ var fragmentShader = ol.renderer.webgl.vectorlayer2.shader.
+ LineStringCollectionFragment.getInstance();
+ var vertexShader = ol.renderer.webgl.vectorlayer2.shader.
+ LineStringCollectionVertex.getInstance();
+ var program = mapRenderer.getProgram(fragmentShader, vertexShader);
+ gl.useProgram(program);
+ if (goog.isNull(this.lineStringCollectionLocations_)) {
+ this.lineStringCollectionLocations_ =
+ new ol.renderer.webgl.vectorlayer2.shader.LineStringCollection.
+ Locations(gl, program);
+ }
+
+ gl.uniformMatrix4fv(this.lineStringCollectionLocations_.u_modelViewMatrix,
+ false, this.modelViewMatrix_);
+
+ var buf, dim, i, indexBuffer, indices, lineStringCollection;
+ for (i = 0; i < lineStringCollections.length; ++i) {
+ lineStringCollection = lineStringCollections[i];
+ buf = lineStringCollection.buf;
+ dim = lineStringCollection.dim;
+ mapRenderer.bindBuffer(goog.webgl.ARRAY_BUFFER, buf);
+ // FIXME re-use index buffer
+ // FIXME use mapRenderer.bindBuffer
+ indices = lineStringCollection.getIndices();
+ indexBuffer = gl.createBuffer();
+ gl.bindBuffer(goog.webgl.ELEMENT_ARRAY_BUFFER, indexBuffer);
+ gl.bufferData(
+ goog.webgl.ELEMENT_ARRAY_BUFFER, indices, goog.webgl.DYNAMIC_DRAW);
+ gl.enableVertexAttribArray(this.lineStringCollectionLocations_.a_position);
+ gl.vertexAttribPointer(this.lineStringCollectionLocations_.a_position, 2,
+ goog.webgl.FLOAT, false, 4 * dim, 0);
+ gl.uniform4fv(this.lineStringCollectionLocations_.u_color, [1, 1, 0, 0.75]);
+ gl.drawElements(
+ goog.webgl.LINES, indices.length, goog.webgl.UNSIGNED_SHORT, 0);
+ gl.bindBuffer(goog.webgl.ELEMENT_ARRAY_BUFFER, null);
+ gl.deleteBuffer(indexBuffer);
+ }
+
+};
+
+
+/**
+ * @param {Array.} pointCollections Point collections.
+ */
+ol.renderer.webgl.VectorLayer2.prototype.renderPointCollections =
+ function(pointCollections) {
+
+ var mapRenderer = this.getWebGLMapRenderer();
+ var gl = mapRenderer.getGL();
+
+ var fragmentShader = ol.renderer.webgl.vectorlayer2.shader.
+ PointCollectionFragment.getInstance();
+ var vertexShader = ol.renderer.webgl.vectorlayer2.shader.
+ PointCollectionVertex.getInstance();
+ var program = mapRenderer.getProgram(fragmentShader, vertexShader);
+ gl.useProgram(program);
+ if (goog.isNull(this.pointCollectionLocations_)) {
+ this.pointCollectionLocations_ =
+ new ol.renderer.webgl.vectorlayer2.shader.PointCollection.Locations(
+ gl, program);
+ }
+
+ gl.uniformMatrix4fv(this.pointCollectionLocations_.u_modelViewMatrix, false,
+ this.modelViewMatrix_);
+
+ var buf, dim, i, pointCollection;
+ for (i = 0; i < pointCollections.length; ++i) {
+ pointCollection = pointCollections[i];
+ buf = pointCollection.buf;
+ dim = pointCollection.dim;
+ mapRenderer.bindBuffer(goog.webgl.ARRAY_BUFFER, buf);
+ gl.enableVertexAttribArray(this.pointCollectionLocations_.a_position);
+ gl.vertexAttribPointer(this.pointCollectionLocations_.a_position, 2,
+ goog.webgl.FLOAT, false, 4 * dim, 0);
+ gl.uniform4fv(this.pointCollectionLocations_.u_color, [1, 0, 0, 0.75]);
+ gl.uniform1f(this.pointCollectionLocations_.u_pointSize, 3);
+ buf.forEachRange(function(start, stop) {
+ gl.drawArrays(goog.webgl.POINTS, start / dim, (stop - start) / dim);
+ });
+ }
+
+};
diff --git a/src/ol/source/vectorsource2.exports b/src/ol/source/vectorsource2.exports
new file mode 100644
index 0000000000..1d3cf6c8ed
--- /dev/null
+++ b/src/ol/source/vectorsource2.exports
@@ -0,0 +1 @@
+@exportSymbol ol.source.VectorSource2
diff --git a/src/ol/source/vectorsource2.js b/src/ol/source/vectorsource2.js
new file mode 100644
index 0000000000..e5e72f37bd
--- /dev/null
+++ b/src/ol/source/vectorsource2.js
@@ -0,0 +1,54 @@
+goog.provide('ol.source.VectorSource2');
+
+goog.require('ol.geom2.LineStringCollection');
+goog.require('ol.geom2.PointCollection');
+goog.require('ol.source.Source');
+
+
+
+/**
+ * This is an internal class that will be removed from the API.
+ * @constructor
+ * @extends {ol.source.Source}
+ * @param {ol.source.VectorSource2Options} options Options.
+ */
+ol.source.VectorSource2 = function(options) {
+
+ goog.base(this, {
+ attributions: options.attributions,
+ extent: options.extent,
+ projection: options.projection
+ });
+
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.pointCollections_ = goog.isDef(options.pointCollections) ?
+ options.pointCollections : [];
+
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.lineStringCollections_ = goog.isDef(options.lineStringCollections) ?
+ options.lineStringCollections : [];
+
+};
+goog.inherits(ol.source.VectorSource2, ol.source.Source);
+
+
+/**
+ * @return {Array.} Line string collections.
+ */
+ol.source.VectorSource2.prototype.getLineStringCollections = function() {
+ return this.lineStringCollections_;
+};
+
+
+/**
+ * @return {Array.} Point collections.
+ */
+ol.source.VectorSource2.prototype.getPointCollections = function() {
+ return this.pointCollections_;
+};
diff --git a/src/ol/structs/buffer.js b/src/ol/structs/buffer.js
index add2494b9f..91848768c2 100644
--- a/src/ol/structs/buffer.js
+++ b/src/ol/structs/buffer.js
@@ -62,6 +62,18 @@ ol.structs.Buffer = function(opt_arr, opt_used, opt_usage) {
}
}
+ /**
+ * @private
+ * @type {?Float32Array}
+ */
+ this.split32_ = null;
+
+ /**
+ * @private
+ * @type {ol.structs.IntegerSet}
+ */
+ this.split32DirtySet_ = null;
+
/**
* @private
* @type {number}
@@ -72,24 +84,31 @@ ol.structs.Buffer = function(opt_arr, opt_used, opt_usage) {
};
+/**
+ * @param {number} size Size.
+ * @return {number} Offset.
+ */
+ol.structs.Buffer.prototype.allocate = function(size) {
+ goog.asserts.assert(size > 0);
+ var offset = this.freeSet_.findRange(size);
+ goog.asserts.assert(offset != -1); // FIXME
+ this.freeSet_.removeRange(offset, offset + size);
+ return offset;
+};
+
+
/**
* @param {Array.} 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 offset = this.allocate(size);
var i;
for (i = 0; i < size; ++i) {
this.arr_[offset + i] = values[i];
}
- var ii;
- for (i = 0, ii = this.dirtySets_.length; i < ii; ++i) {
- this.dirtySets_[i].addRange(offset, offset + size);
- }
+ this.markDirty(size, offset);
return offset;
};
@@ -139,6 +158,45 @@ ol.structs.Buffer.prototype.getFreeSet = function() {
};
+/**
+ * Returns a Float32Array twice the length of the buffer containing each value
+ * split into two 32-bit floating point values that, when added together,
+ * approximate the original value. Even indicies contain the high bits, odd
+ * indicies contain the low bits.
+ * @see http://blogs.agi.com/insight3d/index.php/2008/09/03/precisions-precisions/
+ * @return {Float32Array} Split.
+ */
+ol.structs.Buffer.prototype.getSplit32 = function() {
+ var arr = this.arr_;
+ var n = arr.length;
+ if (goog.isNull(this.split32DirtySet_)) {
+ this.split32DirtySet_ = new ol.structs.IntegerSet([0, n]);
+ this.addDirtySet(this.split32DirtySet_);
+ }
+ if (goog.isNull(this.split32_)) {
+ this.split32_ = new Float32Array(2 * n);
+ }
+ var split32 = this.split32_;
+ this.split32DirtySet_.forEachRange(function(start, stop) {
+ var doubleHigh, i, j, value;
+ for (i = start, j = 2 * start; i < stop; ++i, j += 2) {
+ value = arr[i];
+ if (value < 0) {
+ doubleHigh = 65536 * Math.floor(-value / 65536);
+ split32[j] = -doubleHigh;
+ split32[j + 1] = value + doubleHigh;
+ } else {
+ doubleHigh = 65536 * Math.floor(value / 65536);
+ split32[j] = doubleHigh;
+ split32[j + 1] = value - doubleHigh;
+ }
+ }
+ });
+ this.split32DirtySet_.clear();
+ return this.split32_;
+};
+
+
/**
* @return {number} Usage.
*/
@@ -147,6 +205,18 @@ ol.structs.Buffer.prototype.getUsage = function() {
};
+/**
+ * @param {number} size Size.
+ * @param {number} offset Offset.
+ */
+ol.structs.Buffer.prototype.markDirty = function(size, offset) {
+ var i, ii;
+ for (i = 0, ii = this.dirtySets_.length; i < ii; ++i) {
+ this.dirtySets_[i].addRange(offset, offset + size);
+ }
+};
+
+
/**
* @param {number} size Size.
* @param {number} offset Offset.
@@ -187,8 +257,5 @@ ol.structs.Buffer.prototype.set = function(values, offset) {
for (i = 0; i < n; ++i) {
arr[offset + i] = values[i];
}
- var ii;
- for (i = 0, ii = this.dirtySets_.length; i < ii; ++i) {
- this.dirtySets_[i].addRange(offset, offset + n);
- }
+ this.markDirty(n, offset);
};
diff --git a/test/expect-0.2.0-ol3/expect.js b/test/expect-0.2.0-ol3/expect.js
index 052f9a1df2..d8be00a0f1 100644
--- a/test/expect-0.2.0-ol3/expect.js
+++ b/test/expect-0.2.0-ol3/expect.js
@@ -371,6 +371,20 @@
return this;
};
+ /**
+ * Checks if the array sort of equals another array.
+ *
+ * @api public
+ */
+
+ Assertion.prototype.arreql = function (obj) {
+ this.assert(
+ goog.array.equals(this.obj, obj)
+ , function(){ return 'expected ' + i(this.obj) + ' to sort of equal ' + i(obj) }
+ , function(){ return 'expected ' + i(this.obj) + ' to sort of not equal ' + i(obj) });
+ return this;
+ };
+
/**
* Checks if the obj sortof equals another.
*
diff --git a/test/spec/ol/geom2/geom2.test.js b/test/spec/ol/geom2/geom2.test.js
new file mode 100644
index 0000000000..d894bc65af
--- /dev/null
+++ b/test/spec/ol/geom2/geom2.test.js
@@ -0,0 +1,71 @@
+goog.provide('ol.test.geom2');
+
+
+describe('ol.geom2', function() {
+
+ var buf, dim;
+ beforeEach(function() {
+ buf = new ol.structs.Buffer([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 12);
+ dim = 2;
+ });
+
+ describe('ol.geom2.getExtent', function() {
+
+ it('returns the expected extent', function() {
+ var extent = ol.geom2.getExtent(buf, dim);
+ expect(extent).to.eql([0, 10, 1, 11]);
+ });
+
+ it('returns the expect extent in three dimensions', function() {
+ var extent = ol.geom2.getExtent(buf, 3);
+ expect(extent).to.eql([0, 9, 1, 10, 2, 11]);
+ });
+
+ it('returns the expect extent in four dimensions', function() {
+ var extent = ol.geom2.getExtent(buf, 4);
+ expect(extent).to.eql([0, 8, 1, 9, 2, 10, 3, 11]);
+ });
+
+ it('returns the expect extent in six dimensions', function() {
+ var extent = ol.geom2.getExtent(buf, 6);
+ expect(extent).to.eql([0, 6, 1, 7, 2, 8, 3, 9, 4, 10, 5, 11]);
+ });
+
+ });
+
+ describe('ol.geom2.packPoints', function() {
+
+ it('packs points as expected', function() {
+ var arr = [];
+ var offset = ol.geom2.packPoints(arr, 0, [[0, 1], [2, 3], [4, 5]], 2);
+ expect(offset).to.be(6);
+ expect(arr).to.eql([0, 1, 2, 3, 4, 5]);
+ });
+
+ it('raises an exception if dimensions do not match', function() {
+ expect(function() {
+ ol.geom2.packPoints([], 0, [[0, 1, 2]], 2);
+ }).to.throwException();
+ });
+
+ });
+
+ describe('ol.geom2.unpackPoints', function() {
+
+ it('unpacks points in two dimensions', function() {
+ var unpackedPoints = ol.geom2.unpackPoints([0, 1, 2, 3, 4, 5], 0, 6, 2);
+ expect(unpackedPoints).to.eql([[0, 1], [2, 3], [4, 5]]);
+ });
+
+ it('unpacks points in three dimensions', function() {
+ var unpackedPoints = ol.geom2.unpackPoints([0, 1, 2, 3, 4, 5], 0, 6, 3);
+ expect(unpackedPoints).to.eql([[0, 1, 2], [3, 4, 5]]);
+ });
+
+ });
+
+});
+
+
+goog.require('ol.geom2');
+goog.require('ol.structs.Buffer');
diff --git a/test/spec/ol/geom2/linecollection.test.js b/test/spec/ol/geom2/linecollection.test.js
new file mode 100644
index 0000000000..8fe7c4eddb
--- /dev/null
+++ b/test/spec/ol/geom2/linecollection.test.js
@@ -0,0 +1,330 @@
+goog.provide('ol.test.geom2.LineStringCollection');
+
+
+describe('ol.geom2.LineStringCollection', function() {
+
+ describe('createEmpty', function() {
+
+ it('creates an empty instance with the specified capacity', function() {
+ var lsc = ol.geom2.LineStringCollection.createEmpty(16);
+ expect(lsc.getCount()).to.be(0);
+ expect(lsc.buf.getArray()).to.have.length(32);
+ });
+
+ it('can create empty collections for higher dimensions', function() {
+ var lsc = ol.geom2.LineStringCollection.createEmpty(16, 3);
+ expect(lsc.getCount()).to.be(0);
+ expect(lsc.buf.getArray()).to.have.length(48);
+ });
+
+ });
+
+ describe('pack', function() {
+
+ it('packs an empty array', function() {
+ var lsc = ol.geom2.LineStringCollection.pack([]);
+ expect(lsc.buf.getArray()).to.be.empty();
+ expect(lsc.ranges).to.be.empty();
+ expect(lsc.dim).to.be(2);
+ });
+
+ it('packs an empty array with a capacity', function() {
+ var lsc = ol.geom2.LineStringCollection.pack([], 4);
+ expect(lsc.buf.getArray()).to.eql(
+ [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN]);
+ expect(lsc.ranges).to.be.empty();
+ expect(lsc.dim).to.be(2);
+ });
+
+ it('packs an array of line strings', function() {
+ var lsc = ol.geom2.LineStringCollection.pack(
+ [[[0, 1], [2, 3], [4, 5]], [[6, 7], [8, 9]]]);
+ expect(lsc.buf.getArray()).to.eql([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ expect(lsc.getCount()).to.be(2);
+ expect(lsc.ranges[0]).to.eql([0, 6]);
+ expect(lsc.ranges[6]).to.eql([6, 10]);
+ expect(lsc.dim).to.be(2);
+ });
+
+ it('packs an array of line strings with a different dimension', function() {
+ var lsc = ol.geom2.LineStringCollection.pack(
+ [[[0, 1, 2], [3, 4, 5]], [[6, 7, 8], [9, 10, 11]]]);
+ expect(lsc.buf.getArray()).to.eql([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
+ expect(lsc.getCount()).to.be(2);
+ expect(lsc.ranges[0]).to.eql([0, 6]);
+ expect(lsc.ranges[6]).to.eql([6, 12]);
+ expect(lsc.dim).to.be(3);
+ });
+
+ it('packs an array of line strings with extra capacity', function() {
+ var lsc = ol.geom2.LineStringCollection.pack(
+ [[[0, 1], [2, 3], [4, 5]], [[6, 7], [8, 9]]], 16);
+ expect(lsc.buf.getArray().slice(0, 10)).to.eql(
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ expect(lsc.buf.getArray()).to.have.length(32);
+ expect(lsc.getCount()).to.be(2);
+ expect(lsc.ranges[0]).to.eql([0, 6]);
+ expect(lsc.ranges[6]).to.eql([6, 10]);
+ expect(lsc.dim).to.be(2);
+ });
+
+ it('throws an error when dimensions are inconsistent', function() {
+ expect(function() {
+ var lsc = ol.geom2.LineStringCollection.pack([[[0, 1], [2, 3, 4]]]);
+ lsc = lsc; // suppress gjslint warning about unused variable
+ }).to.throwException();
+ });
+
+ it('throws an error when a line string is too short', function() {
+ expect(function() {
+ var lsc = ol.geom2.LineStringCollection.pack([[[0, 1]]]);
+ lsc = lsc; // suppress gjslint warning about unused variable
+ }).to.throwException();
+ });
+
+ it('throws an error when the capacity is too small', function() {
+ expect(function() {
+ var lsc = ol.geom2.LineStringCollection.pack(
+ [[[0, 1], [2, 3], [4, 5]], [[6, 7], [8, 9]]], 4);
+ lsc = lsc; // suppress gjslint warning about unused variable
+ }).to.throwException();
+ });
+
+ });
+
+ describe('with an empty instance with spare capacity', function() {
+
+ var lsc;
+ beforeEach(function() {
+ var buf = new ol.structs.Buffer(new Array(8), 0);
+ lsc = new ol.geom2.LineStringCollection(buf);
+ });
+
+ describe('add', function() {
+
+ it('adds a line string', function() {
+ var offset = lsc.add([[0, 1], [2, 3]]);
+ expect(offset).to.be(0);
+ expect(lsc.getCount()).to.be(1);
+ expect(lsc.ranges[0]).to.eql([0, 4]);
+ expect(lsc.dim).to.be(2);
+ });
+
+ });
+
+ describe('getCount', function() {
+
+ it('returns zero', function() {
+ expect(lsc.getCount()).to.be(0);
+ });
+
+ });
+
+ describe('getExtent', function() {
+
+ it('returns an empty extent', function() {
+ expect(ol.extent.isEmpty(lsc.getExtent())).to.be(true);
+ });
+
+ });
+
+ describe('getIndices', function() {
+
+ it('returns the expected value', function() {
+ expect(lsc.getIndices()).to.be.empty();
+ });
+
+ });
+
+ describe('remove', function() {
+
+ it('throws an exception', function() {
+ expect(function() {
+ lsc.remove(0);
+ }).to.throwException();
+ });
+
+ });
+
+ });
+
+ describe('with an initial line string', function() {
+
+ var lsc, offset;
+ beforeEach(function() {
+ var buf = new ol.structs.Buffer(new Array(8), 0);
+ lsc = new ol.geom2.LineStringCollection(buf);
+ offset = lsc.add([[0, 1], [2, 3]]);
+ });
+
+ describe('add', function() {
+
+ it('can add a second line string', function() {
+ var offset2 = lsc.add([[4, 5], [6, 7]]);
+ expect(offset2).to.be(4);
+ expect(lsc.getCount()).to.be(2);
+ expect(lsc.ranges[0]).to.eql([0, 4]);
+ expect(lsc.ranges[4]).to.eql([4, 8]);
+ expect(lsc.dim).to.be(2);
+ });
+
+ });
+
+ describe('get', function() {
+
+ it('returns the expected line string', function() {
+ expect(lsc.get(0)).to.eql([[0, 1], [2, 3]]);
+ });
+
+ });
+
+ describe('getCount', function() {
+
+ it('returns the expected value', function() {
+ expect(lsc.getCount()).to.be(1);
+ });
+
+ });
+
+ describe('getExtent', function() {
+
+ it('returns the expected extent', function() {
+ expect(lsc.getExtent()).to.eql([0, 2, 1, 3]);
+ });
+
+ });
+
+ describe('getIndices', function() {
+
+ it('returns the expected value', function() {
+ expect(lsc.getIndices()).to.arreql([0, 1]);
+ });
+
+ });
+
+ describe('remove', function() {
+
+ it('removes the line string', function() {
+ lsc.remove(0);
+ expect(lsc.getCount()).to.be(0);
+ });
+
+ });
+
+ describe('set', function() {
+
+ it('can update the line string in place', function() {
+ expect(lsc.set(0, [[4, 5], [6, 7]])).to.be(0);
+ expect(lsc.buf.getArray()).to.eql([4, 5, 6, 7, NaN, NaN, NaN, NaN]);
+ });
+
+ it('can replace the line string with a shorter one', function() {
+ expect(lsc.set(0, [[4, 5]])).to.be(0);
+ expect(lsc.buf.getArray()).to.eql([4, 5, NaN, NaN, NaN, NaN, NaN, NaN]);
+ });
+
+ it('can replace the line string with a longer one', function() {
+ expect(lsc.set(0, [[4, 5], [6, 7], [8, 9], [10, 11]])).to.be(0);
+ expect(lsc.buf.getArray()).to.eql([4, 5, 6, 7, 8, 9, 10, 11]);
+ });
+
+ });
+
+ describe('unpack', function() {
+
+ it('returns the expected value', function() {
+ expect(lsc.unpack()).to.eql([[[0, 1], [2, 3]]]);
+ });
+
+ });
+
+ });
+
+ describe('with multiple initial line strings', function() {
+
+ var lsc;
+ beforeEach(function() {
+ lsc = ol.geom2.LineStringCollection.pack(
+ [[[0, 1], [2, 3]], [[4, 5], [6, 7], [8, 9]]], 16);
+ });
+
+ describe('get', function() {
+
+ it('returns the expected values', function() {
+ expect(lsc.get(0)).to.eql([[0, 1], [2, 3]]);
+ expect(lsc.get(4)).to.eql([[4, 5], [6, 7], [8, 9]]);
+ });
+
+ });
+
+ describe('getCount', function() {
+
+ it('returns the expected value', function() {
+ expect(lsc.getCount()).to.be(2);
+ });
+
+ });
+
+ describe('getExtent', function() {
+
+ it('returns the expected value', function() {
+ expect(lsc.getExtent()).to.eql([0, 8, 1, 9]);
+ });
+
+ });
+
+ describe('getIndices', function() {
+
+ it('returns the expected value', function() {
+ expect(lsc.getIndices()).to.arreql([0, 1, 2, 3, 3, 4]);
+ });
+
+ });
+
+ describe('remove', function() {
+
+ it('can remove the first line string', function() {
+ lsc.remove(0);
+ expect(lsc.getCount()).to.be(1);
+ expect(lsc.get(4)).to.eql([[4, 5], [6, 7], [8, 9]]);
+ expect(lsc.getIndices()).to.arreql([2, 3, 3, 4]);
+ });
+
+ it('can remove the second line string', function() {
+ lsc.remove(4);
+ expect(lsc.getCount()).to.be(1);
+ expect(lsc.get(0)).to.eql([[0, 1], [2, 3]]);
+ expect(lsc.getIndices()).to.arreql([0, 1]);
+ });
+
+ });
+
+ describe('usage examples', function() {
+
+ it('allows the first line string to be replaced', function() {
+ lsc.remove(0);
+ expect(lsc.getCount()).to.be(1);
+ expect(lsc.add([[10, 11], [12, 13]])).to.be(0);
+ expect(lsc.getCount()).to.be(2);
+ expect(lsc.get(0)).to.eql([[10, 11], [12, 13]]);
+ });
+
+ it('will allocate at the end of the array', function() {
+ lsc.remove(0);
+ expect(lsc.getCount()).to.be(1);
+ expect(lsc.add([[10, 11], [12, 13], [14, 15]])).to.be(10);
+ expect(lsc.getCount()).to.be(2);
+ expect(lsc.get(10)).to.eql([[10, 11], [12, 13], [14, 15]]);
+ expect(lsc.getIndices()).to.arreql([2, 3, 3, 4, 5, 6, 6, 7]);
+ });
+
+ });
+
+ });
+
+});
+
+
+goog.require('ol.geom2.LineStringCollection');
+goog.require('ol.extent');
+goog.require('ol.structs.Buffer');
diff --git a/test/spec/ol/geom2/pointcollection.test.js b/test/spec/ol/geom2/pointcollection.test.js
new file mode 100644
index 0000000000..52d7d33bd5
--- /dev/null
+++ b/test/spec/ol/geom2/pointcollection.test.js
@@ -0,0 +1,293 @@
+goog.provide('ol.test.geom2.PointCollection');
+
+
+describe('ol.geom2.PointCollection', function() {
+
+ describe('createEmpty', function() {
+
+ it('creates an empty instance with the specified capacity', function() {
+ var pc = ol.geom2.PointCollection.createEmpty(16);
+ expect(pc.getCount()).to.be(0);
+ expect(pc.buf.getArray()).to.have.length(32);
+ });
+
+ it('can create empty collections for higher dimensions', function() {
+ var pc = ol.geom2.PointCollection.createEmpty(16, 3);
+ expect(pc.getCount()).to.be(0);
+ expect(pc.buf.getArray()).to.have.length(48);
+ });
+
+ });
+
+ describe('pack', function() {
+
+ it('packs an empty array', function() {
+ var pc = ol.geom2.PointCollection.pack([]);
+ expect(pc.buf.getArray()).to.be.empty();
+ expect(pc.dim).to.be(2);
+ });
+
+ it('packs an empty array with a capacity', function() {
+ var pc = ol.geom2.PointCollection.pack([], 4);
+ expect(pc.buf.getArray()).to.eql([NaN, NaN, NaN, NaN]);
+ expect(pc.dim).to.be(2);
+ });
+
+ it('packs an empty array with a capacity and a dimension', function() {
+ var pc = ol.geom2.PointCollection.pack([], 8, 2);
+ expect(pc.buf.getArray()).to.eql(
+ [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN]);
+ expect(pc.dim).to.be(2);
+ });
+
+ it('packs a single point', function() {
+ var pc = ol.geom2.PointCollection.pack([[0, 1]]);
+ expect(pc.buf.getArray()).to.eql([0, 1]);
+ expect(pc.dim).to.be(2);
+ });
+
+ it('can pack multiple points', function() {
+ var pc = ol.geom2.PointCollection.pack([[0, 1], [2, 3], [4, 5]]);
+ expect(pc.buf.getArray()).to.eql([0, 1, 2, 3, 4, 5]);
+ expect(pc.dim).to.be(2);
+ });
+
+ it('can pack multiple points with a capacity', function() {
+ var pc = ol.geom2.PointCollection.pack([[0, 1], [2, 3], [4, 5]], 8);
+ expect(pc.buf.getArray()).to.eql([0, 1, 2, 3, 4, 5, NaN, NaN]);
+ expect(pc.dim).to.be(2);
+ });
+
+ it('can pack a single 3-dimensional point', function() {
+ var pc = ol.geom2.PointCollection.pack([[0, 1, 2]]);
+ expect(pc.buf.getArray()).to.eql([0, 1, 2]);
+ expect(pc.dim).to.be(3);
+ });
+
+ it('can pack a multiple 3-dimensional points', function() {
+ var pc = ol.geom2.PointCollection.pack([[0, 1, 2], [4, 5, 6]]);
+ expect(pc.buf.getArray()).to.eql([0, 1, 2, 4, 5, 6]);
+ expect(pc.dim).to.be(3);
+ });
+
+ it('raises an error when not all points have the same dimension',
+ function() {
+ expect(function() {
+ var pc = ol.geom2.PointCollection.pack([[0, 1], [2]]);
+ pc = pc; // suppress gjslint warning about unused variable
+ }).to.throwException();
+ });
+
+ it('raises an error when the capacity is too small', function() {
+ expect(function() {
+ var pc = ol.geom2.PointCollection.pack([[0, 1], [2, 3], [4, 5]], 2);
+ pc = pc; // suppress gjslint warning about unused variable
+ }).to.throwException();
+ });
+
+ });
+
+ describe('with an empty buffer, with capacity for two points', function() {
+
+ var pc;
+ beforeEach(function() {
+ var buf = new ol.structs.Buffer(new Array(4), 0);
+ pc = new ol.geom2.PointCollection(buf);
+ });
+
+ describe('add', function() {
+
+ it('can add a first point', function() {
+ expect(pc.add([0, 1])).to.be(0);
+ expect(pc.buf.getArray()).to.eql([0, 1, NaN, NaN]);
+ });
+
+ it('can add a second point', function() {
+ expect(pc.add([0, 1])).to.be(0);
+ expect(pc.buf.getArray()).to.eql([0, 1, NaN, NaN]);
+ expect(pc.add([2, 3])).to.be(2);
+ expect(pc.buf.getArray()).to.eql([0, 1, 2, 3]);
+ });
+
+ it('raises an error when the third point is added', function() {
+ expect(pc.add([0, 1])).to.be(0);
+ expect(pc.buf.getArray()).to.eql([0, 1, NaN, NaN]);
+ expect(pc.add([2, 3])).to.be(2);
+ expect(pc.buf.getArray()).to.eql([0, 1, 2, 3]);
+ expect(function() {
+ pc.add([4, 5]);
+ }).to.throwException();
+ });
+
+ it('raises an error if a point of the wrong dimension is added',
+ function() {
+ expect(function() {
+ pc.add([0, 1, 2]);
+ }).to.throwException();
+ });
+
+ });
+
+ describe('getCount', function() {
+
+ it('returns 0', function() {
+ expect(pc.getCount()).to.be(0);
+ });
+
+ });
+
+ describe('getExtent', function() {
+
+ it('returns an empty extent', function() {
+ expect(ol.extent.isEmpty(pc.getExtent())).to.be(true);
+ });
+
+ });
+
+ describe('unpack', function() {
+
+ it('returns an empty array', function() {
+ expect(pc.unpack()).to.be.empty();
+ });
+
+ });
+
+ });
+
+ describe('with a partially populated instance', function() {
+
+ var dirtySet, pc;
+ beforeEach(function() {
+ dirtySet = new ol.structs.IntegerSet();
+ pc = ol.geom2.PointCollection.pack([[0, 1], [2, 3]], 8);
+ pc.buf.addDirtySet(dirtySet);
+ });
+
+ describe('add', function() {
+
+ it('can add more points', function() {
+ expect(pc.add([4, 5])).to.be(4);
+ expect(pc.buf.getArray()).to.eql([0, 1, 2, 3, 4, 5, NaN, NaN]);
+ expect(pc.add([6, 7])).to.be(6);
+ expect(pc.buf.getArray()).to.eql([0, 1, 2, 3, 4, 5, 6, 7]);
+ });
+
+ });
+
+ describe('get', function() {
+
+ it('returns the expected value for the first point', function() {
+ expect(pc.get(0)).to.eql([0, 1]);
+ });
+
+ it('returns the expected value for the second point', function() {
+ expect(pc.get(2)).to.eql([2, 3]);
+ });
+
+ });
+
+ describe('getCount', function() {
+
+ it('returns the expected value', function() {
+ expect(pc.getCount()).to.be(2);
+ });
+
+ });
+
+ describe('getExtent', function() {
+
+ it('returns the expected value', function() {
+ var extent = pc.getExtent();
+ expect(extent).to.eql([0, 2, 1, 3]);
+ });
+
+ });
+
+ describe('remove', function() {
+
+ it('can remove the first point', function() {
+ pc.remove(0);
+ expect(pc.buf.getArray()).to.eql([NaN, NaN, 2, 3, NaN, NaN, NaN, NaN]);
+ });
+
+ it('can remove the second point', function() {
+ pc.remove(2);
+ expect(pc.buf.getArray()).to.eql([0, 1, NaN, NaN, NaN, NaN, NaN, NaN]);
+ });
+
+ });
+
+ describe('set', function() {
+
+ it('marks the updated elements as dirty', function() {
+ pc.set(2, [4, 5]);
+ expect(pc.buf.getArray()).to.eql([0, 1, 4, 5, NaN, NaN, NaN, NaN]);
+ expect(dirtySet.getArray()).to.eql([2, 4]);
+ });
+
+ });
+
+ describe('unpack', function() {
+
+ it('returns the expect value', function() {
+ expect(pc.unpack()).to.eql([[0, 1], [2, 3]]);
+ });
+
+ });
+
+ describe('after removing the first point', function() {
+
+ beforeEach(function() {
+ pc.remove(0);
+ });
+
+ describe('getCount', function() {
+
+ it('returns the expected value', function() {
+ expect(pc.getCount()).to.be(1);
+ });
+
+ });
+
+ describe('unpack', function() {
+
+ it('returns the expected value', function() {
+ expect(pc.unpack()).to.eql([[2, 3]]);
+ });
+
+ });
+
+ });
+
+ });
+
+ describe('usage example', function() {
+
+ it('works as expected', function() {
+ var pc = ol.geom2.PointCollection.pack([[0, 1], [2, 3], [4, 5]], 8);
+ var dirtySet = new ol.structs.IntegerSet();
+ pc.buf.addDirtySet(dirtySet);
+ expect(pc.buf.getArray()).to.eql([0, 1, 2, 3, 4, 5, NaN, NaN]);
+ expect(pc.unpack()).to.have.length(3);
+ expect(pc.getCount()).to.be(3);
+ expect(pc.get(2)).to.eql([2, 3]);
+ pc.remove(2);
+ expect(pc.buf.getArray()).to.eql([0, 1, NaN, NaN, 4, 5, NaN, NaN]);
+ expect(pc.unpack()).to.have.length(2);
+ expect(pc.getCount()).to.be(2);
+ expect(pc.add([6, 7])).to.be(2);
+ expect(pc.buf.getArray()).to.eql([0, 1, 6, 7, 4, 5, NaN, NaN]);
+ expect(pc.unpack()).to.have.length(3);
+ expect(pc.getCount()).to.be(3);
+ expect(dirtySet.getArray()).to.eql([2, 4]);
+ });
+
+ });
+
+});
+
+
+goog.require('ol.geom2.PointCollection');
+goog.require('ol.extent');
+goog.require('ol.structs.Buffer');
+goog.require('ol.structs.IntegerSet');
diff --git a/test/spec/ol/structs/buffer.test.js b/test/spec/ol/structs/buffer.test.js
index 2e3710cfa3..d91e37a4d7 100644
--- a/test/spec/ol/structs/buffer.test.js
+++ b/test/spec/ol/structs/buffer.test.js
@@ -220,6 +220,41 @@ describe('ol.structs.Buffer', function() {
});
+ describe('with a populated instance', function() {
+
+ var b;
+ beforeEach(function() {
+ b = new ol.structs.Buffer([1234567.1234567, -7654321.7654321]);
+ });
+
+ describe('getSplit32', function() {
+
+ it('returns the expected value', function() {
+ var split32 = b.getSplit32();
+ expect(split32).to.be.a(Float32Array);
+ expect(split32).to.have.length(4);
+ expect(split32[0]).to.roughlyEqual(1179648.0, 1e1);
+ expect(split32[1]).to.roughlyEqual(54919.12345670001, 1e-2);
+ expect(split32[2]).to.roughlyEqual(-7602176.0, 1e1);
+ expect(split32[3]).to.roughlyEqual(-52145.76543209981, 1e-2);
+ });
+
+ it('tracks updates', function() {
+ b.getSplit32();
+ b.getArray()[0] = 0;
+ b.markDirty(1, 0);
+ var split32 = b.getSplit32();
+ expect(split32).to.be.a(Float32Array);
+ expect(split32).to.have.length(4);
+ expect(split32[0]).to.be(0);
+ expect(split32[1]).to.be(0);
+ expect(split32[2]).to.roughlyEqual(-7602176.0, 1e1);
+ expect(split32[3]).to.roughlyEqual(-52145.76543209981, 1e-2);
+ });
+
+ });
+ });
+
describe('usage tests', function() {
it('allows multiple adds and removes', function() {