Merge pull request #2967 from camptocamp/webgl-point
Add support for drawing points with WebGL
This commit is contained in:
BIN
examples/data/Butterfly.png
Normal file
BIN
examples/data/Butterfly.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
@@ -38,6 +38,7 @@ var vector = new ol.layer.Vector({
|
||||
|
||||
var map = new ol.Map({
|
||||
layers: [raster, vector],
|
||||
renderer: exampleNS.getRendererFromQueryString(),
|
||||
target: 'map',
|
||||
view: new ol.View({
|
||||
center: [-11000000, 4600000],
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
goog.require('ol.Feature');
|
||||
goog.require('ol.Map');
|
||||
goog.require('ol.View');
|
||||
goog.require('ol.geom.MultiPoint');
|
||||
goog.require('ol.geom.Point');
|
||||
goog.require('ol.layer.Tile');
|
||||
goog.require('ol.source.MapQuest');
|
||||
goog.require('ol.style.Circle');
|
||||
goog.require('ol.style.Fill');
|
||||
goog.require('ol.style.Stroke');
|
||||
goog.require('ol.style.Style');
|
||||
|
||||
|
||||
var map = new ol.Map({
|
||||
@@ -14,6 +17,7 @@ var map = new ol.Map({
|
||||
source: new ol.source.MapQuest({layer: 'sat'})
|
||||
})
|
||||
],
|
||||
renderer: exampleNS.getRendererFromQueryString(),
|
||||
target: 'map',
|
||||
view: new ol.View({
|
||||
center: [0, 0],
|
||||
@@ -28,6 +32,20 @@ var imageStyle = new ol.style.Circle({
|
||||
stroke: new ol.style.Stroke({color: 'red', width: 1})
|
||||
});
|
||||
|
||||
var headInnerImageStyle = new ol.style.Style({
|
||||
image: new ol.style.Circle({
|
||||
radius: 2,
|
||||
snapToPixel: false,
|
||||
fill: new ol.style.Fill({color: 'blue'})
|
||||
})
|
||||
});
|
||||
|
||||
var headOuterImageStyle = new ol.style.Circle({
|
||||
radius: 5,
|
||||
snapToPixel: false,
|
||||
fill: new ol.style.Fill({color: 'black'})
|
||||
});
|
||||
|
||||
var n = 200;
|
||||
var omegaTheta = 30000; // Rotation period in ms
|
||||
var R = 7e6;
|
||||
@@ -48,6 +66,14 @@ map.on('postcompose', function(event) {
|
||||
vectorContext.setImageStyle(imageStyle);
|
||||
vectorContext.drawMultiPointGeometry(
|
||||
new ol.geom.MultiPoint(coordinates), null);
|
||||
|
||||
var headPoint = new ol.geom.Point(coordinates[coordinates.length - 1]);
|
||||
var headFeature = new ol.Feature(headPoint);
|
||||
vectorContext.drawFeature(headFeature, headInnerImageStyle);
|
||||
|
||||
vectorContext.setImageStyle(headOuterImageStyle);
|
||||
vectorContext.drawMultiPointGeometry(headPoint, null);
|
||||
|
||||
map.render();
|
||||
});
|
||||
map.render();
|
||||
|
||||
53
examples/icon-sprite-webgl.html
Normal file
53
examples/icon-sprite-webgl.html
Normal file
@@ -0,0 +1,53 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
|
||||
<link rel="stylesheet" href="../css/ol.css" type="text/css">
|
||||
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
|
||||
<link rel="stylesheet" href="../resources/layout.css" type="text/css">
|
||||
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
|
||||
<title>Icon sprites with WebGL example</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div id="map" class="map"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
|
||||
<div class="span12">
|
||||
<h4 id="title">Icon sprite with WebGL example</h4>
|
||||
<p id="shortdesc">Icon sprite with WebGL.</p>
|
||||
<div id="docs">
|
||||
<p>See the <a href="icon-sprite-webgl.js" target="_blank">icon-sprite-webgl.js source</a> to see how this is done.</p>
|
||||
<p>In this example a sprite image is used for the icon styles. Using a sprite is required to get good performance with WebGL.</p>
|
||||
</div>
|
||||
<div id="tags">webgl, icon, sprite, vector, point</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="../resources/jquery.min.js" type="text/javascript"></script>
|
||||
<script src="../resources/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
|
||||
<script src="../resources/example-behaviour.js" type="text/javascript"></script>
|
||||
<script src="loader.js?id=icon-sprite-webgl" type="text/javascript"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
111
examples/icon-sprite-webgl.js
Normal file
111
examples/icon-sprite-webgl.js
Normal file
@@ -0,0 +1,111 @@
|
||||
goog.require('ol.Feature');
|
||||
goog.require('ol.FeatureOverlay');
|
||||
goog.require('ol.Map');
|
||||
goog.require('ol.View');
|
||||
goog.require('ol.geom.Point');
|
||||
goog.require('ol.layer.Vector');
|
||||
goog.require('ol.source.Vector');
|
||||
goog.require('ol.style.Icon');
|
||||
goog.require('ol.style.Style');
|
||||
|
||||
|
||||
var iconInfo = [{
|
||||
offset: [0, 0],
|
||||
opacity: 1.0,
|
||||
rotateWithView: true,
|
||||
rotation: 0.0,
|
||||
scale: 1.0,
|
||||
size: [55, 55]
|
||||
}, {
|
||||
offset: [110, 86],
|
||||
opacity: 0.75,
|
||||
rotateWithView: false,
|
||||
rotation: Math.PI / 2.0,
|
||||
scale: 1.25,
|
||||
size: [55, 55]
|
||||
}, {
|
||||
offset: [55, 0],
|
||||
opacity: 0.5,
|
||||
rotateWithView: true,
|
||||
rotation: Math.PI / 3.0,
|
||||
scale: 1.5,
|
||||
size: [55, 86]
|
||||
}, {
|
||||
offset: [212, 0],
|
||||
opacity: 1.0,
|
||||
rotateWithView: true,
|
||||
rotation: 0.0,
|
||||
scale: 1.0,
|
||||
size: [44, 44]
|
||||
}];
|
||||
|
||||
var i;
|
||||
|
||||
var iconCount = iconInfo.length;
|
||||
var icons = new Array(iconCount);
|
||||
for (i = 0; i < iconCount; ++i) {
|
||||
var info = iconInfo[i];
|
||||
icons[i] = new ol.style.Icon({
|
||||
offset: info.offset,
|
||||
opacity: info.opacity,
|
||||
rotateWithView: info.rotateWithView,
|
||||
rotation: info.rotation,
|
||||
scale: info.scale,
|
||||
size: info.size,
|
||||
src: 'data/Butterfly.png'
|
||||
});
|
||||
}
|
||||
|
||||
var featureCount = 50000;
|
||||
var features = new Array(featureCount);
|
||||
var feature, geometry;
|
||||
var e = 25000000;
|
||||
for (i = 0; i < featureCount; ++i) {
|
||||
geometry = new ol.geom.Point(
|
||||
[2 * e * Math.random() - e, 2 * e * Math.random() - e]);
|
||||
feature = new ol.Feature(geometry);
|
||||
feature.setStyle(
|
||||
new ol.style.Style({
|
||||
image: icons[i % (iconCount - 1)]
|
||||
})
|
||||
);
|
||||
features[i] = feature;
|
||||
}
|
||||
|
||||
var vectorSource = new ol.source.Vector({
|
||||
features: features
|
||||
});
|
||||
var vector = new ol.layer.Vector({
|
||||
source: vectorSource
|
||||
});
|
||||
|
||||
// Use the "webgl" renderer by default.
|
||||
var renderer = exampleNS.getRendererFromQueryString();
|
||||
if (!renderer) {
|
||||
renderer = 'webgl';
|
||||
}
|
||||
|
||||
var map = new ol.Map({
|
||||
renderer: renderer,
|
||||
layers: [vector],
|
||||
target: document.getElementById('map'),
|
||||
view: new ol.View({
|
||||
center: [0, 0],
|
||||
zoom: 5
|
||||
})
|
||||
});
|
||||
|
||||
var overlayFeatures = [];
|
||||
for (i = 0; i < featureCount; i += 30) {
|
||||
var clone = features[i].clone();
|
||||
clone.setStyle(null);
|
||||
overlayFeatures.push(clone);
|
||||
}
|
||||
|
||||
var featureOverlay = new ol.FeatureOverlay({
|
||||
map: map,
|
||||
style: new ol.style.Style({
|
||||
image: icons[iconCount - 1]
|
||||
}),
|
||||
features: overlayFeatures
|
||||
});
|
||||
58
examples/symbol-atlas-webgl.html
Normal file
58
examples/symbol-atlas-webgl.html
Normal file
@@ -0,0 +1,58 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
|
||||
<link rel="stylesheet" href="../css/ol.css" type="text/css">
|
||||
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
|
||||
<link rel="stylesheet" href="../resources/layout.css" type="text/css">
|
||||
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
|
||||
<title>Symbols with WebGL example</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div id="map" class="map"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
|
||||
<div class="span12">
|
||||
<h4 id="title">Symbols with WebGL example</h4>
|
||||
<p id="shortdesc">Using symbols in an atlas with WebGL.</p>
|
||||
<div id="docs">
|
||||
<p>When using symbol styles with WebGL, OpenLayers would render the symbol
|
||||
on a temporary image and would create a WebGL texture for each image. For a
|
||||
better performance, it is recommended to use atlas images (similar to
|
||||
image sprites with CSS), so that the number of textures is reduced. OpenLayers
|
||||
provides an <code>AtlasManager</code>, which when passed to the constructor
|
||||
of a symbol style, will create atlases for the symbols.</p>
|
||||
<p>See the <a href="symbol-atlas-webgl.js" target="_blank">symbol-atlas-webgl.js source</a> to see how this is done.</p>
|
||||
</div>
|
||||
<div id="tags">webgl, symbol, atlas, vector, point</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="../resources/jquery.min.js" type="text/javascript"></script>
|
||||
<script src="../resources/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
|
||||
<script src="../resources/example-behaviour.js" type="text/javascript"></script>
|
||||
<script src="loader.js?id=symbol-atlas-webgl" type="text/javascript"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
123
examples/symbol-atlas-webgl.js
Normal file
123
examples/symbol-atlas-webgl.js
Normal file
@@ -0,0 +1,123 @@
|
||||
goog.require('ol.Feature');
|
||||
goog.require('ol.Map');
|
||||
goog.require('ol.View');
|
||||
goog.require('ol.geom.Point');
|
||||
goog.require('ol.layer.Vector');
|
||||
goog.require('ol.source.Vector');
|
||||
goog.require('ol.style.AtlasManager');
|
||||
goog.require('ol.style.Circle');
|
||||
goog.require('ol.style.Fill');
|
||||
goog.require('ol.style.RegularShape');
|
||||
goog.require('ol.style.Stroke');
|
||||
goog.require('ol.style.Style');
|
||||
|
||||
var atlasManager = new ol.style.AtlasManager({
|
||||
// we increase the initial size so that all symbols fit into
|
||||
// a single atlas image
|
||||
initialSize: 512
|
||||
});
|
||||
|
||||
var symbolInfo = [{
|
||||
opacity: 1.0,
|
||||
scale: 1.0,
|
||||
fillColor: 'rgba(255, 153, 0, 0.4)',
|
||||
strokeColor: 'rgba(255, 204, 0, 0.2)'
|
||||
}, {
|
||||
opacity: 0.75,
|
||||
scale: 1.25,
|
||||
fillColor: 'rgba(70, 80, 224, 0.4)',
|
||||
strokeColor: 'rgba(12, 21, 138, 0.2)'
|
||||
}, {
|
||||
opacity: 0.5,
|
||||
scale: 1.5,
|
||||
fillColor: 'rgba(66, 150, 79, 0.4)',
|
||||
strokeColor: 'rgba(20, 99, 32, 0.2)'
|
||||
}, {
|
||||
opacity: 1.0,
|
||||
scale: 1.0,
|
||||
fillColor: 'rgba(176, 61, 35, 0.4)',
|
||||
strokeColor: 'rgba(145, 43, 20, 0.2)'
|
||||
}];
|
||||
|
||||
var radiuses = [3, 6, 9, 15, 19, 25];
|
||||
var symbolCount = symbolInfo.length * radiuses.length * 2;
|
||||
var symbols = [];
|
||||
var i, j;
|
||||
for (i = 0; i < symbolInfo.length; ++i) {
|
||||
var info = symbolInfo[i];
|
||||
for (j = 0; j < radiuses.length; ++j) {
|
||||
// circle symbol
|
||||
symbols.push(new ol.style.Circle({
|
||||
opacity: info.opacity,
|
||||
scale: info.scale,
|
||||
radius: radiuses[j],
|
||||
fill: new ol.style.Fill({
|
||||
color: info.fillColor
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: info.strokeColor,
|
||||
width: 1
|
||||
}),
|
||||
// by passing the atlas manager to the symbol,
|
||||
// the symbol will be added to an atlas
|
||||
atlasManager: atlasManager
|
||||
}));
|
||||
|
||||
// star symbol
|
||||
symbols.push(new ol.style.RegularShape({
|
||||
points: 8,
|
||||
opacity: info.opacity,
|
||||
scale: info.scale,
|
||||
radius: radiuses[j],
|
||||
radius2: radiuses[j] * 0.7,
|
||||
angle: 1.4,
|
||||
fill: new ol.style.Fill({
|
||||
color: info.fillColor
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: info.strokeColor,
|
||||
width: 1
|
||||
}),
|
||||
atlasManager: atlasManager
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
var featureCount = 50000;
|
||||
var features = new Array(featureCount);
|
||||
var feature, geometry;
|
||||
var e = 25000000;
|
||||
for (i = 0; i < featureCount; ++i) {
|
||||
geometry = new ol.geom.Point(
|
||||
[2 * e * Math.random() - e, 2 * e * Math.random() - e]);
|
||||
feature = new ol.Feature(geometry);
|
||||
feature.setStyle(
|
||||
new ol.style.Style({
|
||||
image: symbols[i % symbolCount]
|
||||
})
|
||||
);
|
||||
features[i] = feature;
|
||||
}
|
||||
|
||||
var vectorSource = new ol.source.Vector({
|
||||
features: features
|
||||
});
|
||||
var vector = new ol.layer.Vector({
|
||||
source: vectorSource
|
||||
});
|
||||
|
||||
// Use the "webgl" renderer by default.
|
||||
var renderer = exampleNS.getRendererFromQueryString();
|
||||
if (!renderer) {
|
||||
renderer = 'webgl';
|
||||
}
|
||||
|
||||
var map = new ol.Map({
|
||||
renderer: renderer,
|
||||
layers: [vector],
|
||||
target: document.getElementById('map'),
|
||||
view: new ol.View({
|
||||
center: [0, 0],
|
||||
zoom: 4
|
||||
})
|
||||
});
|
||||
@@ -5438,7 +5438,8 @@ olx.style;
|
||||
* @typedef {{fill: (ol.style.Fill|undefined),
|
||||
* radius: number,
|
||||
* snapToPixel: (boolean|undefined),
|
||||
* stroke: (ol.style.Stroke|undefined)}}
|
||||
* stroke: (ol.style.Stroke|undefined),
|
||||
* atlasManager: (ol.style.AtlasManager|undefined)}}
|
||||
* @api
|
||||
*/
|
||||
olx.style.CircleOptions;
|
||||
@@ -5482,6 +5483,16 @@ olx.style.CircleOptions.prototype.snapToPixel;
|
||||
olx.style.CircleOptions.prototype.stroke;
|
||||
|
||||
|
||||
/**
|
||||
* The atlas manager to use for this circle. When using WebGL it is
|
||||
* recommended to use an atlas manager to avoid texture switching.
|
||||
* If an atlas manager is given, the circle is added to an atlas.
|
||||
* By default no atlas manager is used.
|
||||
* @type {ol.style.AtlasManager|undefined}
|
||||
*/
|
||||
olx.style.CircleOptions.prototype.atlasManager;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{color: (ol.Color|string|undefined)}}
|
||||
* @api
|
||||
@@ -5661,7 +5672,8 @@ olx.style.IconOptions.prototype.src;
|
||||
* radius2: (number|undefined),
|
||||
* angle: (number|undefined),
|
||||
* snapToPixel: (boolean|undefined),
|
||||
* stroke: (ol.style.Stroke|undefined)}}
|
||||
* stroke: (ol.style.Stroke|undefined),
|
||||
* atlasManager: (ol.style.AtlasManager|undefined)}}
|
||||
* @api
|
||||
*/
|
||||
olx.style.RegularShapeOptions;
|
||||
@@ -5740,6 +5752,16 @@ olx.style.RegularShapeOptions.prototype.snapToPixel;
|
||||
olx.style.RegularShapeOptions.prototype.stroke;
|
||||
|
||||
|
||||
/**
|
||||
* The atlas manager to use for this symbol. When using WebGL it is
|
||||
* recommended to use an atlas manager to avoid texture switching.
|
||||
* If an atlas manager is given, the symbol is added to an atlas.
|
||||
* By default no atlas manager is used.
|
||||
* @type {ol.style.AtlasManager|undefined}
|
||||
*/
|
||||
olx.style.RegularShapeOptions.prototype.atlasManager;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{color: (ol.Color|string|undefined),
|
||||
* lineCap: (string|undefined),
|
||||
@@ -6279,3 +6301,41 @@ olx.ViewState.prototype.resolution;
|
||||
* @api
|
||||
*/
|
||||
olx.ViewState.prototype.rotation;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{initialSize: (number|undefined),
|
||||
* maxSize: (number|undefined),
|
||||
* space: (number|undefined)}}
|
||||
* @api
|
||||
*/
|
||||
olx.style.AtlasManagerOptions;
|
||||
|
||||
|
||||
/**
|
||||
* The size in pixels of the first atlas image. If no value is given the
|
||||
* `ol.INITIAL_ATLAS_SIZE` compile-time constant will be used.
|
||||
* @type {number|undefined}
|
||||
* @api
|
||||
*/
|
||||
olx.style.AtlasManagerOptions.prototype.initialSize;
|
||||
|
||||
|
||||
/**
|
||||
* The maximum size in pixels of atlas images. If no value is given then
|
||||
* the `ol.MAX_ATLAS_SIZE` compile-time constant will be used. And if
|
||||
* `ol.MAX_ATLAS_SIZE` is set to `-1` (the default) then
|
||||
* `ol.WEBGL_MAX_TEXTURE_SIZE` will used if WebGL is supported. Otherwise
|
||||
* 2048 is used.
|
||||
* @type {number|undefined}
|
||||
* @api
|
||||
*/
|
||||
olx.style.AtlasManagerOptions.prototype.maxSize;
|
||||
|
||||
|
||||
/**
|
||||
* The space in pixels between images (default: 1).
|
||||
* @type {number|undefined}
|
||||
* @api
|
||||
*/
|
||||
olx.style.AtlasManagerOptions.prototype.space;
|
||||
|
||||
@@ -119,21 +119,32 @@ ol.has.MSPOINTER = !!(goog.global.navigator.msPointerEnabled);
|
||||
* @type {boolean}
|
||||
* @api stable
|
||||
*/
|
||||
ol.has.WEBGL = ol.ENABLE_WEBGL && (
|
||||
/**
|
||||
* @return {boolean} WebGL supported.
|
||||
*/
|
||||
function() {
|
||||
if (!('WebGLRenderingContext' in goog.global)) {
|
||||
return false;
|
||||
}
|
||||
ol.has.WEBGL;
|
||||
|
||||
|
||||
(function() {
|
||||
if (ol.ENABLE_WEBGL) {
|
||||
var hasWebGL = false;
|
||||
var textureSize;
|
||||
var /** @type {Array.<string>} */ extensions = [];
|
||||
|
||||
if ('WebGLRenderingContext' in goog.global) {
|
||||
try {
|
||||
var canvas = /** @type {HTMLCanvasElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.CANVAS));
|
||||
return !goog.isNull(ol.webgl.getContext(canvas, {
|
||||
var gl = ol.webgl.getContext(canvas, {
|
||||
failIfMajorPerformanceCaveat: true
|
||||
}));
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
});
|
||||
if (!goog.isNull(gl)) {
|
||||
hasWebGL = true;
|
||||
textureSize = /** @type {number} */
|
||||
(gl.getParameter(gl.MAX_TEXTURE_SIZE));
|
||||
extensions = gl.getSupportedExtensions();
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
ol.has.WEBGL = hasWebGL;
|
||||
ol.WEBGL_EXTENSIONS = extensions;
|
||||
ol.WEBGL_MAX_TEXTURE_SIZE = textureSize;
|
||||
}
|
||||
})();
|
||||
|
||||
33
src/ol/ol.js
33
src/ol/ol.js
@@ -152,6 +152,13 @@ ol.ENABLE_WEBGL = true;
|
||||
ol.LEGACY_IE_SUPPORT = false;
|
||||
|
||||
|
||||
/**
|
||||
* @define {number} The size in pixels of the first atlas image. Default is
|
||||
* `256`.
|
||||
*/
|
||||
ol.INITIAL_ATLAS_SIZE = 256;
|
||||
|
||||
|
||||
/**
|
||||
* The page is loaded using HTTPS.
|
||||
* @const
|
||||
@@ -175,6 +182,14 @@ ol.IS_LEGACY_IE = goog.userAgent.IE &&
|
||||
ol.KEYBOARD_PAN_DURATION = 100;
|
||||
|
||||
|
||||
/**
|
||||
* @define {number} The maximum size in pixels of atlas images. Default is
|
||||
* `-1`, meaning it is not used (and `ol.ol.WEBGL_MAX_TEXTURE_SIZE` is
|
||||
* used instead).
|
||||
*/
|
||||
ol.MAX_ATLAS_SIZE = -1;
|
||||
|
||||
|
||||
/**
|
||||
* @define {number} Maximum mouse wheel delta.
|
||||
*/
|
||||
@@ -219,6 +234,24 @@ ol.SIMPLIFY_TOLERANCE = 0.5;
|
||||
ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK = 1024;
|
||||
|
||||
|
||||
/**
|
||||
* The maximum supported WebGL texture size in pixels. If WebGL is not
|
||||
* supported, the value is set to `undefined`.
|
||||
* @const
|
||||
* @type {number|undefined}
|
||||
* @api
|
||||
*/
|
||||
ol.WEBGL_MAX_TEXTURE_SIZE; // value is set in `ol.has`
|
||||
|
||||
|
||||
/**
|
||||
* List of supported WebGL extensions.
|
||||
* @const
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
ol.WEBGL_EXTENSIONS; // value is set in `ol.has`
|
||||
|
||||
|
||||
/**
|
||||
* @define {number} Zoom slider animation duration.
|
||||
*/
|
||||
|
||||
@@ -2011,7 +2011,7 @@ ol.render.canvas.ReplayGroup.prototype.forEachGeometryAtPixel = function(
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* FIXME empty description for jsdoc
|
||||
*/
|
||||
ol.render.canvas.ReplayGroup.prototype.finish = function() {
|
||||
var zKey;
|
||||
|
||||
@@ -35,13 +35,6 @@ ol.render.IReplayGroup = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* FIXME empty description for jsdoc
|
||||
*/
|
||||
ol.render.IReplayGroup.prototype.finish = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number|undefined} zIndex Z index.
|
||||
* @param {ol.render.ReplayType} replayType Replay type.
|
||||
|
||||
@@ -5,8 +5,8 @@ goog.provide('ol.render.IVectorContext');
|
||||
|
||||
|
||||
/**
|
||||
* VectorContext interface. Currently implemented by
|
||||
* {@link ol.render.canvas.Immediate}
|
||||
* VectorContext interface. Implemented by
|
||||
* {@link ol.render.canvas.Immediate} and {@link ol.render.webgl.Immediate}.
|
||||
* @interface
|
||||
*/
|
||||
ol.render.IVectorContext = function() {
|
||||
@@ -15,7 +15,7 @@ ol.render.IVectorContext = function() {
|
||||
|
||||
/**
|
||||
* @param {number} zIndex Z index.
|
||||
* @param {function(ol.render.canvas.Immediate)} callback Callback.
|
||||
* @param {function(ol.render.IVectorContext)} callback Callback.
|
||||
*/
|
||||
ol.render.IVectorContext.prototype.drawAsync = function(zIndex, callback) {
|
||||
};
|
||||
|
||||
46
src/ol/render/webgl/webglimagecolor.glsl
Normal file
46
src/ol/render/webgl/webglimagecolor.glsl
Normal file
@@ -0,0 +1,46 @@
|
||||
//! NAMESPACE=ol.render.webgl.imagereplay.shader.Color
|
||||
//! CLASS=ol.render.webgl.imagereplay.shader.Color
|
||||
|
||||
|
||||
//! COMMON
|
||||
varying vec2 v_texCoord;
|
||||
varying float v_opacity;
|
||||
|
||||
//! VERTEX
|
||||
attribute vec2 a_position;
|
||||
attribute vec2 a_texCoord;
|
||||
attribute vec2 a_offsets;
|
||||
attribute float a_opacity;
|
||||
attribute float a_rotateWithView;
|
||||
|
||||
uniform mat4 u_projectionMatrix;
|
||||
uniform mat4 u_offsetScaleMatrix;
|
||||
uniform mat4 u_offsetRotateMatrix;
|
||||
|
||||
void main(void) {
|
||||
mat4 offsetMatrix = u_offsetScaleMatrix;
|
||||
if (a_rotateWithView == 1.0) {
|
||||
offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;
|
||||
}
|
||||
vec4 offsets = offsetMatrix * vec4(a_offsets, 0., 0.);
|
||||
gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.) + offsets;
|
||||
v_texCoord = a_texCoord;
|
||||
v_opacity = a_opacity;
|
||||
}
|
||||
|
||||
|
||||
//! FRAGMENT
|
||||
// @see https://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/filters/skia/SkiaImageFilterBuilder.cpp
|
||||
uniform mat4 u_colorMatrix;
|
||||
uniform float u_opacity;
|
||||
uniform sampler2D u_image;
|
||||
|
||||
void main(void) {
|
||||
vec4 texColor = texture2D(u_image, v_texCoord);
|
||||
float alpha = texColor.a * v_opacity * u_opacity;
|
||||
if (alpha == 0.0) {
|
||||
discard;
|
||||
}
|
||||
gl_FragColor.a = alpha;
|
||||
gl_FragColor.rgb = (u_colorMatrix * vec4(texColor.rgb, 1.)).rgb;
|
||||
}
|
||||
153
src/ol/render/webgl/webglimagecolorshader.js
Normal file
153
src/ol/render/webgl/webglimagecolorshader.js
Normal file
@@ -0,0 +1,153 @@
|
||||
// This file is automatically generated, do not edit
|
||||
goog.provide('ol.render.webgl.imagereplay.shader.Color');
|
||||
|
||||
goog.require('ol.webgl.shader');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {ol.webgl.shader.Fragment}
|
||||
* @struct
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.ColorFragment = function() {
|
||||
goog.base(this, ol.render.webgl.imagereplay.shader.ColorFragment.SOURCE);
|
||||
};
|
||||
goog.inherits(ol.render.webgl.imagereplay.shader.ColorFragment, ol.webgl.shader.Fragment);
|
||||
goog.addSingletonGetter(ol.render.webgl.imagereplay.shader.ColorFragment);
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.ColorFragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\nvarying float v_opacity;\n\n// @see https://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/filters/skia/SkiaImageFilterBuilder.cpp\nuniform mat4 u_colorMatrix;\nuniform float u_opacity;\nuniform sampler2D u_image;\n\nvoid main(void) {\n vec4 texColor = texture2D(u_image, v_texCoord);\n float alpha = texColor.a * v_opacity * u_opacity;\n if (alpha == 0.0) {\n discard;\n }\n gl_FragColor.a = alpha;\n gl_FragColor.rgb = (u_colorMatrix * vec4(texColor.rgb, 1.)).rgb;\n}\n';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.ColorFragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;varying float b;uniform mat4 k;uniform float l;uniform sampler2D m;void main(void){vec4 texColor=texture2D(m,a);float alpha=texColor.a*b*l;if(alpha==0.0){discard;}gl_FragColor.a=alpha;gl_FragColor.rgb=(k*vec4(texColor.rgb,1.)).rgb;}';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.ColorFragment.SOURCE = goog.DEBUG ?
|
||||
ol.render.webgl.imagereplay.shader.ColorFragment.DEBUG_SOURCE :
|
||||
ol.render.webgl.imagereplay.shader.ColorFragment.OPTIMIZED_SOURCE;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {ol.webgl.shader.Vertex}
|
||||
* @struct
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.ColorVertex = function() {
|
||||
goog.base(this, ol.render.webgl.imagereplay.shader.ColorVertex.SOURCE);
|
||||
};
|
||||
goog.inherits(ol.render.webgl.imagereplay.shader.ColorVertex, ol.webgl.shader.Vertex);
|
||||
goog.addSingletonGetter(ol.render.webgl.imagereplay.shader.ColorVertex);
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.ColorVertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\nvarying float v_opacity;\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nattribute vec2 a_offsets;\nattribute float a_opacity;\nattribute float a_rotateWithView;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\n\nvoid main(void) {\n mat4 offsetMatrix = u_offsetScaleMatrix;\n if (a_rotateWithView == 1.0) {\n offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n }\n vec4 offsets = offsetMatrix * vec4(a_offsets, 0., 0.);\n gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.) + offsets;\n v_texCoord = a_texCoord;\n v_opacity = a_opacity;\n}\n\n\n';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.ColorVertex.OPTIMIZED_SOURCE = 'varying vec2 a;varying float b;attribute vec2 c;attribute vec2 d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;void main(void){mat4 offsetMatrix=i;if(g==1.0){offsetMatrix=i*j;}vec4 offsets=offsetMatrix*vec4(e,0.,0.);gl_Position=h*vec4(c,0.,1.)+offsets;a=d;b=f;}';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.ColorVertex.SOURCE = goog.DEBUG ?
|
||||
ol.render.webgl.imagereplay.shader.ColorVertex.DEBUG_SOURCE :
|
||||
ol.render.webgl.imagereplay.shader.ColorVertex.OPTIMIZED_SOURCE;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {WebGLRenderingContext} gl GL.
|
||||
* @param {WebGLProgram} program Program.
|
||||
* @struct
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.Color.Locations = function(gl, program) {
|
||||
|
||||
/**
|
||||
* @type {WebGLUniformLocation}
|
||||
*/
|
||||
this.u_colorMatrix = gl.getUniformLocation(
|
||||
program, goog.DEBUG ? 'u_colorMatrix' : 'k');
|
||||
|
||||
/**
|
||||
* @type {WebGLUniformLocation}
|
||||
*/
|
||||
this.u_image = gl.getUniformLocation(
|
||||
program, goog.DEBUG ? 'u_image' : 'm');
|
||||
|
||||
/**
|
||||
* @type {WebGLUniformLocation}
|
||||
*/
|
||||
this.u_offsetRotateMatrix = gl.getUniformLocation(
|
||||
program, goog.DEBUG ? 'u_offsetRotateMatrix' : 'j');
|
||||
|
||||
/**
|
||||
* @type {WebGLUniformLocation}
|
||||
*/
|
||||
this.u_offsetScaleMatrix = gl.getUniformLocation(
|
||||
program, goog.DEBUG ? 'u_offsetScaleMatrix' : 'i');
|
||||
|
||||
/**
|
||||
* @type {WebGLUniformLocation}
|
||||
*/
|
||||
this.u_opacity = gl.getUniformLocation(
|
||||
program, goog.DEBUG ? 'u_opacity' : 'l');
|
||||
|
||||
/**
|
||||
* @type {WebGLUniformLocation}
|
||||
*/
|
||||
this.u_projectionMatrix = gl.getUniformLocation(
|
||||
program, goog.DEBUG ? 'u_projectionMatrix' : 'h');
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.a_offsets = gl.getAttribLocation(
|
||||
program, goog.DEBUG ? 'a_offsets' : 'e');
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.a_opacity = gl.getAttribLocation(
|
||||
program, goog.DEBUG ? 'a_opacity' : 'f');
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.a_position = gl.getAttribLocation(
|
||||
program, goog.DEBUG ? 'a_position' : 'c');
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.a_rotateWithView = gl.getAttribLocation(
|
||||
program, goog.DEBUG ? 'a_rotateWithView' : 'g');
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.a_texCoord = gl.getAttribLocation(
|
||||
program, goog.DEBUG ? 'a_texCoord' : 'd');
|
||||
};
|
||||
44
src/ol/render/webgl/webglimagedefault.glsl
Normal file
44
src/ol/render/webgl/webglimagedefault.glsl
Normal file
@@ -0,0 +1,44 @@
|
||||
//! NAMESPACE=ol.render.webgl.imagereplay.shader.Default
|
||||
//! CLASS=ol.render.webgl.imagereplay.shader.Default
|
||||
|
||||
|
||||
//! COMMON
|
||||
varying vec2 v_texCoord;
|
||||
varying float v_opacity;
|
||||
|
||||
//! VERTEX
|
||||
attribute vec2 a_position;
|
||||
attribute vec2 a_texCoord;
|
||||
attribute vec2 a_offsets;
|
||||
attribute float a_opacity;
|
||||
attribute float a_rotateWithView;
|
||||
|
||||
uniform mat4 u_projectionMatrix;
|
||||
uniform mat4 u_offsetScaleMatrix;
|
||||
uniform mat4 u_offsetRotateMatrix;
|
||||
|
||||
void main(void) {
|
||||
mat4 offsetMatrix = u_offsetScaleMatrix;
|
||||
if (a_rotateWithView == 1.0) {
|
||||
offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;
|
||||
}
|
||||
vec4 offsets = offsetMatrix * vec4(a_offsets, 0., 0.);
|
||||
gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.) + offsets;
|
||||
v_texCoord = a_texCoord;
|
||||
v_opacity = a_opacity;
|
||||
}
|
||||
|
||||
|
||||
//! FRAGMENT
|
||||
uniform float u_opacity;
|
||||
uniform sampler2D u_image;
|
||||
|
||||
void main(void) {
|
||||
vec4 texColor = texture2D(u_image, v_texCoord);
|
||||
gl_FragColor.rgb = texColor.rgb;
|
||||
float alpha = texColor.a * v_opacity * u_opacity;
|
||||
if (alpha == 0.0) {
|
||||
discard;
|
||||
}
|
||||
gl_FragColor.a = alpha;
|
||||
}
|
||||
147
src/ol/render/webgl/webglimagedefaultshader.js
Normal file
147
src/ol/render/webgl/webglimagedefaultshader.js
Normal file
@@ -0,0 +1,147 @@
|
||||
// This file is automatically generated, do not edit
|
||||
goog.provide('ol.render.webgl.imagereplay.shader.Default');
|
||||
|
||||
goog.require('ol.webgl.shader');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {ol.webgl.shader.Fragment}
|
||||
* @struct
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.DefaultFragment = function() {
|
||||
goog.base(this, ol.render.webgl.imagereplay.shader.DefaultFragment.SOURCE);
|
||||
};
|
||||
goog.inherits(ol.render.webgl.imagereplay.shader.DefaultFragment, ol.webgl.shader.Fragment);
|
||||
goog.addSingletonGetter(ol.render.webgl.imagereplay.shader.DefaultFragment);
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.DefaultFragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\nvarying float v_opacity;\n\nuniform float u_opacity;\nuniform sampler2D u_image;\n\nvoid main(void) {\n vec4 texColor = texture2D(u_image, v_texCoord);\n gl_FragColor.rgb = texColor.rgb;\n float alpha = texColor.a * v_opacity * u_opacity;\n if (alpha == 0.0) {\n discard;\n }\n gl_FragColor.a = alpha;\n}\n';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.DefaultFragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;varying float b;uniform float k;uniform sampler2D l;void main(void){vec4 texColor=texture2D(l,a);gl_FragColor.rgb=texColor.rgb;float alpha=texColor.a*b*k;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.DefaultFragment.SOURCE = goog.DEBUG ?
|
||||
ol.render.webgl.imagereplay.shader.DefaultFragment.DEBUG_SOURCE :
|
||||
ol.render.webgl.imagereplay.shader.DefaultFragment.OPTIMIZED_SOURCE;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {ol.webgl.shader.Vertex}
|
||||
* @struct
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.DefaultVertex = function() {
|
||||
goog.base(this, ol.render.webgl.imagereplay.shader.DefaultVertex.SOURCE);
|
||||
};
|
||||
goog.inherits(ol.render.webgl.imagereplay.shader.DefaultVertex, ol.webgl.shader.Vertex);
|
||||
goog.addSingletonGetter(ol.render.webgl.imagereplay.shader.DefaultVertex);
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.DefaultVertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\nvarying float v_opacity;\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nattribute vec2 a_offsets;\nattribute float a_opacity;\nattribute float a_rotateWithView;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\n\nvoid main(void) {\n mat4 offsetMatrix = u_offsetScaleMatrix;\n if (a_rotateWithView == 1.0) {\n offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n }\n vec4 offsets = offsetMatrix * vec4(a_offsets, 0., 0.);\n gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.) + offsets;\n v_texCoord = a_texCoord;\n v_opacity = a_opacity;\n}\n\n\n';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.DefaultVertex.OPTIMIZED_SOURCE = 'varying vec2 a;varying float b;attribute vec2 c;attribute vec2 d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;void main(void){mat4 offsetMatrix=i;if(g==1.0){offsetMatrix=i*j;}vec4 offsets=offsetMatrix*vec4(e,0.,0.);gl_Position=h*vec4(c,0.,1.)+offsets;a=d;b=f;}';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.DefaultVertex.SOURCE = goog.DEBUG ?
|
||||
ol.render.webgl.imagereplay.shader.DefaultVertex.DEBUG_SOURCE :
|
||||
ol.render.webgl.imagereplay.shader.DefaultVertex.OPTIMIZED_SOURCE;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {WebGLRenderingContext} gl GL.
|
||||
* @param {WebGLProgram} program Program.
|
||||
* @struct
|
||||
*/
|
||||
ol.render.webgl.imagereplay.shader.Default.Locations = function(gl, program) {
|
||||
|
||||
/**
|
||||
* @type {WebGLUniformLocation}
|
||||
*/
|
||||
this.u_image = gl.getUniformLocation(
|
||||
program, goog.DEBUG ? 'u_image' : 'l');
|
||||
|
||||
/**
|
||||
* @type {WebGLUniformLocation}
|
||||
*/
|
||||
this.u_offsetRotateMatrix = gl.getUniformLocation(
|
||||
program, goog.DEBUG ? 'u_offsetRotateMatrix' : 'j');
|
||||
|
||||
/**
|
||||
* @type {WebGLUniformLocation}
|
||||
*/
|
||||
this.u_offsetScaleMatrix = gl.getUniformLocation(
|
||||
program, goog.DEBUG ? 'u_offsetScaleMatrix' : 'i');
|
||||
|
||||
/**
|
||||
* @type {WebGLUniformLocation}
|
||||
*/
|
||||
this.u_opacity = gl.getUniformLocation(
|
||||
program, goog.DEBUG ? 'u_opacity' : 'k');
|
||||
|
||||
/**
|
||||
* @type {WebGLUniformLocation}
|
||||
*/
|
||||
this.u_projectionMatrix = gl.getUniformLocation(
|
||||
program, goog.DEBUG ? 'u_projectionMatrix' : 'h');
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.a_offsets = gl.getAttribLocation(
|
||||
program, goog.DEBUG ? 'a_offsets' : 'e');
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.a_opacity = gl.getAttribLocation(
|
||||
program, goog.DEBUG ? 'a_opacity' : 'f');
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.a_position = gl.getAttribLocation(
|
||||
program, goog.DEBUG ? 'a_position' : 'c');
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.a_rotateWithView = gl.getAttribLocation(
|
||||
program, goog.DEBUG ? 'a_rotateWithView' : 'g');
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.a_texCoord = gl.getAttribLocation(
|
||||
program, goog.DEBUG ? 'a_texCoord' : 'd');
|
||||
};
|
||||
@@ -1,4 +1,8 @@
|
||||
goog.provide('ol.render.webgl.Immediate');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.object');
|
||||
goog.require('ol.extent');
|
||||
goog.require('ol.render.webgl.ReplayGroup');
|
||||
|
||||
|
||||
|
||||
@@ -6,22 +10,103 @@ goog.provide('ol.render.webgl.Immediate');
|
||||
* @constructor
|
||||
* @implements {ol.render.IVectorContext}
|
||||
* @param {ol.webgl.Context} context Context.
|
||||
* @param {ol.Coordinate} center Center.
|
||||
* @param {number} resolution Resolution.
|
||||
* @param {number} rotation Rotation.
|
||||
* @param {ol.Size} size Size.
|
||||
* @param {ol.Extent} extent Extent.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @struct
|
||||
*/
|
||||
ol.render.webgl.Immediate = function(context, pixelRatio) {
|
||||
ol.render.webgl.Immediate = function(context,
|
||||
center, resolution, rotation, size, extent, pixelRatio) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this.context_ = context;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this.center_ = center;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this.extent_ = extent;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this.pixelRatio_ = pixelRatio;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this.size_ = size;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this.rotation_ = rotation;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this.resolution_ = resolution;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.style.Image}
|
||||
*/
|
||||
this.imageStyle_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Object.<string,
|
||||
* Array.<function(ol.render.webgl.Immediate)>>}
|
||||
*/
|
||||
this.callbacksByZIndex_ = {};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* FIXME: empty description for jsdoc
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.flush = function() {
|
||||
/** @type {Array.<number>} */
|
||||
var zs = goog.array.map(goog.object.getKeys(this.callbacksByZIndex_), Number);
|
||||
goog.array.sort(zs);
|
||||
var i, ii, callbacks, j, jj;
|
||||
for (i = 0, ii = zs.length; i < ii; ++i) {
|
||||
callbacks = this.callbacksByZIndex_[zs[i].toString()];
|
||||
for (j = 0, jj = callbacks.length; j < jj; ++j) {
|
||||
callbacks[j](this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} zIndex Z index.
|
||||
* @param {function(ol.render.webgl.Immediate)} callback Callback.
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.drawAsync = function(zIndex, callback) {
|
||||
var zIndexKey = zIndex.toString();
|
||||
var callbacks = this.callbacksByZIndex_[zIndexKey];
|
||||
if (goog.isDef(callbacks)) {
|
||||
callbacks.push(callback);
|
||||
} else {
|
||||
this.callbacksByZIndex_[zIndexKey] = [callback];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.drawCircleGeometry =
|
||||
function(circleGeometry, data) {
|
||||
@@ -30,29 +115,83 @@ ol.render.webgl.Immediate.prototype.drawCircleGeometry =
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.drawFeature = function(feature, style) {
|
||||
var geometry = feature.getGeometry();
|
||||
if (!goog.isDefAndNotNull(geometry) ||
|
||||
!ol.extent.intersects(this.extent_, geometry.getExtent())) {
|
||||
return;
|
||||
}
|
||||
var zIndex = style.getZIndex();
|
||||
if (!goog.isDef(zIndex)) {
|
||||
zIndex = 0;
|
||||
}
|
||||
this.drawAsync(zIndex, function(render) {
|
||||
render.setFillStrokeStyle(style.getFill(), style.getStroke());
|
||||
render.setImageStyle(style.getImage());
|
||||
render.setTextStyle(style.getText());
|
||||
var type = geometry.getType();
|
||||
var renderGeometry = ol.render.webgl.Immediate.GEOMETRY_RENDERERS_[type];
|
||||
// Do not assert since all kinds of geometries are not handled yet.
|
||||
// In spite, render what we support.
|
||||
if (renderGeometry) {
|
||||
renderGeometry.call(render, geometry, null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.drawGeometryCollectionGeometry =
|
||||
function(geometryCollectionGeometry, data) {
|
||||
var geometries = geometryCollectionGeometry.getGeometriesArray();
|
||||
var renderers = ol.render.webgl.Immediate.GEOMETRY_RENDERERS_;
|
||||
var i, ii;
|
||||
for (i = 0, ii = geometries.length; i < ii; ++i) {
|
||||
var geometry = geometries[i];
|
||||
var geometryRenderer = renderers[geometry.getType()];
|
||||
// Do not assert since all kinds of geometries are not handled yet.
|
||||
// In order to support hierarchies, delegate instead what we can to
|
||||
// valid renderers.
|
||||
if (geometryRenderer) {
|
||||
geometryRenderer.call(this, geometry, data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.drawPointGeometry =
|
||||
function(pointGeometry, data) {
|
||||
var context = this.context_;
|
||||
var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_);
|
||||
var replay = replayGroup.getReplay(0, ol.render.ReplayType.IMAGE);
|
||||
replay.setImageStyle(this.imageStyle_);
|
||||
replay.drawPointGeometry(pointGeometry, data);
|
||||
replay.finish(context);
|
||||
// default colors
|
||||
var opacity = 1;
|
||||
var brightness = 0;
|
||||
var contrast = 1;
|
||||
var hue = 0;
|
||||
var saturation = 1;
|
||||
replay.replay(this.context_, this.center_, this.resolution_, this.rotation_,
|
||||
this.size_, this.extent_, this.pixelRatio_, opacity, brightness,
|
||||
contrast, hue, saturation, {});
|
||||
replay.getDeleteResourcesFunction(context)();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.drawLineStringGeometry =
|
||||
function(lineStringGeometry, data) {
|
||||
@@ -61,6 +200,7 @@ ol.render.webgl.Immediate.prototype.drawLineStringGeometry =
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.drawMultiLineStringGeometry =
|
||||
function(multiLineStringGeometry, data) {
|
||||
@@ -69,14 +209,32 @@ ol.render.webgl.Immediate.prototype.drawMultiLineStringGeometry =
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.drawMultiPointGeometry =
|
||||
function(multiPointGeometry, data) {
|
||||
var context = this.context_;
|
||||
var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_);
|
||||
var replay = replayGroup.getReplay(0, ol.render.ReplayType.IMAGE);
|
||||
replay.setImageStyle(this.imageStyle_);
|
||||
replay.drawMultiPointGeometry(multiPointGeometry, data);
|
||||
replay.finish(context);
|
||||
// default colors
|
||||
var opacity = 1;
|
||||
var brightness = 0;
|
||||
var contrast = 1;
|
||||
var hue = 0;
|
||||
var saturation = 1;
|
||||
replay.replay(this.context_, this.center_, this.resolution_, this.rotation_,
|
||||
this.size_, this.extent_, this.pixelRatio_, opacity, brightness,
|
||||
contrast, hue, saturation, {});
|
||||
replay.getDeleteResourcesFunction(context)();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.drawMultiPolygonGeometry =
|
||||
function(multiPolygonGeometry, data) {
|
||||
@@ -85,6 +243,7 @@ ol.render.webgl.Immediate.prototype.drawMultiPolygonGeometry =
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.drawPolygonGeometry =
|
||||
function(polygonGeometry, data) {
|
||||
@@ -93,6 +252,7 @@ ol.render.webgl.Immediate.prototype.drawPolygonGeometry =
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.drawText =
|
||||
function(flatCoordinates, offset, end, stride, geometry, data) {
|
||||
@@ -101,6 +261,7 @@ ol.render.webgl.Immediate.prototype.drawText =
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.setFillStrokeStyle =
|
||||
function(fillStyle, strokeStyle) {
|
||||
@@ -109,13 +270,31 @@ ol.render.webgl.Immediate.prototype.setFillStrokeStyle =
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.setImageStyle = function(imageStyle) {
|
||||
this.imageStyle_ = imageStyle;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
ol.render.webgl.Immediate.prototype.setTextStyle = function(textStyle) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private
|
||||
* @type {Object.<ol.geom.GeometryType,
|
||||
* function(this: ol.render.webgl.Immediate, ol.geom.Geometry,
|
||||
* Object)>}
|
||||
*/
|
||||
ol.render.webgl.Immediate.GEOMETRY_RENDERERS_ = {
|
||||
'Point': ol.render.webgl.Immediate.prototype.drawPointGeometry,
|
||||
'MultiPoint': ol.render.webgl.Immediate.prototype.drawMultiPointGeometry,
|
||||
'GeometryCollection':
|
||||
ol.render.webgl.Immediate.prototype.drawGeometryCollectionGeometry
|
||||
};
|
||||
|
||||
841
src/ol/render/webgl/webglreplay.js
Normal file
841
src/ol/render/webgl/webglreplay.js
Normal file
@@ -0,0 +1,841 @@
|
||||
goog.provide('ol.render.webgl.ImageReplay');
|
||||
goog.provide('ol.render.webgl.ReplayGroup');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.functions');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.vec.Mat4');
|
||||
goog.require('ol.color.Matrix');
|
||||
goog.require('ol.extent');
|
||||
goog.require('ol.render.IReplayGroup');
|
||||
goog.require('ol.render.webgl.imagereplay.shader.Color');
|
||||
goog.require('ol.render.webgl.imagereplay.shader.Default');
|
||||
goog.require('ol.vec.Mat4');
|
||||
goog.require('ol.webgl.Buffer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @implements {ol.render.IVectorContext}
|
||||
* @param {number} tolerance Tolerance.
|
||||
* @param {ol.Extent} maxExtent Max extent.
|
||||
* @protected
|
||||
* @struct
|
||||
*/
|
||||
ol.render.webgl.ImageReplay = function(tolerance, maxExtent) {
|
||||
|
||||
/**
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.anchorX_ = undefined;
|
||||
|
||||
/**
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.anchorY_ = undefined;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.color.Matrix}
|
||||
*/
|
||||
this.colorMatrix_ = new ol.color.Matrix();
|
||||
|
||||
/**
|
||||
* The origin of the coordinate system for the point coordinates sent to
|
||||
* the GPU. To eliminate jitter caused by precision problems in the GPU
|
||||
* we use the "Rendering Relative to Eye" technique described in the "3D
|
||||
* Engine Design for Virtual Globes" book.
|
||||
* @private
|
||||
* @type {ol.Coordinate}
|
||||
*/
|
||||
this.origin_ = ol.extent.getCenter(maxExtent);
|
||||
|
||||
/**
|
||||
* @type {ol.Extent}
|
||||
* @private
|
||||
*/
|
||||
this.extent_ = ol.extent.createEmpty();
|
||||
|
||||
/**
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.groupIndices_ = [];
|
||||
|
||||
/**
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.height_ = undefined;
|
||||
|
||||
/**
|
||||
* @type {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>}
|
||||
* @private
|
||||
*/
|
||||
this.images_ = [];
|
||||
|
||||
/**
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.imageHeight_ = undefined;
|
||||
|
||||
/**
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.imageWidth_ = undefined;
|
||||
|
||||
/**
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.indices_ = [];
|
||||
|
||||
/**
|
||||
* @type {ol.webgl.Buffer}
|
||||
* @private
|
||||
*/
|
||||
this.indicesBuffer_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.render.webgl.imagereplay.shader.Color.Locations}
|
||||
*/
|
||||
this.colorLocations_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.render.webgl.imagereplay.shader.Default.Locations}
|
||||
*/
|
||||
this.defaultLocations_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
this.opacity_ = undefined;
|
||||
|
||||
/**
|
||||
* @type {!goog.vec.Mat4.Number}
|
||||
* @private
|
||||
*/
|
||||
this.offsetRotateMatrix_ = goog.vec.Mat4.createNumberIdentity();
|
||||
|
||||
/**
|
||||
* @type {!goog.vec.Mat4.Number}
|
||||
* @private
|
||||
*/
|
||||
this.offsetScaleMatrix_ = goog.vec.Mat4.createNumberIdentity();
|
||||
|
||||
/**
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.originX_ = undefined;
|
||||
|
||||
/**
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.originY_ = undefined;
|
||||
|
||||
/**
|
||||
* @type {!goog.vec.Mat4.Number}
|
||||
* @private
|
||||
*/
|
||||
this.projectionMatrix_ = goog.vec.Mat4.createNumberIdentity();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {boolean|undefined}
|
||||
*/
|
||||
this.rotateWithView_ = undefined;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
this.rotation_ = undefined;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
this.scale_ = undefined;
|
||||
|
||||
/**
|
||||
* @type {Array.<WebGLTexture>}
|
||||
* @private
|
||||
*/
|
||||
this.textures_ = [];
|
||||
|
||||
/**
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.vertices_ = [];
|
||||
|
||||
/**
|
||||
* @type {ol.webgl.Buffer}
|
||||
* @private
|
||||
*/
|
||||
this.verticesBuffer_ = null;
|
||||
|
||||
/**
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.width_ = undefined;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.webgl.Context} context WebGL context.
|
||||
* @return {function()} Delete resources function.
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.getDeleteResourcesFunction =
|
||||
function(context) {
|
||||
// We only delete our stuff here. The shaders and the program may
|
||||
// be used by other ImageReplay instances (for other layers). And
|
||||
// they will be deleted when disposing of the ol.webgl.Context
|
||||
// object.
|
||||
goog.asserts.assert(!goog.isNull(this.verticesBuffer_));
|
||||
goog.asserts.assert(!goog.isNull(this.indicesBuffer_));
|
||||
var verticesBuffer = this.verticesBuffer_;
|
||||
var indicesBuffer = this.indicesBuffer_;
|
||||
var textures = this.textures_;
|
||||
var gl = context.getGL();
|
||||
return function() {
|
||||
if (!gl.isContextLost()) {
|
||||
var i, ii;
|
||||
for (i = 0, ii = textures.length; i < ii; ++i) {
|
||||
gl.deleteTexture(textures[i]);
|
||||
}
|
||||
}
|
||||
context.deleteBuffer(verticesBuffer);
|
||||
context.deleteBuffer(indicesBuffer);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawAsync = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @param {Array.<number>} flatCoordinates Flat coordinates.
|
||||
* @param {number} offset Offset.
|
||||
* @param {number} end End.
|
||||
* @param {number} stride Stride.
|
||||
* @return {number} My end.
|
||||
* @private
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawCoordinates_ =
|
||||
function(flatCoordinates, offset, end, stride) {
|
||||
goog.asserts.assert(goog.isDef(this.anchorX_));
|
||||
goog.asserts.assert(goog.isDef(this.anchorY_));
|
||||
goog.asserts.assert(goog.isDef(this.height_));
|
||||
goog.asserts.assert(goog.isDef(this.imageHeight_));
|
||||
goog.asserts.assert(goog.isDef(this.imageWidth_));
|
||||
goog.asserts.assert(goog.isDef(this.opacity_));
|
||||
goog.asserts.assert(goog.isDef(this.originX_));
|
||||
goog.asserts.assert(goog.isDef(this.originY_));
|
||||
goog.asserts.assert(goog.isDef(this.rotateWithView_));
|
||||
goog.asserts.assert(goog.isDef(this.rotation_));
|
||||
goog.asserts.assert(goog.isDef(this.scale_));
|
||||
goog.asserts.assert(goog.isDef(this.width_));
|
||||
var anchorX = this.anchorX_;
|
||||
var anchorY = this.anchorY_;
|
||||
var height = this.height_;
|
||||
var imageHeight = this.imageHeight_;
|
||||
var imageWidth = this.imageWidth_;
|
||||
var opacity = this.opacity_;
|
||||
var originX = this.originX_;
|
||||
var originY = this.originY_;
|
||||
var rotateWithView = this.rotateWithView_ ? 1.0 : 0.0;
|
||||
var rotation = this.rotation_;
|
||||
var scale = this.scale_;
|
||||
var width = this.width_;
|
||||
var cos = Math.cos(rotation);
|
||||
var sin = Math.sin(rotation);
|
||||
var numIndices = this.indices_.length;
|
||||
var numVertices = this.vertices_.length;
|
||||
var i, n, offsetX, offsetY, x, y;
|
||||
for (i = offset; i < end; i += stride) {
|
||||
x = flatCoordinates[i] - this.origin_[0];
|
||||
y = flatCoordinates[i + 1] - this.origin_[1];
|
||||
|
||||
// There are 4 vertices per [x, y] point, one for each corner of the
|
||||
// rectangle we're going to draw. We'd use 1 vertex per [x, y] point if
|
||||
// WebGL supported Geometry Shaders (which can emit new vertices), but that
|
||||
// is not currently the case.
|
||||
//
|
||||
// And each vertex includes 8 values: the x and y coordinates, the x and
|
||||
// y offsets used to calculate the position of the corner, the u and
|
||||
// v texture coordinates for the corner, the opacity, and whether the
|
||||
// the image should be rotated with the view (rotateWithView).
|
||||
|
||||
n = numVertices / 8;
|
||||
|
||||
// bottom-left corner
|
||||
offsetX = -scale * anchorX;
|
||||
offsetY = -scale * (height - anchorY);
|
||||
this.vertices_[numVertices++] = x;
|
||||
this.vertices_[numVertices++] = y;
|
||||
this.vertices_[numVertices++] = offsetX * cos - offsetY * sin;
|
||||
this.vertices_[numVertices++] = offsetX * sin + offsetY * cos;
|
||||
this.vertices_[numVertices++] = originX / imageWidth;
|
||||
this.vertices_[numVertices++] = (originY + height) / imageHeight;
|
||||
this.vertices_[numVertices++] = opacity;
|
||||
this.vertices_[numVertices++] = rotateWithView;
|
||||
|
||||
// bottom-right corner
|
||||
offsetX = scale * (width - anchorX);
|
||||
offsetY = -scale * (height - anchorY);
|
||||
this.vertices_[numVertices++] = x;
|
||||
this.vertices_[numVertices++] = y;
|
||||
this.vertices_[numVertices++] = offsetX * cos - offsetY * sin;
|
||||
this.vertices_[numVertices++] = offsetX * sin + offsetY * cos;
|
||||
this.vertices_[numVertices++] = (originX + width) / imageWidth;
|
||||
this.vertices_[numVertices++] = (originY + height) / imageHeight;
|
||||
this.vertices_[numVertices++] = opacity;
|
||||
this.vertices_[numVertices++] = rotateWithView;
|
||||
|
||||
// top-right corner
|
||||
offsetX = scale * (width - anchorX);
|
||||
offsetY = scale * anchorY;
|
||||
this.vertices_[numVertices++] = x;
|
||||
this.vertices_[numVertices++] = y;
|
||||
this.vertices_[numVertices++] = offsetX * cos - offsetY * sin;
|
||||
this.vertices_[numVertices++] = offsetX * sin + offsetY * cos;
|
||||
this.vertices_[numVertices++] = (originX + width) / imageWidth;
|
||||
this.vertices_[numVertices++] = originY / imageHeight;
|
||||
this.vertices_[numVertices++] = opacity;
|
||||
this.vertices_[numVertices++] = rotateWithView;
|
||||
|
||||
// top-left corner
|
||||
offsetX = -scale * anchorX;
|
||||
offsetY = scale * anchorY;
|
||||
this.vertices_[numVertices++] = x;
|
||||
this.vertices_[numVertices++] = y;
|
||||
this.vertices_[numVertices++] = offsetX * cos - offsetY * sin;
|
||||
this.vertices_[numVertices++] = offsetX * sin + offsetY * cos;
|
||||
this.vertices_[numVertices++] = originX / imageWidth;
|
||||
this.vertices_[numVertices++] = originY / imageHeight;
|
||||
this.vertices_[numVertices++] = opacity;
|
||||
this.vertices_[numVertices++] = rotateWithView;
|
||||
|
||||
this.indices_[numIndices++] = n;
|
||||
this.indices_[numIndices++] = n + 1;
|
||||
this.indices_[numIndices++] = n + 2;
|
||||
this.indices_[numIndices++] = n;
|
||||
this.indices_[numIndices++] = n + 2;
|
||||
this.indices_[numIndices++] = n + 3;
|
||||
}
|
||||
|
||||
return numVertices;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawCircleGeometry = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawFeature = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawGeometryCollectionGeometry =
|
||||
goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawLineStringGeometry =
|
||||
goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawMultiLineStringGeometry =
|
||||
goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawMultiPointGeometry =
|
||||
function(multiPointGeometry, data) {
|
||||
ol.extent.extend(this.extent_, multiPointGeometry.getExtent());
|
||||
var flatCoordinates = multiPointGeometry.getFlatCoordinates();
|
||||
var stride = multiPointGeometry.getStride();
|
||||
this.drawCoordinates_(
|
||||
flatCoordinates, 0, flatCoordinates.length, stride);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawMultiPolygonGeometry =
|
||||
goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawPointGeometry =
|
||||
function(pointGeometry, data) {
|
||||
ol.extent.extend(this.extent_, pointGeometry.getExtent());
|
||||
var flatCoordinates = pointGeometry.getFlatCoordinates();
|
||||
var stride = pointGeometry.getStride();
|
||||
this.drawCoordinates_(
|
||||
flatCoordinates, 0, flatCoordinates.length, stride);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawPolygonGeometry = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawText = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.webgl.Context} context Context.
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.finish = function(context) {
|
||||
var gl = context.getGL();
|
||||
|
||||
this.groupIndices_.push(this.indices_.length);
|
||||
goog.asserts.assert(this.images_.length == this.groupIndices_.length);
|
||||
|
||||
// create, bind, and populate the vertices buffer
|
||||
this.verticesBuffer_ = new ol.webgl.Buffer(this.vertices_);
|
||||
context.bindBuffer(goog.webgl.ARRAY_BUFFER, this.verticesBuffer_);
|
||||
|
||||
var indices = this.indices_;
|
||||
var bits = context.hasOESElementIndexUint ? 32 : 16;
|
||||
goog.asserts.assert(indices[indices.length - 1] < Math.pow(2, bits),
|
||||
'Too large element index detected [%s] (OES_element_index_uint "%s")',
|
||||
indices[indices.length - 1], context.hasOESElementIndexUint);
|
||||
|
||||
// create, bind, and populate the indices buffer
|
||||
this.indicesBuffer_ = new ol.webgl.Buffer(indices);
|
||||
context.bindBuffer(goog.webgl.ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
|
||||
|
||||
goog.asserts.assert(this.textures_.length === 0);
|
||||
|
||||
// create textures
|
||||
var texture, image, uid;
|
||||
/** @type {Object.<string, WebGLTexture>} */
|
||||
var texturePerImage = {};
|
||||
var i;
|
||||
var ii = this.images_.length;
|
||||
for (i = 0; i < ii; ++i) {
|
||||
image = this.images_[i];
|
||||
|
||||
uid = goog.getUid(image).toString();
|
||||
if (goog.object.containsKey(texturePerImage, uid)) {
|
||||
texture = goog.object.get(texturePerImage, uid);
|
||||
} else {
|
||||
texture = gl.createTexture();
|
||||
gl.bindTexture(goog.webgl.TEXTURE_2D, texture);
|
||||
gl.texParameteri(goog.webgl.TEXTURE_2D,
|
||||
goog.webgl.TEXTURE_WRAP_S, goog.webgl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(goog.webgl.TEXTURE_2D,
|
||||
goog.webgl.TEXTURE_WRAP_T, goog.webgl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(goog.webgl.TEXTURE_2D,
|
||||
goog.webgl.TEXTURE_MIN_FILTER, goog.webgl.LINEAR);
|
||||
gl.texParameteri(goog.webgl.TEXTURE_2D,
|
||||
goog.webgl.TEXTURE_MAG_FILTER, goog.webgl.LINEAR);
|
||||
gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA, goog.webgl.RGBA,
|
||||
goog.webgl.UNSIGNED_BYTE, image);
|
||||
goog.object.set(texturePerImage, uid, texture);
|
||||
}
|
||||
this.textures_[i] = texture;
|
||||
}
|
||||
|
||||
goog.asserts.assert(this.textures_.length == this.groupIndices_.length);
|
||||
|
||||
this.anchorX_ = undefined;
|
||||
this.anchorY_ = undefined;
|
||||
this.height_ = undefined;
|
||||
this.images_ = null;
|
||||
this.imageHeight_ = undefined;
|
||||
this.imageWidth_ = undefined;
|
||||
this.indices_ = null;
|
||||
this.opacity_ = undefined;
|
||||
this.originX_ = undefined;
|
||||
this.originY_ = undefined;
|
||||
this.rotateWithView_ = undefined;
|
||||
this.rotation_ = undefined;
|
||||
this.scale_ = undefined;
|
||||
this.vertices_ = null;
|
||||
this.width_ = undefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {ol.Extent} Extent.
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.getExtent = function() {
|
||||
return this.extent_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.webgl.Context} context Context.
|
||||
* @param {ol.Coordinate} center Center.
|
||||
* @param {number} resolution Resolution.
|
||||
* @param {number} rotation Rotation.
|
||||
* @param {ol.Size} size Size.
|
||||
* @param {ol.Extent} extent Extent.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @param {number} opacity Global opacity.
|
||||
* @param {number} brightness Global brightness.
|
||||
* @param {number} contrast Global contrast.
|
||||
* @param {number} hue Global hue.
|
||||
* @param {number} saturation Global saturation.
|
||||
* @param {Object} skippedFeaturesHash Ids of features to skip.
|
||||
* @return {T|undefined} Callback result.
|
||||
* @template T
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.replay = function(context,
|
||||
center, resolution, rotation, size, extent, pixelRatio,
|
||||
opacity, brightness, contrast, hue, saturation, skippedFeaturesHash) {
|
||||
var gl = context.getGL();
|
||||
|
||||
// bind the vertices buffer
|
||||
goog.asserts.assert(!goog.isNull(this.verticesBuffer_));
|
||||
context.bindBuffer(goog.webgl.ARRAY_BUFFER, this.verticesBuffer_);
|
||||
|
||||
// bind the indices buffer
|
||||
goog.asserts.assert(!goog.isNull(this.indicesBuffer_));
|
||||
context.bindBuffer(goog.webgl.ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
|
||||
|
||||
var useColor = brightness || contrast != 1 || hue || saturation != 1;
|
||||
|
||||
// get the program
|
||||
var fragmentShader, vertexShader;
|
||||
if (useColor) {
|
||||
fragmentShader =
|
||||
ol.render.webgl.imagereplay.shader.ColorFragment.getInstance();
|
||||
vertexShader =
|
||||
ol.render.webgl.imagereplay.shader.ColorVertex.getInstance();
|
||||
} else {
|
||||
fragmentShader =
|
||||
ol.render.webgl.imagereplay.shader.DefaultFragment.getInstance();
|
||||
vertexShader =
|
||||
ol.render.webgl.imagereplay.shader.DefaultVertex.getInstance();
|
||||
}
|
||||
var program = context.getProgram(fragmentShader, vertexShader);
|
||||
|
||||
// get the locations
|
||||
var locations;
|
||||
if (useColor) {
|
||||
if (goog.isNull(this.colorLocations_)) {
|
||||
locations =
|
||||
new ol.render.webgl.imagereplay.shader.Color.Locations(gl, program);
|
||||
this.colorLocations_ = locations;
|
||||
} else {
|
||||
locations = this.colorLocations_;
|
||||
}
|
||||
} else {
|
||||
if (goog.isNull(this.defaultLocations_)) {
|
||||
locations =
|
||||
new ol.render.webgl.imagereplay.shader.Default.Locations(gl, program);
|
||||
this.defaultLocations_ = locations;
|
||||
} else {
|
||||
locations = this.defaultLocations_;
|
||||
}
|
||||
}
|
||||
|
||||
// use the program (FIXME: use the return value)
|
||||
context.useProgram(program);
|
||||
|
||||
// enable the vertex attrib arrays
|
||||
gl.enableVertexAttribArray(locations.a_position);
|
||||
gl.vertexAttribPointer(locations.a_position, 2, goog.webgl.FLOAT,
|
||||
false, 32, 0);
|
||||
|
||||
gl.enableVertexAttribArray(locations.a_offsets);
|
||||
gl.vertexAttribPointer(locations.a_offsets, 2, goog.webgl.FLOAT,
|
||||
false, 32, 8);
|
||||
|
||||
gl.enableVertexAttribArray(locations.a_texCoord);
|
||||
gl.vertexAttribPointer(locations.a_texCoord, 2, goog.webgl.FLOAT,
|
||||
false, 32, 16);
|
||||
|
||||
gl.enableVertexAttribArray(locations.a_opacity);
|
||||
gl.vertexAttribPointer(locations.a_opacity, 1, goog.webgl.FLOAT,
|
||||
false, 32, 24);
|
||||
|
||||
gl.enableVertexAttribArray(locations.a_rotateWithView);
|
||||
gl.vertexAttribPointer(locations.a_rotateWithView, 1, goog.webgl.FLOAT,
|
||||
false, 32, 28);
|
||||
|
||||
// set the "uniform" values
|
||||
var projectionMatrix = this.projectionMatrix_;
|
||||
ol.vec.Mat4.makeTransform2D(projectionMatrix,
|
||||
0.0, 0.0,
|
||||
2 / (resolution * size[0]),
|
||||
2 / (resolution * size[1]),
|
||||
-rotation,
|
||||
-(center[0] - this.origin_[0]), -(center[1] - this.origin_[1]));
|
||||
|
||||
var offsetScaleMatrix = this.offsetScaleMatrix_;
|
||||
goog.vec.Mat4.makeScale(offsetScaleMatrix, 2 / size[0], 2 / size[1], 1);
|
||||
|
||||
var offsetRotateMatrix = this.offsetRotateMatrix_;
|
||||
goog.vec.Mat4.makeIdentity(offsetRotateMatrix);
|
||||
if (rotation !== 0) {
|
||||
goog.vec.Mat4.rotateZ(offsetRotateMatrix, -rotation);
|
||||
}
|
||||
|
||||
gl.uniformMatrix4fv(locations.u_projectionMatrix, false, projectionMatrix);
|
||||
gl.uniformMatrix4fv(locations.u_offsetScaleMatrix, false, offsetScaleMatrix);
|
||||
gl.uniformMatrix4fv(locations.u_offsetRotateMatrix, false,
|
||||
offsetRotateMatrix);
|
||||
gl.uniform1f(locations.u_opacity, opacity);
|
||||
if (useColor) {
|
||||
gl.uniformMatrix4fv(locations.u_colorMatrix, false,
|
||||
this.colorMatrix_.getMatrix(brightness, contrast, hue, saturation));
|
||||
}
|
||||
|
||||
// draw!
|
||||
goog.asserts.assert(this.textures_.length == this.groupIndices_.length);
|
||||
var i, ii, start;
|
||||
for (i = 0, ii = this.textures_.length, start = 0; i < ii; ++i) {
|
||||
gl.bindTexture(goog.webgl.TEXTURE_2D, this.textures_[i]);
|
||||
var end = this.groupIndices_[i];
|
||||
var numItems = end - start;
|
||||
var offsetInBytes = start * (context.hasOESElementIndexUint ? 4 : 2);
|
||||
var elementType = context.hasOESElementIndexUint ?
|
||||
goog.webgl.UNSIGNED_INT : goog.webgl.UNSIGNED_SHORT;
|
||||
gl.drawElements(goog.webgl.TRIANGLES, numItems, elementType, offsetInBytes);
|
||||
start = end;
|
||||
}
|
||||
|
||||
// disable the vertex attrib arrays
|
||||
gl.disableVertexAttribArray(locations.a_position);
|
||||
gl.disableVertexAttribArray(locations.a_offsets);
|
||||
gl.disableVertexAttribArray(locations.a_texCoord);
|
||||
gl.disableVertexAttribArray(locations.a_opacity);
|
||||
gl.disableVertexAttribArray(locations.a_rotateWithView);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.setFillStrokeStyle = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.setImageStyle = function(imageStyle) {
|
||||
var anchor = imageStyle.getAnchor();
|
||||
goog.asserts.assert(!goog.isNull(anchor));
|
||||
var image = imageStyle.getImage(1);
|
||||
goog.asserts.assert(!goog.isNull(image));
|
||||
var imageSize = imageStyle.getImageSize();
|
||||
goog.asserts.assert(!goog.isNull(imageSize));
|
||||
var opacity = imageStyle.getOpacity();
|
||||
goog.asserts.assert(goog.isDef(opacity));
|
||||
var origin = imageStyle.getOrigin();
|
||||
goog.asserts.assert(!goog.isNull(origin));
|
||||
var rotateWithView = imageStyle.getRotateWithView();
|
||||
goog.asserts.assert(goog.isDef(rotateWithView));
|
||||
var rotation = imageStyle.getRotation();
|
||||
goog.asserts.assert(goog.isDef(rotation));
|
||||
var size = imageStyle.getSize();
|
||||
goog.asserts.assert(!goog.isNull(size));
|
||||
var scale = imageStyle.getScale();
|
||||
goog.asserts.assert(goog.isDef(scale));
|
||||
|
||||
if (this.images_.length === 0) {
|
||||
this.images_.push(image);
|
||||
} else {
|
||||
var currentImage = this.images_[this.images_.length - 1];
|
||||
if (goog.getUid(currentImage) != goog.getUid(image)) {
|
||||
this.groupIndices_.push(this.indices_.length);
|
||||
goog.asserts.assert(this.groupIndices_.length == this.images_.length);
|
||||
this.images_.push(image);
|
||||
}
|
||||
}
|
||||
|
||||
this.anchorX_ = anchor[0];
|
||||
this.anchorY_ = anchor[1];
|
||||
this.height_ = size[1];
|
||||
this.imageHeight_ = imageSize[1];
|
||||
this.imageWidth_ = imageSize[0];
|
||||
this.opacity_ = opacity;
|
||||
this.originX_ = origin[0];
|
||||
this.originY_ = origin[1];
|
||||
this.rotation_ = rotation;
|
||||
this.rotateWithView_ = rotateWithView;
|
||||
this.scale_ = scale;
|
||||
this.width_ = size[0];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.setTextStyle = goog.abstractMethod;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @implements {ol.render.IReplayGroup}
|
||||
* @param {number} tolerance Tolerance.
|
||||
* @param {ol.Extent} maxExtent Max extent.
|
||||
* @struct
|
||||
*/
|
||||
ol.render.webgl.ReplayGroup = function(tolerance, maxExtent) {
|
||||
|
||||
/**
|
||||
* @type {ol.Extent}
|
||||
* @private
|
||||
*/
|
||||
this.maxExtent_ = maxExtent;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.tolerance_ = tolerance;
|
||||
|
||||
/**
|
||||
* ImageReplay only is supported at this point.
|
||||
* @type {Object.<ol.render.ReplayType, ol.render.webgl.ImageReplay>}
|
||||
* @private
|
||||
*/
|
||||
this.replays_ = {};
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.webgl.Context} context WebGL context.
|
||||
* @return {function()} Delete resources function.
|
||||
*/
|
||||
ol.render.webgl.ReplayGroup.prototype.getDeleteResourcesFunction =
|
||||
function(context) {
|
||||
var functions = [];
|
||||
var replayKey;
|
||||
for (replayKey in this.replays_) {
|
||||
functions.push(
|
||||
this.replays_[replayKey].getDeleteResourcesFunction(context));
|
||||
}
|
||||
return goog.functions.sequence.apply(null, functions);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.webgl.Context} context Context.
|
||||
*/
|
||||
ol.render.webgl.ReplayGroup.prototype.finish = function(context) {
|
||||
var replayKey;
|
||||
for (replayKey in this.replays_) {
|
||||
this.replays_[replayKey].finish(context);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ReplayGroup.prototype.getReplay =
|
||||
function(zIndex, replayType) {
|
||||
var replay = this.replays_[replayType];
|
||||
if (!goog.isDef(replay)) {
|
||||
var constructor = ol.render.webgl.BATCH_CONSTRUCTORS_[replayType];
|
||||
goog.asserts.assert(goog.isDef(constructor));
|
||||
replay = new constructor(this.tolerance_, this.maxExtent_);
|
||||
this.replays_[replayType] = replay;
|
||||
}
|
||||
return replay;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.render.webgl.ReplayGroup.prototype.isEmpty = function() {
|
||||
return goog.object.isEmpty(this.replays_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.webgl.Context} context Context.
|
||||
* @param {ol.Coordinate} center Center.
|
||||
* @param {number} resolution Resolution.
|
||||
* @param {number} rotation Rotation.
|
||||
* @param {ol.Size} size Size.
|
||||
* @param {ol.Extent} extent Extent.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @param {number} opacity Global opacity.
|
||||
* @param {number} brightness Global brightness.
|
||||
* @param {number} contrast Global contrast.
|
||||
* @param {number} hue Global hue.
|
||||
* @param {number} saturation Global saturation.
|
||||
* @param {Object} skippedFeaturesHash Ids of features to skip.
|
||||
* @return {T|undefined} Callback result.
|
||||
* @template T
|
||||
*/
|
||||
ol.render.webgl.ReplayGroup.prototype.replay = function(context,
|
||||
center, resolution, rotation, size, extent, pixelRatio,
|
||||
opacity, brightness, contrast, hue, saturation, skippedFeaturesHash) {
|
||||
var i, ii, replay, result;
|
||||
for (i = 0, ii = ol.render.REPLAY_ORDER.length; i < ii; ++i) {
|
||||
replay = this.replays_[ol.render.REPLAY_ORDER[i]];
|
||||
if (goog.isDef(replay) &&
|
||||
ol.extent.intersects(extent, replay.getExtent())) {
|
||||
result = replay.replay(context,
|
||||
center, resolution, rotation, size, extent, pixelRatio,
|
||||
opacity, brightness, contrast, hue, saturation, skippedFeaturesHash);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private
|
||||
* @type {Object.<ol.render.ReplayType,
|
||||
* function(new: ol.render.webgl.ImageReplay, number,
|
||||
* ol.Extent)>}
|
||||
*/
|
||||
ol.render.webgl.BATCH_CONSTRUCTORS_ = {
|
||||
'Image': ol.render.webgl.ImageReplay
|
||||
};
|
||||
@@ -207,6 +207,14 @@ ol.renderer.canvas.Layer.prototype.getTransform = function(frameState) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {olx.FrameState} frameState Frame state.
|
||||
* @param {ol.layer.LayerState} layerState Layer state.
|
||||
* @return {boolean} whether composeFrame should be called.
|
||||
*/
|
||||
ol.renderer.canvas.Layer.prototype.prepareFrame = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Size} size Size.
|
||||
* @return {boolean} True when the canvas with the current size does not exceed
|
||||
|
||||
@@ -46,3 +46,11 @@ ol.renderer.dom.Layer.prototype.composeFrame = goog.nullFunction;
|
||||
ol.renderer.dom.Layer.prototype.getTarget = function() {
|
||||
return this.target;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {olx.FrameState} frameState Frame state.
|
||||
* @param {ol.layer.LayerState} layerState Layer state.
|
||||
* @return {boolean} whether composeFrame should be called.
|
||||
*/
|
||||
ol.renderer.dom.Layer.prototype.prepareFrame = goog.abstractMethod;
|
||||
|
||||
@@ -6,7 +6,6 @@ goog.require('ol.ImageState');
|
||||
goog.require('ol.TileRange');
|
||||
goog.require('ol.TileState');
|
||||
goog.require('ol.layer.Layer');
|
||||
goog.require('ol.layer.LayerState');
|
||||
goog.require('ol.source.Source');
|
||||
goog.require('ol.source.State');
|
||||
goog.require('ol.source.Tile');
|
||||
@@ -95,14 +94,6 @@ ol.renderer.Layer.prototype.handleImageChange = function(event) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {olx.FrameState} frameState Frame state.
|
||||
* @param {ol.layer.LayerState} layerState Layer state.
|
||||
* @return {boolean} whether composeFrame should be called.
|
||||
*/
|
||||
ol.renderer.Layer.prototype.prepareFrame = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
|
||||
@@ -101,7 +101,7 @@ ol.renderer.webgl.ImageLayer.prototype.forEachFeatureAtPixel =
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.ImageLayer.prototype.prepareFrame =
|
||||
function(frameState, layerState) {
|
||||
function(frameState, layerState, context) {
|
||||
|
||||
var gl = this.getWebGLMapRenderer().getGL();
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ goog.require('ol.render.webgl.Immediate');
|
||||
goog.require('ol.renderer.Layer');
|
||||
goog.require('ol.renderer.webgl.map.shader.Color');
|
||||
goog.require('ol.renderer.webgl.map.shader.Default');
|
||||
goog.require('ol.structs.Buffer');
|
||||
goog.require('ol.webgl.Buffer');
|
||||
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ ol.renderer.webgl.Layer = function(mapRenderer, layer) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.structs.Buffer}
|
||||
* @type {ol.webgl.Buffer}
|
||||
*/
|
||||
this.arrayBuffer_ = new ol.structs.Buffer([
|
||||
this.arrayBuffer_ = new ol.webgl.Buffer([
|
||||
-1, -1, 0, 0,
|
||||
1, -1, 1, 0,
|
||||
-1, 1, 0, 1,
|
||||
@@ -237,7 +237,16 @@ ol.renderer.webgl.Layer.prototype.dispatchComposeEvent_ =
|
||||
function(type, context, frameState) {
|
||||
var layer = this.getLayer();
|
||||
if (layer.hasListener(type)) {
|
||||
var render = new ol.render.webgl.Immediate(context, frameState.pixelRatio);
|
||||
var viewState = frameState.viewState;
|
||||
var resolution = viewState.resolution;
|
||||
var pixelRatio = frameState.pixelRatio;
|
||||
var extent = frameState.extent;
|
||||
var center = viewState.center;
|
||||
var rotation = viewState.rotation;
|
||||
var size = frameState.size;
|
||||
|
||||
var render = new ol.render.webgl.Immediate(
|
||||
context, center, resolution, rotation, size, extent, pixelRatio);
|
||||
var composeEvent = new ol.render.Event(
|
||||
type, layer, render, null, frameState, null, context);
|
||||
layer.dispatchEvent(composeEvent);
|
||||
@@ -286,3 +295,12 @@ ol.renderer.webgl.Layer.prototype.handleWebGLContextLost = function() {
|
||||
this.framebuffer = null;
|
||||
this.framebufferDimension = undefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {olx.FrameState} frameState Frame state.
|
||||
* @param {ol.layer.LayerState} layerState Layer state.
|
||||
* @param {ol.webgl.Context} context Context.
|
||||
* @return {boolean} whether composeFrame should be called.
|
||||
*/
|
||||
ol.renderer.webgl.Layer.prototype.prepareFrame = goog.abstractMethod;
|
||||
|
||||
@@ -20,13 +20,17 @@ goog.require('ol.dom');
|
||||
goog.require('ol.layer.Image');
|
||||
goog.require('ol.layer.Layer');
|
||||
goog.require('ol.layer.Tile');
|
||||
goog.require('ol.layer.Vector');
|
||||
goog.require('ol.render.Event');
|
||||
goog.require('ol.render.EventType');
|
||||
goog.require('ol.render.webgl.Immediate');
|
||||
goog.require('ol.render.webgl.ReplayGroup');
|
||||
goog.require('ol.renderer.Map');
|
||||
goog.require('ol.renderer.vector');
|
||||
goog.require('ol.renderer.webgl.ImageLayer');
|
||||
goog.require('ol.renderer.webgl.Layer');
|
||||
goog.require('ol.renderer.webgl.TileLayer');
|
||||
goog.require('ol.renderer.webgl.VectorLayer');
|
||||
goog.require('ol.source.State');
|
||||
goog.require('ol.structs.LRUCache');
|
||||
goog.require('ol.structs.PriorityQueue');
|
||||
@@ -250,6 +254,8 @@ ol.renderer.webgl.Map.prototype.createLayerRenderer = function(layer) {
|
||||
return new ol.renderer.webgl.ImageLayer(this, layer);
|
||||
} else if (ol.ENABLE_TILE && layer instanceof ol.layer.Tile) {
|
||||
return new ol.renderer.webgl.TileLayer(this, layer);
|
||||
} else if (ol.ENABLE_VECTOR && layer instanceof ol.layer.Vector) {
|
||||
return new ol.renderer.webgl.VectorLayer(this, layer);
|
||||
} else {
|
||||
goog.asserts.fail();
|
||||
return null;
|
||||
@@ -266,11 +272,40 @@ ol.renderer.webgl.Map.prototype.dispatchComposeEvent_ =
|
||||
function(type, frameState) {
|
||||
var map = this.getMap();
|
||||
if (map.hasListener(type)) {
|
||||
var context = this.getContext();
|
||||
var render = new ol.render.webgl.Immediate(context, frameState.pixelRatio);
|
||||
var composeEvent = new ol.render.Event(
|
||||
type, map, render, null, frameState, null, context);
|
||||
var context = this.context_;
|
||||
|
||||
var extent = frameState.extent;
|
||||
var size = frameState.size;
|
||||
var viewState = frameState.viewState;
|
||||
var pixelRatio = frameState.pixelRatio;
|
||||
|
||||
var resolution = viewState.resolution;
|
||||
var center = viewState.center;
|
||||
var rotation = viewState.rotation;
|
||||
var tolerance = ol.renderer.vector.getTolerance(resolution, pixelRatio);
|
||||
|
||||
var vectorContext = new ol.render.webgl.Immediate(context,
|
||||
center, resolution, rotation, size, extent, pixelRatio);
|
||||
var replayGroup = new ol.render.webgl.ReplayGroup(tolerance, extent);
|
||||
var composeEvent = new ol.render.Event(type, map, vectorContext,
|
||||
replayGroup, frameState, null, context);
|
||||
map.dispatchEvent(composeEvent);
|
||||
|
||||
replayGroup.finish(context);
|
||||
if (!replayGroup.isEmpty()) {
|
||||
// use default color values
|
||||
var opacity = 1;
|
||||
var brightness = 0;
|
||||
var contrast = 1;
|
||||
var hue = 0;
|
||||
var saturation = 1;
|
||||
replayGroup.replay(context, center, resolution, rotation, size, extent,
|
||||
pixelRatio, opacity, brightness, contrast, hue, saturation, {});
|
||||
}
|
||||
replayGroup.getDeleteResourcesFunction(context)();
|
||||
|
||||
vectorContext.flush();
|
||||
this.replayGroup = replayGroup;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -455,7 +490,7 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
|
||||
layerState.sourceState == ol.source.State.READY) {
|
||||
layerRenderer = this.getLayerRenderer(layerState.layer);
|
||||
goog.asserts.assertInstanceof(layerRenderer, ol.renderer.webgl.Layer);
|
||||
if (layerRenderer.prepareFrame(frameState, layerState)) {
|
||||
if (layerRenderer.prepareFrame(frameState, layerState, context)) {
|
||||
layerStatesToDraw.push(layerState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ goog.require('ol.layer.Tile');
|
||||
goog.require('ol.math');
|
||||
goog.require('ol.renderer.webgl.Layer');
|
||||
goog.require('ol.renderer.webgl.tilelayer.shader');
|
||||
goog.require('ol.structs.Buffer');
|
||||
goog.require('ol.tilecoord');
|
||||
goog.require('ol.webgl.Buffer');
|
||||
|
||||
|
||||
|
||||
@@ -52,9 +52,9 @@ ol.renderer.webgl.TileLayer = function(mapRenderer, tileLayer) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.structs.Buffer}
|
||||
* @type {ol.webgl.Buffer}
|
||||
*/
|
||||
this.renderArrayBuffer_ = new ol.structs.Buffer([
|
||||
this.renderArrayBuffer_ = new ol.webgl.Buffer([
|
||||
0, 0, 0, 1,
|
||||
1, 0, 1, 1,
|
||||
0, 1, 0, 0,
|
||||
@@ -107,11 +107,10 @@ ol.renderer.webgl.TileLayer.prototype.handleWebGLContextLost = function() {
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.TileLayer.prototype.prepareFrame =
|
||||
function(frameState, layerState) {
|
||||
function(frameState, layerState, context) {
|
||||
|
||||
var mapRenderer = this.getWebGLMapRenderer();
|
||||
var context = mapRenderer.getContext();
|
||||
var gl = mapRenderer.getGL();
|
||||
var gl = context.getGL();
|
||||
|
||||
var viewState = frameState.viewState;
|
||||
var projection = viewState.projection;
|
||||
|
||||
240
src/ol/renderer/webgl/webglvectorlayerrenderer.js
Normal file
240
src/ol/renderer/webgl/webglvectorlayerrenderer.js
Normal file
@@ -0,0 +1,240 @@
|
||||
goog.provide('ol.renderer.webgl.VectorLayer');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.events');
|
||||
goog.require('ol.ViewHint');
|
||||
goog.require('ol.extent');
|
||||
goog.require('ol.layer.Vector');
|
||||
goog.require('ol.render.webgl.ReplayGroup');
|
||||
goog.require('ol.renderer.vector');
|
||||
goog.require('ol.renderer.webgl.Layer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {ol.renderer.webgl.Layer}
|
||||
* @param {ol.renderer.Map} mapRenderer Map renderer.
|
||||
* @param {ol.layer.Vector} vectorLayer Vector layer.
|
||||
*/
|
||||
ol.renderer.webgl.VectorLayer = function(mapRenderer, vectorLayer) {
|
||||
|
||||
goog.base(this, mapRenderer, vectorLayer);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.dirty_ = false;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.renderedRevision_ = -1;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.renderedResolution_ = NaN;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.Extent}
|
||||
*/
|
||||
this.renderedExtent_ = ol.extent.createEmpty();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {function(ol.Feature, ol.Feature): number|null}
|
||||
*/
|
||||
this.renderedRenderOrder_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.render.webgl.ReplayGroup}
|
||||
*/
|
||||
this.replayGroup_ = null;
|
||||
|
||||
};
|
||||
goog.inherits(ol.renderer.webgl.VectorLayer, ol.renderer.webgl.Layer);
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.VectorLayer.prototype.composeFrame =
|
||||
function(frameState, layerState, context) {
|
||||
var viewState = frameState.viewState;
|
||||
var replayGroup = this.replayGroup_;
|
||||
if (!goog.isNull(replayGroup) && !replayGroup.isEmpty()) {
|
||||
replayGroup.replay(context,
|
||||
viewState.center, viewState.resolution, viewState.rotation,
|
||||
frameState.size, frameState.extent, frameState.pixelRatio,
|
||||
layerState.opacity, layerState.brightness, layerState.contrast,
|
||||
layerState.hue, layerState.saturation, frameState.skippedFeatureUids);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.VectorLayer.prototype.disposeInternal = function() {
|
||||
var replayGroup = this.replayGroup_;
|
||||
if (!goog.isNull(replayGroup)) {
|
||||
var mapRenderer = this.getWebGLMapRenderer();
|
||||
var context = mapRenderer.getContext();
|
||||
replayGroup.getDeleteResourcesFunction(context)();
|
||||
this.replayGroup_ = null;
|
||||
}
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.VectorLayer.prototype.forEachFeatureAtPixel =
|
||||
function(coordinate, frameState, callback, thisArg) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle changes in image style state.
|
||||
* @param {goog.events.Event} event Image style change event.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.webgl.VectorLayer.prototype.handleImageChange_ =
|
||||
function(event) {
|
||||
this.renderIfReadyAndVisible();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.VectorLayer.prototype.prepareFrame =
|
||||
function(frameState, layerState, context) {
|
||||
|
||||
var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer());
|
||||
goog.asserts.assertInstanceof(vectorLayer, ol.layer.Vector);
|
||||
var vectorSource = vectorLayer.getSource();
|
||||
|
||||
this.updateAttributions(
|
||||
frameState.attributions, vectorSource.getAttributions());
|
||||
this.updateLogos(frameState, vectorSource);
|
||||
|
||||
if (!this.dirty_ && (frameState.viewHints[ol.ViewHint.ANIMATING] ||
|
||||
frameState.viewHints[ol.ViewHint.INTERACTING])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var frameStateExtent = frameState.extent;
|
||||
var viewState = frameState.viewState;
|
||||
var projection = viewState.projection;
|
||||
var resolution = viewState.resolution;
|
||||
var pixelRatio = frameState.pixelRatio;
|
||||
var vectorLayerRevision = vectorLayer.getRevision();
|
||||
var vectorLayerRenderOrder = vectorLayer.getRenderOrder();
|
||||
if (!goog.isDef(vectorLayerRenderOrder)) {
|
||||
vectorLayerRenderOrder = ol.renderer.vector.defaultOrder;
|
||||
}
|
||||
|
||||
if (!this.dirty_ &&
|
||||
this.renderedResolution_ == resolution &&
|
||||
this.renderedRevision_ == vectorLayerRevision &&
|
||||
this.renderedRenderOrder_ == vectorLayerRenderOrder &&
|
||||
ol.extent.containsExtent(this.renderedExtent_, frameStateExtent)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var extent = this.renderedExtent_;
|
||||
var xBuffer = ol.extent.getWidth(frameStateExtent) / 4;
|
||||
var yBuffer = ol.extent.getHeight(frameStateExtent) / 4;
|
||||
extent[0] = frameStateExtent[0] - xBuffer;
|
||||
extent[1] = frameStateExtent[1] - yBuffer;
|
||||
extent[2] = frameStateExtent[2] + xBuffer;
|
||||
extent[3] = frameStateExtent[3] + yBuffer;
|
||||
|
||||
if (!goog.isNull(this.replayGroup_)) {
|
||||
frameState.postRenderFunctions.push(
|
||||
this.replayGroup_.getDeleteResourcesFunction(context));
|
||||
}
|
||||
|
||||
this.dirty_ = false;
|
||||
|
||||
var replayGroup = new ol.render.webgl.ReplayGroup(
|
||||
ol.renderer.vector.getTolerance(resolution, pixelRatio),
|
||||
extent);
|
||||
vectorSource.loadFeatures(extent, resolution, projection);
|
||||
var renderFeature =
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature.
|
||||
* @this {ol.renderer.webgl.VectorLayer}
|
||||
*/
|
||||
function(feature) {
|
||||
var styles;
|
||||
if (goog.isDef(feature.getStyleFunction())) {
|
||||
styles = feature.getStyleFunction().call(feature, resolution);
|
||||
} else if (goog.isDef(vectorLayer.getStyleFunction())) {
|
||||
styles = vectorLayer.getStyleFunction()(feature, resolution);
|
||||
}
|
||||
if (goog.isDefAndNotNull(styles)) {
|
||||
var dirty = this.renderFeature(
|
||||
feature, resolution, pixelRatio, styles, replayGroup);
|
||||
this.dirty_ = this.dirty_ || dirty;
|
||||
}
|
||||
};
|
||||
if (!goog.isNull(vectorLayerRenderOrder)) {
|
||||
/** @type {Array.<ol.Feature>} */
|
||||
var features = [];
|
||||
vectorSource.forEachFeatureInExtentAtResolution(extent, resolution,
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature.
|
||||
*/
|
||||
function(feature) {
|
||||
features.push(feature);
|
||||
}, this);
|
||||
goog.array.sort(features, vectorLayerRenderOrder);
|
||||
goog.array.forEach(features, renderFeature, this);
|
||||
} else {
|
||||
vectorSource.forEachFeatureInExtentAtResolution(
|
||||
extent, resolution, renderFeature, this);
|
||||
}
|
||||
replayGroup.finish(context);
|
||||
|
||||
this.renderedResolution_ = resolution;
|
||||
this.renderedRevision_ = vectorLayerRevision;
|
||||
this.renderedRenderOrder_ = vectorLayerRenderOrder;
|
||||
this.replayGroup_ = replayGroup;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature.
|
||||
* @param {number} resolution Resolution.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @param {Array.<ol.style.Style>} styles Array of styles
|
||||
* @param {ol.render.webgl.ReplayGroup} replayGroup Replay group.
|
||||
* @return {boolean} `true` if an image is loading.
|
||||
*/
|
||||
ol.renderer.webgl.VectorLayer.prototype.renderFeature =
|
||||
function(feature, resolution, pixelRatio, styles, replayGroup) {
|
||||
if (!goog.isDefAndNotNull(styles)) {
|
||||
return false;
|
||||
}
|
||||
var i, ii, loading = false;
|
||||
for (i = 0, ii = styles.length; i < ii; ++i) {
|
||||
loading = ol.renderer.vector.renderFeature(
|
||||
replayGroup, feature, styles[i],
|
||||
ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio),
|
||||
feature, this.handleImageChange_, this) || loading;
|
||||
}
|
||||
return loading;
|
||||
};
|
||||
@@ -1,257 +0,0 @@
|
||||
goog.provide('ol.structs.Buffer');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.webgl');
|
||||
goog.require('ol');
|
||||
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
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Array.<number>=} opt_arr Array.
|
||||
* @param {number=} opt_used Used.
|
||||
* @param {number=} opt_usage Usage.
|
||||
* @struct
|
||||
*/
|
||||
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 {?Float32Array}
|
||||
*/
|
||||
this.split32_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.structs.IntegerSet}
|
||||
*/
|
||||
this.split32DirtySet_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.usage_ = goog.isDef(opt_usage) ?
|
||||
opt_usage : ol.structs.BufferUsage.STATIC_DRAW;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @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.<number>} values Values.
|
||||
* @return {number} Offset.
|
||||
*/
|
||||
ol.structs.Buffer.prototype.add = function(values) {
|
||||
var size = values.length;
|
||||
var offset = this.allocate(size);
|
||||
var i;
|
||||
for (i = 0; i < size; ++i) {
|
||||
this.arr_[offset + i] = values[i];
|
||||
}
|
||||
this.markDirty(size, offset);
|
||||
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_this The object to use as `this` in `f`.
|
||||
* @template T
|
||||
*/
|
||||
ol.structs.Buffer.prototype.forEachRange = function(f, opt_this) {
|
||||
if (this.arr_.length !== 0) {
|
||||
this.freeSet_.forEachRangeInverted(0, this.arr_.length, f, opt_this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @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_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
ol.structs.Buffer.prototype.getUsage = function() {
|
||||
return this.usage_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
ol.structs.Buffer.prototype.remove = function(size, offset) {
|
||||
var i, ii;
|
||||
this.freeSet_.addRange(offset, offset + size);
|
||||
for (i = 0, ii = this.dirtySets_.length; i < ii; ++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];
|
||||
}
|
||||
this.markDirty(n, offset);
|
||||
};
|
||||
16
src/ol/structs/checksum.js
Normal file
16
src/ol/structs/checksum.js
Normal file
@@ -0,0 +1,16 @@
|
||||
goog.provide('ol.structs.IHasChecksum');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*/
|
||||
ol.structs.IHasChecksum = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The checksum.
|
||||
*/
|
||||
ol.structs.IHasChecksum.prototype.getChecksum = function() {
|
||||
};
|
||||
@@ -1,330 +0,0 @@
|
||||
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
|
||||
* @struct
|
||||
* @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 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_this The object to use as `this` in `f`.
|
||||
* @template T
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.forEachRange = function(f, opt_this) {
|
||||
var arr = this.arr_;
|
||||
var n = arr.length;
|
||||
var i;
|
||||
for (i = 0; i < n; i += 2) {
|
||||
f.call(opt_this, 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_this The object to use as `this` in `f`.
|
||||
* @template T
|
||||
*/
|
||||
ol.structs.IntegerSet.prototype.forEachRangeInverted =
|
||||
function(start, stop, f, opt_this) {
|
||||
goog.asserts.assert(start < stop);
|
||||
var arr = this.arr_;
|
||||
var n = arr.length;
|
||||
if (n === 0) {
|
||||
f.call(opt_this, start, stop);
|
||||
} else {
|
||||
if (start < arr[0]) {
|
||||
f.call(opt_this, start, arr[0]);
|
||||
}
|
||||
var i;
|
||||
for (i = 1; i < n - 1; i += 2) {
|
||||
f.call(opt_this, arr[i], arr[i + 1]);
|
||||
}
|
||||
if (arr[n - 1] < stop) {
|
||||
f.call(opt_this, 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(', ');
|
||||
};
|
||||
|
||||
}
|
||||
440
src/ol/style/atlasmanager.js
Normal file
440
src/ol/style/atlasmanager.js
Normal file
@@ -0,0 +1,440 @@
|
||||
goog.provide('ol.style.Atlas');
|
||||
goog.provide('ol.style.AtlasManager');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.object');
|
||||
goog.require('ol');
|
||||
|
||||
|
||||
/**
|
||||
* Provides information for an image inside an atlas manager.
|
||||
* `offsetX` and `offsetY` is the position of the image inside
|
||||
* the atlas image `image`.
|
||||
* `hitOffsetX` and `hitOffsetY` ist the position of the hit-detection image
|
||||
* inside the hit-detection atlas image `hitImage` (only when a hit-detection
|
||||
* image was created for this image).
|
||||
* @typedef {{offsetX: number, offsetY: number, image: HTMLCanvasElement,
|
||||
* hitOffsetX: number, hitOffsetY: number, hitImage: HTMLCanvasElement}}
|
||||
*/
|
||||
ol.style.AtlasManagerInfo;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Manages the creation of image atlases.
|
||||
*
|
||||
* Images added to this manager will be inserted into an atlas, which
|
||||
* will be used for rendering.
|
||||
* The `size` given in the constructor is the size for the first
|
||||
* atlas. After that, when new atlases are created, they will have
|
||||
* twice the size as the latest atlas (until `maxSize` is reached).
|
||||
*
|
||||
* If an application uses many images or very large images, it is recommended
|
||||
* to set a higher `size` value to avoid the creation of too many atlases.
|
||||
*
|
||||
* @constructor
|
||||
* @struct
|
||||
* @api
|
||||
* @param {olx.style.AtlasManagerOptions=} opt_options Options.
|
||||
*/
|
||||
ol.style.AtlasManager = function(opt_options) {
|
||||
|
||||
var options = goog.isDef(opt_options) ? opt_options : {};
|
||||
|
||||
/**
|
||||
* The size in pixels of the latest atlas image.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.currentSize_ = goog.isDef(options.initialSize) ?
|
||||
options.initialSize : ol.INITIAL_ATLAS_SIZE;
|
||||
|
||||
/**
|
||||
* The maximum size in pixels of atlas images.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.maxSize_ = goog.isDef(options.maxSize) ?
|
||||
options.maxSize : ol.MAX_ATLAS_SIZE != -1 ?
|
||||
ol.MAX_ATLAS_SIZE : goog.isDef(ol.WEBGL_MAX_TEXTURE_SIZE) ?
|
||||
ol.WEBGL_MAX_TEXTURE_SIZE : 2048;
|
||||
|
||||
/**
|
||||
* The size in pixels between images.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.space_ = goog.isDef(options.space) ? options.space : 1;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<ol.style.Atlas>}
|
||||
*/
|
||||
this.atlases_ = [new ol.style.Atlas(this.currentSize_, this.space_)];
|
||||
|
||||
/**
|
||||
* The size in pixels of the latest atlas image for hit-detection images.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.currentHitSize_ = this.currentSize_;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<ol.style.Atlas>}
|
||||
*/
|
||||
this.hitAtlases_ = [new ol.style.Atlas(this.currentHitSize_, this.space_)];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} id The identifier of the entry to check.
|
||||
* @return {?ol.style.AtlasManagerInfo} The position and atlas image for the
|
||||
* entry, or `null` if the entry is not part of the atlas manager.
|
||||
*/
|
||||
ol.style.AtlasManager.prototype.getInfo = function(id) {
|
||||
/** @type {?ol.style.AtlasInfo} */
|
||||
var info = this.getInfo_(this.atlases_, id);
|
||||
|
||||
if (goog.isNull(info)) {
|
||||
return null;
|
||||
}
|
||||
/** @type {?ol.style.AtlasInfo} */
|
||||
var hitInfo = this.getInfo_(this.hitAtlases_, id);
|
||||
|
||||
return this.mergeInfos_(info, hitInfo);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Array.<ol.style.Atlas>} atlases The atlases to search.
|
||||
* @param {string} id The identifier of the entry to check.
|
||||
* @return {?ol.style.AtlasInfo} The position and atlas image for the entry,
|
||||
* or `null` if the entry is not part of the atlases.
|
||||
*/
|
||||
ol.style.AtlasManager.prototype.getInfo_ = function(atlases, id) {
|
||||
var atlas, info, i, ii;
|
||||
for (i = 0, ii = atlases.length; i < ii; ++i) {
|
||||
atlas = atlases[i];
|
||||
info = atlas.get(id);
|
||||
if (!goog.isNull(info)) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {ol.style.AtlasInfo} info The info for the real image.
|
||||
* @param {?ol.style.AtlasInfo} hitInfo The info for the hit-detection
|
||||
* image.
|
||||
* @return {?ol.style.AtlasManagerInfo} The position and atlas image for the
|
||||
* entry, or `null` if the entry is not part of the atlases.
|
||||
*/
|
||||
ol.style.AtlasManager.prototype.mergeInfos_ = function(info, hitInfo) {
|
||||
return /** @type {ol.style.AtlasManagerInfo} */ ({
|
||||
offsetX: info.offsetX,
|
||||
offsetY: info.offsetY,
|
||||
image: info.image,
|
||||
hitOffsetX: goog.isNull(hitInfo) ? undefined : hitInfo.offsetX,
|
||||
hitOffsetY: goog.isNull(hitInfo) ? undefined : hitInfo.offsetY,
|
||||
hitImage: goog.isNull(hitInfo) ? undefined : hitInfo.image
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Add an image to the atlas manager.
|
||||
*
|
||||
* If an entry for the given id already exists, the entry will
|
||||
* be overridden (but the space on the atlas graphic will not be freed).
|
||||
*
|
||||
* If `renderHitCallback` is provided, the image (or the hit-detection version
|
||||
* of the image) will be rendered into a separate hit-detection atlas image.
|
||||
*
|
||||
* @param {string} id The identifier of the entry to add.
|
||||
* @param {number} width The width.
|
||||
* @param {number} height The height.
|
||||
* @param {function(CanvasRenderingContext2D, number, number)} renderCallback
|
||||
* Called to render the new image onto an atlas image.
|
||||
* @param {function(CanvasRenderingContext2D, number, number)=}
|
||||
* opt_renderHitCallback Called to render a hit-detection image onto a hit
|
||||
* detection atlas image.
|
||||
* @param {Object=} opt_this Value to use as `this` when executing
|
||||
* `renderCallback` and `renderHitCallback`.
|
||||
* @return {?ol.style.AtlasManagerInfo} The position and atlas image for the
|
||||
* entry, or `null` if the image is too big.
|
||||
*/
|
||||
ol.style.AtlasManager.prototype.add =
|
||||
function(id, width, height,
|
||||
renderCallback, opt_renderHitCallback, opt_this) {
|
||||
if (width + this.space_ > this.maxSize_ ||
|
||||
height + this.space_ > this.maxSize_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @type {?ol.style.AtlasInfo} */
|
||||
var info = this.add_(false,
|
||||
id, width, height, renderCallback, opt_this);
|
||||
if (goog.isNull(info)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @type {?ol.style.AtlasInfo} */
|
||||
var hitInfo = null;
|
||||
if (goog.isDef(opt_renderHitCallback)) {
|
||||
hitInfo = this.add_(true,
|
||||
id, width, height, opt_renderHitCallback, opt_this);
|
||||
}
|
||||
return this.mergeInfos_(info, hitInfo);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {boolean} isHitAtlas If the hit-detection atlases are used.
|
||||
* @param {string} id The identifier of the entry to add.
|
||||
* @param {number} width The width.
|
||||
* @param {number} height The height.
|
||||
* @param {function(CanvasRenderingContext2D, number, number)} renderCallback
|
||||
* Called to render the new image onto an atlas image.
|
||||
* @param {Object=} opt_this Value to use as `this` when executing
|
||||
* `renderCallback` and `renderHitCallback`.
|
||||
* @return {?ol.style.AtlasInfo} The position and atlas image for the entry,
|
||||
* or `null` if the image is too big.
|
||||
*/
|
||||
ol.style.AtlasManager.prototype.add_ =
|
||||
function(isHitAtlas, id, width, height,
|
||||
renderCallback, opt_this) {
|
||||
var atlases = (isHitAtlas) ? this.hitAtlases_ : this.atlases_;
|
||||
var atlas, info, i, ii;
|
||||
for (i = 0, ii = atlases.length; i < ii; ++i) {
|
||||
atlas = atlases[i];
|
||||
info = atlas.add(id, width, height, renderCallback, opt_this);
|
||||
if (!goog.isNull(info)) {
|
||||
return info;
|
||||
} else if (goog.isNull(info) && i === ii - 1) {
|
||||
// the entry could not be added to one of the existing atlases,
|
||||
// create a new atlas that is twice as big and try to add to this one.
|
||||
var size;
|
||||
if (isHitAtlas) {
|
||||
size = Math.min(this.currentHitSize_ * 2, this.maxSize_);
|
||||
this.currentHitSize_ = size;
|
||||
} else {
|
||||
size = Math.min(this.currentSize_ * 2, this.maxSize_);
|
||||
this.currentSize_ = size;
|
||||
}
|
||||
atlas = new ol.style.Atlas(size, this.space_);
|
||||
atlases.push(atlas);
|
||||
// run the loop another time
|
||||
++ii;
|
||||
}
|
||||
}
|
||||
goog.asserts.fail();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Provides information for an image inside an atlas.
|
||||
* `offsetX` and `offsetY` are the position of the image inside
|
||||
* the atlas image `image`.
|
||||
* @typedef {{offsetX: number, offsetY: number, image: HTMLCanvasElement}}
|
||||
*/
|
||||
ol.style.AtlasInfo;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This class facilitates the creation of image atlases.
|
||||
*
|
||||
* Images added to an atlas will be rendered onto a single
|
||||
* atlas canvas. The distribution of images on the canvas is
|
||||
* managed with the bin packing algorithm described in:
|
||||
* http://www.blackpawn.com/texts/lightmaps/
|
||||
*
|
||||
* @constructor
|
||||
* @struct
|
||||
* @param {number} size The size in pixels of the sprite image.
|
||||
* @param {number} space The space in pixels between images.
|
||||
* Because texture coordinates are float values, the edges of
|
||||
* images might not be completely correct (in a way that the
|
||||
* edges overlap when being rendered). To avoid this we add a
|
||||
* padding around each image.
|
||||
*/
|
||||
ol.style.Atlas = function(size, space) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.space_ = space;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<ol.style.Atlas.Block>}
|
||||
*/
|
||||
this.emptyBlocks_ = [{x: 0, y: 0, width: size, height: size}];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Object.<string, ol.style.AtlasInfo>}
|
||||
*/
|
||||
this.entries_ = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {HTMLCanvasElement}
|
||||
*/
|
||||
this.canvas_ = /** @type {HTMLCanvasElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.CANVAS));
|
||||
this.canvas_.width = size;
|
||||
this.canvas_.height = size;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {CanvasRenderingContext2D}
|
||||
*/
|
||||
this.context_ = /** @type {CanvasRenderingContext2D} */
|
||||
(this.canvas_.getContext('2d'));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} id The identifier of the entry to check.
|
||||
* @return {?ol.style.AtlasInfo}
|
||||
*/
|
||||
ol.style.Atlas.prototype.get = function(id) {
|
||||
return /** @type {?ol.style.AtlasInfo} */ (
|
||||
goog.object.get(this.entries_, id, null));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} id The identifier of the entry to add.
|
||||
* @param {number} width The width.
|
||||
* @param {number} height The height.
|
||||
* @param {function(CanvasRenderingContext2D, number, number)} renderCallback
|
||||
* Called to render the new image onto an atlas image.
|
||||
* @param {Object=} opt_this Value to use as `this` when executing
|
||||
* `renderCallback`.
|
||||
* @return {?ol.style.AtlasInfo} The position and atlas image for the entry.
|
||||
*/
|
||||
ol.style.Atlas.prototype.add =
|
||||
function(id, width, height, renderCallback, opt_this) {
|
||||
var block, i, ii;
|
||||
for (i = 0, ii = this.emptyBlocks_.length; i < ii; ++i) {
|
||||
block = this.emptyBlocks_[i];
|
||||
if (block.width >= width + this.space_ &&
|
||||
block.height >= height + this.space_) {
|
||||
// we found a block that is big enough for our entry
|
||||
var entry = {
|
||||
offsetX: block.x + this.space_,
|
||||
offsetY: block.y + this.space_,
|
||||
image: this.canvas_
|
||||
};
|
||||
this.entries_[id] = entry;
|
||||
|
||||
// render the image on the atlas image
|
||||
renderCallback.call(opt_this, this.context_,
|
||||
block.x + this.space_, block.y + this.space_);
|
||||
|
||||
// split the block after the insertion, either horizontally or vertically
|
||||
this.split_(i, block, width + this.space_, height + this.space_);
|
||||
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
// there is no space for the new entry in this atlas
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} index The index of the block.
|
||||
* @param {ol.style.Atlas.Block} block The block to split.
|
||||
* @param {number} width The width of the entry to insert.
|
||||
* @param {number} height The height of the entry to insert.
|
||||
*/
|
||||
ol.style.Atlas.prototype.split_ =
|
||||
function(index, block, width, height) {
|
||||
var deltaWidth = block.width - width;
|
||||
var deltaHeight = block.height - height;
|
||||
|
||||
/** @type {ol.style.Atlas.Block} */
|
||||
var newBlock1;
|
||||
/** @type {ol.style.Atlas.Block} */
|
||||
var newBlock2;
|
||||
|
||||
if (deltaWidth > deltaHeight) {
|
||||
// split vertically
|
||||
// block right of the inserted entry
|
||||
newBlock1 = {
|
||||
x: block.x + width,
|
||||
y: block.y,
|
||||
width: block.width - width,
|
||||
height: block.height
|
||||
};
|
||||
|
||||
// block below the inserted entry
|
||||
newBlock2 = {
|
||||
x: block.x,
|
||||
y: block.y + height,
|
||||
width: width,
|
||||
height: block.height - height
|
||||
};
|
||||
this.updateBlocks_(index, newBlock1, newBlock2);
|
||||
} else {
|
||||
// split horizontally
|
||||
// block right of the inserted entry
|
||||
newBlock1 = {
|
||||
x: block.x + width,
|
||||
y: block.y,
|
||||
width: block.width - width,
|
||||
height: height
|
||||
};
|
||||
|
||||
// block below the inserted entry
|
||||
newBlock2 = {
|
||||
x: block.x,
|
||||
y: block.y + height,
|
||||
width: block.width,
|
||||
height: block.height - height
|
||||
};
|
||||
this.updateBlocks_(index, newBlock1, newBlock2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Remove the old block and insert new blocks at the same array position.
|
||||
* The new blocks are inserted at the same position, so that splitted
|
||||
* blocks (that are potentially smaller) are filled first.
|
||||
* @private
|
||||
* @param {number} index The index of the block to remove.
|
||||
* @param {ol.style.Atlas.Block} newBlock1 The 1st block to add.
|
||||
* @param {ol.style.Atlas.Block} newBlock2 The 2nd block to add.
|
||||
*/
|
||||
ol.style.Atlas.prototype.updateBlocks_ =
|
||||
function(index, newBlock1, newBlock2) {
|
||||
var args = [index, 1];
|
||||
if (newBlock1.width > 0 && newBlock1.height > 0) {
|
||||
args.push(newBlock1);
|
||||
}
|
||||
if (newBlock2.width > 0 && newBlock2.height > 0) {
|
||||
args.push(newBlock2);
|
||||
}
|
||||
this.emptyBlocks_.splice.apply(this.emptyBlocks_, args);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{x: number, y: number, width: number, height: number}}
|
||||
*/
|
||||
ol.style.Atlas.Block;
|
||||
@@ -1,10 +1,12 @@
|
||||
goog.provide('ol.style.Circle');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('ol.color');
|
||||
goog.require('ol.has');
|
||||
goog.require('ol.render.canvas');
|
||||
goog.require('ol.structs.IHasChecksum');
|
||||
goog.require('ol.style.Fill');
|
||||
goog.require('ol.style.Image');
|
||||
goog.require('ol.style.ImageState');
|
||||
@@ -19,18 +21,24 @@ goog.require('ol.style.Stroke');
|
||||
* @constructor
|
||||
* @param {olx.style.CircleOptions=} opt_options Options.
|
||||
* @extends {ol.style.Image}
|
||||
* @implements {ol.structs.IHasChecksum}
|
||||
* @api
|
||||
*/
|
||||
ol.style.Circle = function(opt_options) {
|
||||
|
||||
var options = goog.isDef(opt_options) ? opt_options : {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
this.checksums_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {HTMLCanvasElement}
|
||||
*/
|
||||
this.canvas_ = /** @type {HTMLCanvasElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.CANVAS));
|
||||
this.canvas_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -46,9 +54,9 @@ ol.style.Circle = function(opt_options) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<number>}
|
||||
* @type {ol.style.Stroke}
|
||||
*/
|
||||
this.origin_ = [0, 0];
|
||||
this.stroke_ = goog.isDef(options.stroke) ? options.stroke : null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -58,23 +66,41 @@ ol.style.Circle = function(opt_options) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.style.Stroke}
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.stroke_ = goog.isDef(options.stroke) ? options.stroke : null;
|
||||
|
||||
var size = this.render_();
|
||||
this.origin_ = [0, 0];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.anchor_ = [size / 2, size / 2];
|
||||
this.hitDetectionOrigin_ = [0, 0];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.anchor_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.Size}
|
||||
*/
|
||||
this.size_ = [size, size];
|
||||
this.size_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.Size}
|
||||
*/
|
||||
this.imageSize_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.Size}
|
||||
*/
|
||||
this.hitDetectionImageSize_ = null;
|
||||
|
||||
this.render_(options.atlasManager);
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
@@ -138,6 +164,22 @@ ol.style.Circle.prototype.getImageState = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.style.Circle.prototype.getImageSize = function() {
|
||||
return this.imageSize_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.style.Circle.prototype.getHitDetectionImageSize = function() {
|
||||
return this.hitDetectionImageSize_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
@@ -147,6 +189,14 @@ ol.style.Circle.prototype.getOrigin = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.style.Circle.prototype.getHitDetectionOrigin = function() {
|
||||
return this.hitDetectionOrigin_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} Radius.
|
||||
* @api
|
||||
@@ -193,79 +243,216 @@ ol.style.Circle.prototype.unlistenImageChange = goog.nullFunction;
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @return {number} Size.
|
||||
* @typedef {{strokeStyle: (string|undefined), strokeWidth: number,
|
||||
* size: number, lineDash: Array.<number>}}
|
||||
*/
|
||||
ol.style.Circle.prototype.render_ = function() {
|
||||
var canvas = this.canvas_;
|
||||
var strokeStyle, strokeWidth, lineDash;
|
||||
ol.style.Circle.RenderOptions;
|
||||
|
||||
if (goog.isNull(this.stroke_)) {
|
||||
strokeWidth = 0;
|
||||
} else {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {ol.style.AtlasManager|undefined} atlasManager
|
||||
*/
|
||||
ol.style.Circle.prototype.render_ = function(atlasManager) {
|
||||
var imageSize;
|
||||
var lineDash = null;
|
||||
var strokeStyle;
|
||||
var strokeWidth = 0;
|
||||
|
||||
if (!goog.isNull(this.stroke_)) {
|
||||
strokeStyle = ol.color.asString(this.stroke_.getColor());
|
||||
strokeWidth = this.stroke_.getWidth();
|
||||
if (!goog.isDef(strokeWidth)) {
|
||||
strokeWidth = ol.render.canvas.defaultLineWidth;
|
||||
}
|
||||
lineDash = this.stroke_.getLineDash();
|
||||
if (!ol.has.CANVAS_LINE_DASH) {
|
||||
lineDash = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var size = 2 * (this.radius_ + strokeWidth) + 1;
|
||||
|
||||
// draw the circle on the canvas
|
||||
/** @type {ol.style.Circle.RenderOptions} */
|
||||
var renderOptions = {
|
||||
strokeStyle: strokeStyle,
|
||||
strokeWidth: strokeWidth,
|
||||
size: size,
|
||||
lineDash: lineDash
|
||||
};
|
||||
|
||||
canvas.height = size;
|
||||
canvas.width = size;
|
||||
if (!goog.isDef(atlasManager)) {
|
||||
// no atlas manager is used, create a new canvas
|
||||
this.canvas_ = /** @type {HTMLCanvasElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.CANVAS));
|
||||
this.canvas_.height = size;
|
||||
this.canvas_.width = size;
|
||||
|
||||
// canvas.width and height are rounded to the closest integer
|
||||
size = canvas.width;
|
||||
// canvas.width and height are rounded to the closest integer
|
||||
size = this.canvas_.width;
|
||||
imageSize = size;
|
||||
|
||||
var context = /** @type {CanvasRenderingContext2D} */
|
||||
(canvas.getContext('2d'));
|
||||
context.arc(size / 2, size / 2, this.radius_, 0, 2 * Math.PI, true);
|
||||
// draw the circle on the canvas
|
||||
var context = /** @type {CanvasRenderingContext2D} */
|
||||
(this.canvas_.getContext('2d'));
|
||||
this.draw_(renderOptions, context, 0, 0);
|
||||
|
||||
this.createHitDetectionCanvas_(renderOptions);
|
||||
} else {
|
||||
// an atlas manager is used, add the symbol to an atlas
|
||||
size = Math.round(size);
|
||||
|
||||
var hasCustomHitDetectionImage = goog.isNull(this.fill_);
|
||||
var renderHitDetectionCallback;
|
||||
if (hasCustomHitDetectionImage) {
|
||||
// render the hit-detection image into a separate atlas image
|
||||
renderHitDetectionCallback =
|
||||
goog.bind(this.drawHitDetectionCanvas_, this, renderOptions);
|
||||
}
|
||||
|
||||
var id = this.getChecksum();
|
||||
var info = atlasManager.add(
|
||||
id, size, size, goog.bind(this.draw_, this, renderOptions),
|
||||
renderHitDetectionCallback);
|
||||
goog.asserts.assert(info !== null, 'circle radius is too large');
|
||||
|
||||
this.canvas_ = info.image;
|
||||
this.origin_ = [info.offsetX, info.offsetY];
|
||||
imageSize = info.image.width;
|
||||
|
||||
if (hasCustomHitDetectionImage) {
|
||||
this.hitDetectionCanvas_ = info.hitImage;
|
||||
this.hitDetectionOrigin_ = [info.hitOffsetX, info.hitOffsetY];
|
||||
this.hitDetectionImageSize_ =
|
||||
[info.hitImage.width, info.hitImage.height];
|
||||
} else {
|
||||
this.hitDetectionCanvas_ = this.canvas_;
|
||||
this.hitDetectionOrigin_ = this.origin_;
|
||||
this.hitDetectionImageSize_ = [imageSize, imageSize];
|
||||
}
|
||||
}
|
||||
|
||||
this.anchor_ = [size / 2, size / 2];
|
||||
this.size_ = [size, size];
|
||||
this.imageSize_ = [imageSize, imageSize];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {ol.style.Circle.RenderOptions} renderOptions
|
||||
* @param {CanvasRenderingContext2D} context
|
||||
* @param {number} x The origin for the symbol (x).
|
||||
* @param {number} y The origin for the symbol (y).
|
||||
*/
|
||||
ol.style.Circle.prototype.draw_ = function(renderOptions, context, x, y) {
|
||||
// reset transform
|
||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
|
||||
// then move to (x, y)
|
||||
context.translate(x, y);
|
||||
|
||||
context.beginPath();
|
||||
context.arc(
|
||||
renderOptions.size / 2, renderOptions.size / 2,
|
||||
this.radius_, 0, 2 * Math.PI, true);
|
||||
|
||||
if (!goog.isNull(this.fill_)) {
|
||||
context.fillStyle = ol.color.asString(this.fill_.getColor());
|
||||
context.fill();
|
||||
}
|
||||
if (!goog.isNull(this.stroke_)) {
|
||||
context.strokeStyle = strokeStyle;
|
||||
lineDash = this.stroke_.getLineDash();
|
||||
if (ol.has.CANVAS_LINE_DASH && !goog.isNull(lineDash)) {
|
||||
context.setLineDash(lineDash);
|
||||
context.strokeStyle = renderOptions.strokeStyle;
|
||||
context.lineWidth = renderOptions.strokeWidth;
|
||||
if (!goog.isNull(renderOptions.lineDash)) {
|
||||
context.setLineDash(renderOptions.lineDash);
|
||||
}
|
||||
context.lineWidth = strokeWidth;
|
||||
context.stroke();
|
||||
}
|
||||
context.closePath();
|
||||
};
|
||||
|
||||
// deal with the hit detection canvas
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {ol.style.Circle.RenderOptions} renderOptions
|
||||
*/
|
||||
ol.style.Circle.prototype.createHitDetectionCanvas_ = function(renderOptions) {
|
||||
this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size];
|
||||
if (!goog.isNull(this.fill_)) {
|
||||
this.hitDetectionCanvas_ = canvas;
|
||||
} else {
|
||||
this.hitDetectionCanvas_ = /** @type {HTMLCanvasElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.CANVAS));
|
||||
canvas = this.hitDetectionCanvas_;
|
||||
|
||||
canvas.height = size;
|
||||
canvas.width = size;
|
||||
|
||||
context = /** @type {CanvasRenderingContext2D} */
|
||||
(canvas.getContext('2d'));
|
||||
context.arc(size / 2, size / 2, this.radius_, 0, 2 * Math.PI, true);
|
||||
|
||||
context.fillStyle = ol.render.canvas.defaultFillStyle;
|
||||
context.fill();
|
||||
if (!goog.isNull(this.stroke_)) {
|
||||
context.strokeStyle = strokeStyle;
|
||||
lineDash = this.stroke_.getLineDash();
|
||||
if (ol.has.CANVAS_LINE_DASH && !goog.isNull(lineDash)) {
|
||||
context.setLineDash(lineDash);
|
||||
}
|
||||
context.lineWidth = strokeWidth;
|
||||
context.stroke();
|
||||
}
|
||||
this.hitDetectionCanvas_ = this.canvas_;
|
||||
return;
|
||||
}
|
||||
|
||||
return size;
|
||||
// if no fill style is set, create an extra hit-detection image with a
|
||||
// default fill style
|
||||
this.hitDetectionCanvas_ = /** @type {HTMLCanvasElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.CANVAS));
|
||||
var canvas = this.hitDetectionCanvas_;
|
||||
|
||||
canvas.height = renderOptions.size;
|
||||
canvas.width = renderOptions.size;
|
||||
|
||||
var context = /** @type {CanvasRenderingContext2D} */
|
||||
(canvas.getContext('2d'));
|
||||
this.drawHitDetectionCanvas_(renderOptions, context, 0, 0);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {ol.style.Circle.RenderOptions} renderOptions
|
||||
* @param {CanvasRenderingContext2D} context
|
||||
* @param {number} x The origin for the symbol (x).
|
||||
* @param {number} y The origin for the symbol (y).
|
||||
*/
|
||||
ol.style.Circle.prototype.drawHitDetectionCanvas_ =
|
||||
function(renderOptions, context, x, y) {
|
||||
// reset transform
|
||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
|
||||
// then move to (x, y)
|
||||
context.translate(x, y);
|
||||
|
||||
context.beginPath();
|
||||
context.arc(
|
||||
renderOptions.size / 2, renderOptions.size / 2,
|
||||
this.radius_, 0, 2 * Math.PI, true);
|
||||
|
||||
context.fillStyle = ol.render.canvas.defaultFillStyle;
|
||||
context.fill();
|
||||
if (!goog.isNull(this.stroke_)) {
|
||||
context.strokeStyle = renderOptions.strokeStyle;
|
||||
context.lineWidth = renderOptions.strokeWidth;
|
||||
if (!goog.isNull(renderOptions.lineDash)) {
|
||||
context.setLineDash(renderOptions.lineDash);
|
||||
}
|
||||
context.stroke();
|
||||
}
|
||||
context.closePath();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.style.Circle.prototype.getChecksum = function() {
|
||||
var strokeChecksum = !goog.isNull(this.stroke_) ?
|
||||
this.stroke_.getChecksum() : '-';
|
||||
var fillChecksum = !goog.isNull(this.fill_) ?
|
||||
this.fill_.getChecksum() : '-';
|
||||
|
||||
var recalculate = goog.isNull(this.checksums_) ||
|
||||
(strokeChecksum != this.checksums_[1] ||
|
||||
fillChecksum != this.checksums_[2] ||
|
||||
this.radius_ != this.checksums_[3]);
|
||||
|
||||
if (recalculate) {
|
||||
var checksum = 'c' + strokeChecksum + fillChecksum +
|
||||
(goog.isDef(this.radius_) ? this.radius_.toString() : '-');
|
||||
this.checksums_ = [checksum, strokeChecksum, fillChecksum, this.radius_];
|
||||
}
|
||||
|
||||
return this.checksums_[0];
|
||||
};
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
goog.provide('ol.style.Fill');
|
||||
|
||||
goog.require('ol.color');
|
||||
goog.require('ol.structs.IHasChecksum');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@@ -8,6 +11,7 @@ goog.provide('ol.style.Fill');
|
||||
*
|
||||
* @constructor
|
||||
* @param {olx.style.FillOptions=} opt_options Options.
|
||||
* @implements {ol.structs.IHasChecksum}
|
||||
* @api
|
||||
*/
|
||||
ol.style.Fill = function(opt_options) {
|
||||
@@ -19,6 +23,12 @@ ol.style.Fill = function(opt_options) {
|
||||
* @type {ol.Color|string}
|
||||
*/
|
||||
this.color_ = goog.isDef(options.color) ? options.color : null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {string|undefined}
|
||||
*/
|
||||
this.checksum_ = undefined;
|
||||
};
|
||||
|
||||
|
||||
@@ -39,4 +49,18 @@ ol.style.Fill.prototype.getColor = function() {
|
||||
*/
|
||||
ol.style.Fill.prototype.setColor = function(color) {
|
||||
this.color_ = color;
|
||||
this.checksum_ = undefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.style.Fill.prototype.getChecksum = function() {
|
||||
if (!goog.isDef(this.checksum_)) {
|
||||
this.checksum_ = 'f' + (!goog.isNull(this.color_) ?
|
||||
ol.color.asString(this.color_) : '-');
|
||||
}
|
||||
|
||||
return this.checksum_;
|
||||
};
|
||||
|
||||
@@ -246,6 +246,14 @@ ol.style.Icon.prototype.getImageSize = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.style.Icon.prototype.getHitDetectionImageSize = function() {
|
||||
return this.getImageSize();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -293,6 +301,14 @@ ol.style.Icon.prototype.getOrigin = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.style.Icon.prototype.getHitDetectionOrigin = function() {
|
||||
return this.getOrigin();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string|undefined} Image src.
|
||||
* @api
|
||||
|
||||
@@ -126,12 +126,6 @@ ol.style.Image.prototype.getAnchor = goog.abstractMethod;
|
||||
ol.style.Image.prototype.getImage = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @return {ol.style.ImageState} Image state.
|
||||
*/
|
||||
ol.style.Image.prototype.getImageState = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @return {HTMLCanvasElement|HTMLVideoElement|Image} Image element.
|
||||
@@ -139,6 +133,24 @@ ol.style.Image.prototype.getImageState = goog.abstractMethod;
|
||||
ol.style.Image.prototype.getHitDetectionImage = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @return {ol.style.ImageState} Image state.
|
||||
*/
|
||||
ol.style.Image.prototype.getImageState = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @return {ol.Size} Image size.
|
||||
*/
|
||||
ol.style.Image.prototype.getImageSize = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @return {ol.Size} Size of the hit-detection image.
|
||||
*/
|
||||
ol.style.Image.prototype.getHitDetectionImageSize = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @return {Array.<number>} Origin.
|
||||
@@ -146,6 +158,13 @@ ol.style.Image.prototype.getHitDetectionImage = goog.abstractMethod;
|
||||
ol.style.Image.prototype.getOrigin = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @return {Array.<number>} Origin for the hit-detection image.
|
||||
*/
|
||||
ol.style.Image.prototype.getHitDetectionOrigin = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @return {ol.Size} Size.
|
||||
|
||||
@@ -22,18 +22,24 @@ goog.require('ol.style.Stroke');
|
||||
* @constructor
|
||||
* @param {olx.style.RegularShapeOptions=} opt_options Options.
|
||||
* @extends {ol.style.Image}
|
||||
* @implements {ol.structs.IHasChecksum}
|
||||
* @api
|
||||
*/
|
||||
ol.style.RegularShape = function(opt_options) {
|
||||
|
||||
var options = goog.isDef(opt_options) ? opt_options : {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
this.checksums_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {HTMLCanvasElement}
|
||||
*/
|
||||
this.canvas_ = /** @type {HTMLCanvasElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.CANVAS));
|
||||
this.canvas_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -53,6 +59,12 @@ ol.style.RegularShape = function(opt_options) {
|
||||
*/
|
||||
this.origin_ = [0, 0];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.hitDetectionOrigin_ = [0, 0];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
@@ -88,19 +100,31 @@ ol.style.RegularShape = function(opt_options) {
|
||||
*/
|
||||
this.stroke_ = goog.isDef(options.stroke) ? options.stroke : null;
|
||||
|
||||
var size = this.render_();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.anchor_ = [size / 2, size / 2];
|
||||
this.anchor_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.Size}
|
||||
*/
|
||||
this.size_ = [size, size];
|
||||
this.size_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.Size}
|
||||
*/
|
||||
this.imageSize_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {ol.Size}
|
||||
*/
|
||||
this.hitDetectionImageSize_ = null;
|
||||
|
||||
this.render_(options.atlasManager);
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
@@ -155,6 +179,22 @@ ol.style.RegularShape.prototype.getImage = function(pixelRatio) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.style.RegularShape.prototype.getImageSize = function() {
|
||||
return this.imageSize_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.style.RegularShape.prototype.getHitDetectionImageSize = function() {
|
||||
return this.hitDetectionImageSize_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -172,6 +212,14 @@ ol.style.RegularShape.prototype.getOrigin = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.style.RegularShape.prototype.getHitDetectionOrigin = function() {
|
||||
return this.hitDetectionOrigin_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} Radius.
|
||||
* @api
|
||||
@@ -227,36 +275,116 @@ ol.style.RegularShape.prototype.unlistenImageChange = goog.nullFunction;
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @return {number} Size.
|
||||
* @typedef {{strokeStyle: (string|undefined), strokeWidth: number,
|
||||
* size: number, lineDash: Array.<number>}}
|
||||
*/
|
||||
ol.style.RegularShape.prototype.render_ = function() {
|
||||
var canvas = this.canvas_;
|
||||
var strokeStyle, strokeWidth, lineDash;
|
||||
ol.style.RegularShape.RenderOptions;
|
||||
|
||||
if (goog.isNull(this.stroke_)) {
|
||||
strokeWidth = 0;
|
||||
} else {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {ol.style.AtlasManager|undefined} atlasManager
|
||||
*/
|
||||
ol.style.RegularShape.prototype.render_ = function(atlasManager) {
|
||||
var imageSize;
|
||||
var lineDash = null;
|
||||
var strokeStyle;
|
||||
var strokeWidth = 0;
|
||||
|
||||
if (!goog.isNull(this.stroke_)) {
|
||||
strokeStyle = ol.color.asString(this.stroke_.getColor());
|
||||
strokeWidth = this.stroke_.getWidth();
|
||||
if (!goog.isDef(strokeWidth)) {
|
||||
strokeWidth = ol.render.canvas.defaultLineWidth;
|
||||
}
|
||||
lineDash = this.stroke_.getLineDash();
|
||||
if (!ol.has.CANVAS_LINE_DASH) {
|
||||
lineDash = null;
|
||||
}
|
||||
}
|
||||
|
||||
var size = 2 * (this.radius_ + strokeWidth) + 1;
|
||||
|
||||
// draw the regular shape on the canvas
|
||||
/** @type {ol.style.RegularShape.RenderOptions} */
|
||||
var renderOptions = {
|
||||
strokeStyle: strokeStyle,
|
||||
strokeWidth: strokeWidth,
|
||||
size: size,
|
||||
lineDash: lineDash
|
||||
};
|
||||
|
||||
canvas.height = size;
|
||||
canvas.width = size;
|
||||
if (!goog.isDef(atlasManager)) {
|
||||
// no atlas manager is used, create a new canvas
|
||||
this.canvas_ = /** @type {HTMLCanvasElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.CANVAS));
|
||||
|
||||
// canvas.width and height are rounded to the closest integer
|
||||
size = canvas.width;
|
||||
this.canvas_.height = size;
|
||||
this.canvas_.width = size;
|
||||
|
||||
var context = /** @type {CanvasRenderingContext2D} */
|
||||
(canvas.getContext('2d'));
|
||||
// canvas.width and height are rounded to the closest integer
|
||||
size = this.canvas_.width;
|
||||
imageSize = size;
|
||||
|
||||
var context = /** @type {CanvasRenderingContext2D} */
|
||||
(this.canvas_.getContext('2d'));
|
||||
this.draw_(renderOptions, context, 0, 0);
|
||||
|
||||
this.createHitDetectionCanvas_(renderOptions);
|
||||
} else {
|
||||
// an atlas manager is used, add the symbol to an atlas
|
||||
size = Math.round(size);
|
||||
|
||||
var hasCustomHitDetectionImage = goog.isNull(this.fill_);
|
||||
var renderHitDetectionCallback;
|
||||
if (hasCustomHitDetectionImage) {
|
||||
// render the hit-detection image into a separate atlas image
|
||||
renderHitDetectionCallback =
|
||||
goog.bind(this.drawHitDetectionCanvas_, this, renderOptions);
|
||||
}
|
||||
|
||||
var id = this.getChecksum();
|
||||
var info = atlasManager.add(
|
||||
id, size, size, goog.bind(this.draw_, this, renderOptions),
|
||||
renderHitDetectionCallback);
|
||||
goog.asserts.assert(!goog.isNull(info), 'shape size is too large');
|
||||
|
||||
this.canvas_ = info.image;
|
||||
this.origin_ = [info.offsetX, info.offsetY];
|
||||
imageSize = info.image.width;
|
||||
|
||||
if (hasCustomHitDetectionImage) {
|
||||
this.hitDetectionCanvas_ = info.hitImage;
|
||||
this.hitDetectionOrigin_ = [info.hitOffsetX, info.hitOffsetY];
|
||||
this.hitDetectionImageSize_ =
|
||||
[info.hitImage.width, info.hitImage.height];
|
||||
} else {
|
||||
this.hitDetectionCanvas_ = this.canvas_;
|
||||
this.hitDetectionOrigin_ = this.origin_;
|
||||
this.hitDetectionImageSize_ = [imageSize, imageSize];
|
||||
}
|
||||
}
|
||||
|
||||
this.anchor_ = [size / 2, size / 2];
|
||||
this.size_ = [size, size];
|
||||
this.imageSize_ = [imageSize, imageSize];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {ol.style.Circle.RenderOptions} renderOptions
|
||||
* @param {CanvasRenderingContext2D} context
|
||||
* @param {number} x The origin for the symbol (x).
|
||||
* @param {number} y The origin for the symbol (y).
|
||||
*/
|
||||
ol.style.RegularShape.prototype.draw_ = function(renderOptions, context, x, y) {
|
||||
var i, angle0, radiusC;
|
||||
// reset transform
|
||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
|
||||
// then move to (x, y)
|
||||
context.translate(x, y);
|
||||
|
||||
context.beginPath();
|
||||
if (this.radius2_ !== this.radius_) {
|
||||
this.points_ = 2 * this.points_;
|
||||
@@ -264,8 +392,8 @@ ol.style.RegularShape.prototype.render_ = function() {
|
||||
for (i = 0; i <= this.points_; i++) {
|
||||
angle0 = i * 2 * Math.PI / this.points_ - Math.PI / 2 + this.angle_;
|
||||
radiusC = i % 2 === 0 ? this.radius_ : this.radius2_;
|
||||
context.lineTo(size / 2 + radiusC * Math.cos(angle0),
|
||||
size / 2 + radiusC * Math.sin(angle0));
|
||||
context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0),
|
||||
renderOptions.size / 2 + radiusC * Math.sin(angle0));
|
||||
}
|
||||
|
||||
if (!goog.isNull(this.fill_)) {
|
||||
@@ -273,52 +401,111 @@ ol.style.RegularShape.prototype.render_ = function() {
|
||||
context.fill();
|
||||
}
|
||||
if (!goog.isNull(this.stroke_)) {
|
||||
context.strokeStyle = strokeStyle;
|
||||
lineDash = this.stroke_.getLineDash();
|
||||
if (ol.has.CANVAS_LINE_DASH && !goog.isNull(lineDash)) {
|
||||
context.setLineDash(lineDash);
|
||||
context.strokeStyle = renderOptions.strokeStyle;
|
||||
context.lineWidth = renderOptions.strokeWidth;
|
||||
if (!goog.isNull(renderOptions.lineDash)) {
|
||||
context.setLineDash(renderOptions.lineDash);
|
||||
}
|
||||
context.lineWidth = strokeWidth;
|
||||
context.stroke();
|
||||
}
|
||||
context.closePath();
|
||||
};
|
||||
|
||||
// deal with the hit detection canvas
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {ol.style.RegularShape.RenderOptions} renderOptions
|
||||
*/
|
||||
ol.style.RegularShape.prototype.createHitDetectionCanvas_ =
|
||||
function(renderOptions) {
|
||||
this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size];
|
||||
if (!goog.isNull(this.fill_)) {
|
||||
this.hitDetectionCanvas_ = canvas;
|
||||
} else {
|
||||
this.hitDetectionCanvas_ = /** @type {HTMLCanvasElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.CANVAS));
|
||||
canvas = this.hitDetectionCanvas_;
|
||||
|
||||
canvas.height = size;
|
||||
canvas.width = size;
|
||||
|
||||
context = /** @type {CanvasRenderingContext2D} */
|
||||
(canvas.getContext('2d'));
|
||||
context.beginPath();
|
||||
if (this.radius2_ !== this.radius_) {
|
||||
this.points_ = 2 * this.points_;
|
||||
}
|
||||
for (i = 0; i <= this.points_; i++) {
|
||||
angle0 = i * 2 * Math.PI / this.points_ - Math.PI / 2 + this.angle_;
|
||||
radiusC = i % 2 === 0 ? this.radius_ : this.radius2_;
|
||||
context.lineTo(size / 2 + radiusC * Math.cos(angle0),
|
||||
size / 2 + radiusC * Math.sin(angle0));
|
||||
}
|
||||
|
||||
context.fillStyle = ol.render.canvas.defaultFillStyle;
|
||||
context.fill();
|
||||
if (!goog.isNull(this.stroke_)) {
|
||||
context.strokeStyle = strokeStyle;
|
||||
lineDash = this.stroke_.getLineDash();
|
||||
if (ol.has.CANVAS_LINE_DASH && !goog.isNull(lineDash)) {
|
||||
context.setLineDash(lineDash);
|
||||
}
|
||||
context.lineWidth = strokeWidth;
|
||||
context.stroke();
|
||||
}
|
||||
this.hitDetectionCanvas_ = this.canvas_;
|
||||
return;
|
||||
}
|
||||
|
||||
return size;
|
||||
// if no fill style is set, create an extra hit-detection image with a
|
||||
// default fill style
|
||||
this.hitDetectionCanvas_ = /** @type {HTMLCanvasElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.CANVAS));
|
||||
var canvas = this.hitDetectionCanvas_;
|
||||
|
||||
canvas.height = renderOptions.size;
|
||||
canvas.width = renderOptions.size;
|
||||
|
||||
var context = /** @type {CanvasRenderingContext2D} */
|
||||
(canvas.getContext('2d'));
|
||||
this.drawHitDetectionCanvas_(renderOptions, context, 0, 0);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {ol.style.RegularShape.RenderOptions} renderOptions
|
||||
* @param {CanvasRenderingContext2D} context
|
||||
* @param {number} x The origin for the symbol (x).
|
||||
* @param {number} y The origin for the symbol (y).
|
||||
*/
|
||||
ol.style.RegularShape.prototype.drawHitDetectionCanvas_ =
|
||||
function(renderOptions, context, x, y) {
|
||||
// reset transform
|
||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
|
||||
// then move to (x, y)
|
||||
context.translate(x, y);
|
||||
|
||||
context.beginPath();
|
||||
if (this.radius2_ !== this.radius_) {
|
||||
this.points_ = 2 * this.points_;
|
||||
}
|
||||
var i, radiusC, angle0;
|
||||
for (i = 0; i <= this.points_; i++) {
|
||||
angle0 = i * 2 * Math.PI / this.points_ - Math.PI / 2 + this.angle_;
|
||||
radiusC = i % 2 === 0 ? this.radius_ : this.radius2_;
|
||||
context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0),
|
||||
renderOptions.size / 2 + radiusC * Math.sin(angle0));
|
||||
}
|
||||
|
||||
context.fillStyle = ol.render.canvas.defaultFillStyle;
|
||||
context.fill();
|
||||
if (!goog.isNull(this.stroke_)) {
|
||||
context.strokeStyle = renderOptions.strokeStyle;
|
||||
context.lineWidth = renderOptions.strokeWidth;
|
||||
if (!goog.isNull(renderOptions.lineDash)) {
|
||||
context.setLineDash(renderOptions.lineDash);
|
||||
}
|
||||
context.stroke();
|
||||
}
|
||||
context.closePath();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.style.RegularShape.prototype.getChecksum = function() {
|
||||
var strokeChecksum = !goog.isNull(this.stroke_) ?
|
||||
this.stroke_.getChecksum() : '-';
|
||||
var fillChecksum = !goog.isNull(this.fill_) ?
|
||||
this.fill_.getChecksum() : '-';
|
||||
|
||||
var recalculate = goog.isNull(this.checksums_) ||
|
||||
(strokeChecksum != this.checksums_[1] ||
|
||||
fillChecksum != this.checksums_[2] ||
|
||||
this.radius_ != this.checksums_[3] ||
|
||||
this.radius2_ != this.checksums_[4] ||
|
||||
this.angle_ != this.checksums_[5] ||
|
||||
this.points_ != this.checksums_[6]);
|
||||
|
||||
if (recalculate) {
|
||||
var checksum = 'r' + strokeChecksum + fillChecksum +
|
||||
(goog.isDef(this.radius_) ? this.radius_.toString() : '-') +
|
||||
(goog.isDef(this.radius2_) ? this.radius2_.toString() : '-') +
|
||||
(goog.isDef(this.angle_) ? this.angle_.toString() : '-') +
|
||||
(goog.isDef(this.points_) ? this.points_.toString() : '-');
|
||||
this.checksums_ = [checksum, strokeChecksum, fillChecksum,
|
||||
this.radius_, this.radius2_, this.angle_, this.points_];
|
||||
}
|
||||
|
||||
return this.checksums_[0];
|
||||
};
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
goog.provide('ol.style.Stroke');
|
||||
|
||||
goog.require('goog.crypt');
|
||||
goog.require('goog.crypt.Md5');
|
||||
goog.require('ol.color');
|
||||
goog.require('ol.structs.IHasChecksum');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@@ -11,6 +16,7 @@ goog.provide('ol.style.Stroke');
|
||||
*
|
||||
* @constructor
|
||||
* @param {olx.style.StrokeOptions=} opt_options Options.
|
||||
* @implements {ol.structs.IHasChecksum}
|
||||
* @api
|
||||
*/
|
||||
ol.style.Stroke = function(opt_options) {
|
||||
@@ -52,6 +58,12 @@ ol.style.Stroke = function(opt_options) {
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
this.width_ = options.width;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {string|undefined}
|
||||
*/
|
||||
this.checksum_ = undefined;
|
||||
};
|
||||
|
||||
|
||||
@@ -117,6 +129,7 @@ ol.style.Stroke.prototype.getWidth = function() {
|
||||
*/
|
||||
ol.style.Stroke.prototype.setColor = function(color) {
|
||||
this.color_ = color;
|
||||
this.checksum_ = undefined;
|
||||
};
|
||||
|
||||
|
||||
@@ -128,6 +141,7 @@ ol.style.Stroke.prototype.setColor = function(color) {
|
||||
*/
|
||||
ol.style.Stroke.prototype.setLineCap = function(lineCap) {
|
||||
this.lineCap_ = lineCap;
|
||||
this.checksum_ = undefined;
|
||||
};
|
||||
|
||||
|
||||
@@ -139,6 +153,7 @@ ol.style.Stroke.prototype.setLineCap = function(lineCap) {
|
||||
*/
|
||||
ol.style.Stroke.prototype.setLineDash = function(lineDash) {
|
||||
this.lineDash_ = lineDash;
|
||||
this.checksum_ = undefined;
|
||||
};
|
||||
|
||||
|
||||
@@ -150,6 +165,7 @@ ol.style.Stroke.prototype.setLineDash = function(lineDash) {
|
||||
*/
|
||||
ol.style.Stroke.prototype.setLineJoin = function(lineJoin) {
|
||||
this.lineJoin_ = lineJoin;
|
||||
this.checksum_ = undefined;
|
||||
};
|
||||
|
||||
|
||||
@@ -161,6 +177,7 @@ ol.style.Stroke.prototype.setLineJoin = function(lineJoin) {
|
||||
*/
|
||||
ol.style.Stroke.prototype.setMiterLimit = function(miterLimit) {
|
||||
this.miterLimit_ = miterLimit;
|
||||
this.checksum_ = undefined;
|
||||
};
|
||||
|
||||
|
||||
@@ -172,4 +189,33 @@ ol.style.Stroke.prototype.setMiterLimit = function(miterLimit) {
|
||||
*/
|
||||
ol.style.Stroke.prototype.setWidth = function(width) {
|
||||
this.width_ = width;
|
||||
this.checksum_ = undefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.style.Stroke.prototype.getChecksum = function() {
|
||||
if (!goog.isDef(this.checksum_)) {
|
||||
var raw = 's' +
|
||||
(!goog.isNull(this.color_) ?
|
||||
ol.color.asString(this.color_) : '-') + ',' +
|
||||
(goog.isDef(this.lineCap_) ?
|
||||
this.lineCap_.toString() : '-') + ',' +
|
||||
(!goog.isNull(this.lineDash_) ?
|
||||
this.lineDash_.toString() : '-') + ',' +
|
||||
(goog.isDef(this.lineJoin_) ?
|
||||
this.lineJoin_ : '-') + ',' +
|
||||
(goog.isDef(this.miterLimit_) ?
|
||||
this.miterLimit_.toString() : '-') + ',' +
|
||||
(goog.isDef(this.width_) ?
|
||||
this.width_.toString() : '-');
|
||||
|
||||
var md5 = new goog.crypt.Md5();
|
||||
md5.update(raw);
|
||||
this.checksum_ = goog.crypt.byteArrayToString(md5.digest());
|
||||
}
|
||||
|
||||
return this.checksum_;
|
||||
};
|
||||
|
||||
56
src/ol/webgl/buffer.js
Normal file
56
src/ol/webgl/buffer.js
Normal file
@@ -0,0 +1,56 @@
|
||||
goog.provide('ol.webgl.Buffer');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.webgl');
|
||||
goog.require('ol');
|
||||
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
ol.webgl.BufferUsage = {
|
||||
STATIC_DRAW: goog.webgl.STATIC_DRAW,
|
||||
STREAM_DRAW: goog.webgl.STREAM_DRAW,
|
||||
DYNAMIC_DRAW: goog.webgl.DYNAMIC_DRAW
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Array.<number>=} opt_arr Array.
|
||||
* @param {number=} opt_usage Usage.
|
||||
* @struct
|
||||
*/
|
||||
ol.webgl.Buffer = function(opt_arr, opt_usage) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.arr_ = goog.isDef(opt_arr) ? opt_arr : [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.usage_ = goog.isDef(opt_usage) ?
|
||||
opt_usage : ol.webgl.BufferUsage.STATIC_DRAW;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Array.<number>} Array.
|
||||
*/
|
||||
ol.webgl.Buffer.prototype.getArray = function() {
|
||||
return this.arr_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} Usage.
|
||||
*/
|
||||
ol.webgl.Buffer.prototype.getUsage = function() {
|
||||
return this.usage_;
|
||||
};
|
||||
@@ -1,18 +1,18 @@
|
||||
goog.provide('ol.webgl.Context');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.object');
|
||||
goog.require('ol.structs.Buffer');
|
||||
goog.require('ol.structs.IntegerSet');
|
||||
goog.require('ol');
|
||||
goog.require('ol.webgl.Buffer');
|
||||
goog.require('ol.webgl.WebGLContextEventType');
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{buf: ol.structs.Buffer,
|
||||
* buffer: WebGLBuffer,
|
||||
* dirtySet: ol.structs.IntegerSet}}
|
||||
* @typedef {{buf: ol.webgl.Buffer,
|
||||
* buffer: WebGLBuffer}}
|
||||
*/
|
||||
ol.webgl.BufferCacheEntry;
|
||||
|
||||
@@ -66,6 +66,18 @@ ol.webgl.Context = function(canvas, gl) {
|
||||
*/
|
||||
this.currentProgram_ = null;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.hasOESElementIndexUint = goog.array.contains(
|
||||
ol.WEBGL_EXTENSIONS, 'OES_element_index_uint');
|
||||
|
||||
// use the OES_element_index_uint extension if available
|
||||
if (this.hasOESElementIndexUint) {
|
||||
var ext = gl.getExtension('OES_element_index_uint');
|
||||
goog.asserts.assert(!goog.isNull(ext));
|
||||
}
|
||||
|
||||
goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.LOST,
|
||||
this.handleWebGLContextLost, false, this);
|
||||
goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.RESTORED,
|
||||
@@ -75,8 +87,11 @@ ol.webgl.Context = function(canvas, gl) {
|
||||
|
||||
|
||||
/**
|
||||
* Just bind the buffer if it's in the cache. Otherwise create
|
||||
* the WebGL buffer, bind it, populate it, and add an entry to
|
||||
* the cache.
|
||||
* @param {number} target Target.
|
||||
* @param {ol.structs.Buffer} buf Buffer.
|
||||
* @param {ol.webgl.Buffer} buf Buffer.
|
||||
*/
|
||||
ol.webgl.Context.prototype.bindBuffer = function(target, buf) {
|
||||
var gl = this.getGL();
|
||||
@@ -85,45 +100,37 @@ ol.webgl.Context.prototype.bindBuffer = function(target, 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);
|
||||
goog.asserts.assert(target == goog.webgl.ARRAY_BUFFER ||
|
||||
target == goog.webgl.ELEMENT_ARRAY_BUFFER);
|
||||
var /** @type {ArrayBufferView} */ arrayBuffer;
|
||||
if (target == goog.webgl.ARRAY_BUFFER) {
|
||||
arrayBuffer = new Float32Array(arr);
|
||||
} else if (target == goog.webgl.ELEMENT_ARRAY_BUFFER) {
|
||||
arrayBuffer = this.hasOESElementIndexUint ?
|
||||
new Uint32Array(arr) : new Uint16Array(arr);
|
||||
} else {
|
||||
goog.asserts.fail();
|
||||
}
|
||||
gl.bufferData(target, arrayBuffer, buf.getUsage());
|
||||
this.bufferCache_[bufferKey] = {
|
||||
buf: buf,
|
||||
buffer: buffer,
|
||||
dirtySet: dirtySet
|
||||
buffer: buffer
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.structs.Buffer} buf Buffer.
|
||||
* @param {ol.webgl.Buffer} buf Buffer.
|
||||
*/
|
||||
ol.webgl.Context.prototype.deleteBuffer = function(buf) {
|
||||
var gl = this.getGL();
|
||||
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);
|
||||
}
|
||||
@@ -135,9 +142,6 @@ ol.webgl.Context.prototype.deleteBuffer = function(buf) {
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.webgl.Context.prototype.disposeInternal = function() {
|
||||
goog.object.forEach(this.bufferCache_, function(bufferCacheEntry) {
|
||||
bufferCacheEntry.buf.removeDirtySet(bufferCacheEntry.dirtySet);
|
||||
});
|
||||
var gl = this.getGL();
|
||||
if (!gl.isContextLost()) {
|
||||
goog.object.forEach(this.bufferCache_, function(bufferCacheEntry) {
|
||||
@@ -171,6 +175,8 @@ ol.webgl.Context.prototype.getGL = function() {
|
||||
|
||||
|
||||
/**
|
||||
* Get shader from the cache if it's in the cache. Otherwise, create
|
||||
* the WebGL shader, compile it, and add entry to cache.
|
||||
* @param {ol.webgl.Shader} shaderObject Shader object.
|
||||
* @return {WebGLShader} Shader.
|
||||
*/
|
||||
@@ -199,6 +205,9 @@ ol.webgl.Context.prototype.getShader = function(shaderObject) {
|
||||
|
||||
|
||||
/**
|
||||
* Get the program from the cache if it's in the cache. Otherwise create
|
||||
* the WebGL program, attach the shaders to it, and add an entry to the
|
||||
* cache.
|
||||
* @param {ol.webgl.shader.Fragment} fragmentShaderObject Fragment shader.
|
||||
* @param {ol.webgl.shader.Vertex} vertexShaderObject Vertex shader.
|
||||
* @return {WebGLProgram} Program.
|
||||
@@ -249,6 +258,9 @@ ol.webgl.Context.prototype.handleWebGLContextRestored = function() {
|
||||
|
||||
|
||||
/**
|
||||
* Just return false if that program is used already. Other use
|
||||
* that program (call `gl.useProgram`) and make it the "current
|
||||
* program".
|
||||
* @param {WebGLProgram} program Program.
|
||||
* @return {boolean} Changed.
|
||||
* @api
|
||||
|
||||
158
test/spec/ol/render/webglreplay.test.js
Normal file
158
test/spec/ol/render/webglreplay.test.js
Normal file
@@ -0,0 +1,158 @@
|
||||
goog.provide('ol.test.render.webgl.Replay');
|
||||
|
||||
describe('ol.render.webgl.ImageReplay', function() {
|
||||
var replay;
|
||||
|
||||
var createImageStyle = function(image) {
|
||||
var imageStyle = new ol.style.Image({
|
||||
opacity: 0.1,
|
||||
rotateWithView: true,
|
||||
rotation: 1.5,
|
||||
scale: 2.0
|
||||
});
|
||||
imageStyle.getAnchor = function() {
|
||||
return [0.5, 1];
|
||||
};
|
||||
imageStyle.getImage = function() {
|
||||
return image;
|
||||
};
|
||||
imageStyle.getImageSize = function() {
|
||||
return [512, 512];
|
||||
};
|
||||
imageStyle.getOrigin = function() {
|
||||
return [200, 200];
|
||||
};
|
||||
imageStyle.getSize = function() {
|
||||
return [256, 256];
|
||||
};
|
||||
return imageStyle;
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
var tolerance = 0.1;
|
||||
var maxExtent = [-10000, -20000, 10000, 20000];
|
||||
replay = new ol.render.webgl.ImageReplay(tolerance, maxExtent);
|
||||
});
|
||||
|
||||
describe('#setImageStyle', function() {
|
||||
|
||||
var imageStyle1, imageStyle2;
|
||||
|
||||
beforeEach(function() {
|
||||
imageStyle1 = createImageStyle(new Image());
|
||||
imageStyle2 = createImageStyle(new Image());
|
||||
});
|
||||
|
||||
it('set expected states', function() {
|
||||
replay.setImageStyle(imageStyle1);
|
||||
expect(replay.anchorX_).to.be(0.5);
|
||||
expect(replay.anchorY_).to.be(1);
|
||||
expect(replay.height_).to.be(256);
|
||||
expect(replay.imageHeight_).to.be(512);
|
||||
expect(replay.imageWidth_).to.be(512);
|
||||
expect(replay.opacity_).to.be(0.1);
|
||||
expect(replay.originX_).to.be(200);
|
||||
expect(replay.originY_).to.be(200);
|
||||
expect(replay.rotation_).to.be(1.5);
|
||||
expect(replay.rotateWithView_).to.be(true);
|
||||
expect(replay.scale_).to.be(2.0);
|
||||
expect(replay.width_).to.be(256);
|
||||
expect(replay.images_).to.have.length(1);
|
||||
expect(replay.groupIndices_).to.have.length(0);
|
||||
|
||||
replay.setImageStyle(imageStyle1);
|
||||
expect(replay.images_).to.have.length(1);
|
||||
expect(replay.groupIndices_).to.have.length(0);
|
||||
|
||||
replay.setImageStyle(imageStyle2);
|
||||
expect(replay.images_).to.have.length(2);
|
||||
expect(replay.groupIndices_).to.have.length(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#drawPointGeometry', function() {
|
||||
beforeEach(function() {
|
||||
var imageStyle = createImageStyle(new Image());
|
||||
replay.setImageStyle(imageStyle);
|
||||
});
|
||||
|
||||
it('sets the buffer data', function() {
|
||||
var point;
|
||||
|
||||
point = new ol.geom.Point([1000, 2000]);
|
||||
replay.drawPointGeometry(point, null);
|
||||
expect(replay.vertices_).to.have.length(32);
|
||||
expect(replay.indices_).to.have.length(6);
|
||||
expect(replay.indices_[0]).to.be(0);
|
||||
expect(replay.indices_[1]).to.be(1);
|
||||
expect(replay.indices_[2]).to.be(2);
|
||||
expect(replay.indices_[3]).to.be(0);
|
||||
expect(replay.indices_[4]).to.be(2);
|
||||
expect(replay.indices_[5]).to.be(3);
|
||||
|
||||
point = new ol.geom.Point([2000, 3000]);
|
||||
replay.drawPointGeometry(point, null);
|
||||
expect(replay.vertices_).to.have.length(64);
|
||||
expect(replay.indices_).to.have.length(12);
|
||||
expect(replay.indices_[6]).to.be(4);
|
||||
expect(replay.indices_[7]).to.be(5);
|
||||
expect(replay.indices_[8]).to.be(6);
|
||||
expect(replay.indices_[9]).to.be(4);
|
||||
expect(replay.indices_[10]).to.be(6);
|
||||
expect(replay.indices_[11]).to.be(7);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#drawMultiPointGeometry', function() {
|
||||
beforeEach(function() {
|
||||
var imageStyle = createImageStyle(new Image());
|
||||
replay.setImageStyle(imageStyle);
|
||||
});
|
||||
|
||||
it('sets the buffer data', function() {
|
||||
var multiPoint;
|
||||
|
||||
multiPoint = new ol.geom.MultiPoint(
|
||||
[[1000, 2000], [2000, 3000]]);
|
||||
replay.drawMultiPointGeometry(multiPoint, null);
|
||||
expect(replay.vertices_).to.have.length(64);
|
||||
expect(replay.indices_).to.have.length(12);
|
||||
expect(replay.indices_[0]).to.be(0);
|
||||
expect(replay.indices_[1]).to.be(1);
|
||||
expect(replay.indices_[2]).to.be(2);
|
||||
expect(replay.indices_[3]).to.be(0);
|
||||
expect(replay.indices_[4]).to.be(2);
|
||||
expect(replay.indices_[5]).to.be(3);
|
||||
expect(replay.indices_[6]).to.be(4);
|
||||
expect(replay.indices_[7]).to.be(5);
|
||||
expect(replay.indices_[8]).to.be(6);
|
||||
expect(replay.indices_[9]).to.be(4);
|
||||
expect(replay.indices_[10]).to.be(6);
|
||||
expect(replay.indices_[11]).to.be(7);
|
||||
|
||||
multiPoint = new ol.geom.MultiPoint(
|
||||
[[3000, 4000], [4000, 5000]]);
|
||||
replay.drawMultiPointGeometry(multiPoint, null);
|
||||
expect(replay.vertices_).to.have.length(128);
|
||||
expect(replay.indices_).to.have.length(24);
|
||||
expect(replay.indices_[12]).to.be(8);
|
||||
expect(replay.indices_[13]).to.be(9);
|
||||
expect(replay.indices_[14]).to.be(10);
|
||||
expect(replay.indices_[15]).to.be(8);
|
||||
expect(replay.indices_[16]).to.be(10);
|
||||
expect(replay.indices_[17]).to.be(11);
|
||||
expect(replay.indices_[18]).to.be(12);
|
||||
expect(replay.indices_[19]).to.be(13);
|
||||
expect(replay.indices_[20]).to.be(14);
|
||||
expect(replay.indices_[21]).to.be(12);
|
||||
expect(replay.indices_[22]).to.be(14);
|
||||
expect(replay.indices_[23]).to.be(15);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
goog.require('ol.extent');
|
||||
goog.require('ol.geom.MultiPoint');
|
||||
goog.require('ol.geom.Point');
|
||||
goog.require('ol.render.webgl.ImageReplay');
|
||||
goog.require('ol.style.Image');
|
||||
@@ -1,297 +0,0 @@
|
||||
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.eql([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.eql([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.eql([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.eql([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.eql([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.eql([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.eql([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.eql([4, 8]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
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() {
|
||||
var b = new ol.structs.Buffer(new Array(8), 0);
|
||||
expect(b.add([0, 1])).to.be(0);
|
||||
expect(b.getArray()).to.arreqlNaN([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.arreqlNaN([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.eql([0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
expect(b.getCount()).to.be(8);
|
||||
b.remove(2, 2);
|
||||
expect(b.getArray()).to.arreqlNaN([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.eql([0, 1, 8, 9, 4, 5, 6, 7]);
|
||||
expect(b.getCount()).to.be(8);
|
||||
b.remove(1, 1);
|
||||
expect(b.getArray()).to.arreqlNaN([0, NaN, 8, 9, 4, 5, 6, 7]);
|
||||
expect(b.getCount()).to.be(7);
|
||||
b.remove(4, 4);
|
||||
expect(b.getArray()).to.arreqlNaN([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.arreqlNaN([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.arreqlNaN([0, 13, 8, 9, 10, 11, 12, NaN]);
|
||||
expect(b.getCount()).to.be(7);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
goog.require('ol.structs.Buffer');
|
||||
goog.require('ol.structs.IntegerSet');
|
||||
@@ -1,622 +0,0 @@
|
||||
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.eql([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]);
|
||||
is = is; // suppress gjslint warning about unused variable
|
||||
}).to.throwException();
|
||||
});
|
||||
|
||||
it('throws an exception with out-of-order elements', function() {
|
||||
expect(function() {
|
||||
var is = new ol.structs.IntegerSet([0, 2, 2, 4]);
|
||||
is = is; // suppress gjslint warning about unused variable
|
||||
}).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.eql([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.eql([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.eql([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.eql([0, 6, 8, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('extends the first element to the right', function() {
|
||||
is.addRange(6, 7);
|
||||
expect(is.getArray()).to.eql([4, 7, 8, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('merges the first two elements', function() {
|
||||
is.addRange(6, 8);
|
||||
expect(is.getArray()).to.eql([4, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('extends middle elements to the left', function() {
|
||||
is.addRange(7, 8);
|
||||
expect(is.getArray()).to.eql([4, 6, 7, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('extends middle elements to the right', function() {
|
||||
is.addRange(10, 11);
|
||||
expect(is.getArray()).to.eql([4, 6, 8, 11, 12, 14]);
|
||||
});
|
||||
|
||||
it('merges the last two elements', function() {
|
||||
is.addRange(10, 12);
|
||||
expect(is.getArray()).to.eql([4, 6, 8, 14]);
|
||||
});
|
||||
|
||||
it('extends the last element to the left', function() {
|
||||
is.addRange(11, 12);
|
||||
expect(is.getArray()).to.eql([4, 6, 8, 10, 11, 14]);
|
||||
});
|
||||
|
||||
it('extends the last element to the right', function() {
|
||||
is.addRange(14, 15);
|
||||
expect(is.getArray()).to.eql([4, 6, 8, 10, 12, 15]);
|
||||
});
|
||||
|
||||
it('inserts after the last element', function() {
|
||||
is.addRange(16, 18);
|
||||
expect(is.getArray()).to.eql([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.eql([4, 6]);
|
||||
expect(callback.args[1]).to.eql([8, 10]);
|
||||
expect(callback.args[2]).to.eql([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.eql([0, 4]);
|
||||
expect(callback.args[1]).to.eql([6, 8]);
|
||||
expect(callback.args[2]).to.eql([10, 12]);
|
||||
expect(callback.args[3]).to.eql([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.eql([5, 6, 8, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('removes the last part of the first element', function() {
|
||||
is.removeRange(5, 6);
|
||||
expect(is.getArray()).to.eql([4, 5, 8, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('removes the first element', function() {
|
||||
is.removeRange(4, 6);
|
||||
expect(is.getArray()).to.eql([8, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('removes the first part of a middle element', function() {
|
||||
is.removeRange(8, 9);
|
||||
expect(is.getArray()).to.eql([4, 6, 9, 10, 12, 14]);
|
||||
});
|
||||
|
||||
it('removes the last part of a middle element', function() {
|
||||
is.removeRange(9, 10);
|
||||
expect(is.getArray()).to.eql([4, 6, 8, 9, 12, 14]);
|
||||
});
|
||||
|
||||
it('removes a middle element', function() {
|
||||
is.removeRange(8, 10);
|
||||
expect(is.getArray()).to.eql([4, 6, 12, 14]);
|
||||
});
|
||||
|
||||
it('removes the first part of the last element', function() {
|
||||
is.removeRange(12, 13);
|
||||
expect(is.getArray()).to.eql([4, 6, 8, 10, 13, 14]);
|
||||
});
|
||||
|
||||
it('removes the last part of the last element', function() {
|
||||
is.removeRange(13, 14);
|
||||
expect(is.getArray()).to.eql([4, 6, 8, 10, 12, 13]);
|
||||
});
|
||||
|
||||
it('removes the last element', function() {
|
||||
is.removeRange(12, 14);
|
||||
expect(is.getArray()).to.eql([4, 6, 8, 10]);
|
||||
});
|
||||
|
||||
it('can remove multiple ranges near the start', function() {
|
||||
is.removeRange(3, 11);
|
||||
expect(is.getArray()).to.eql([12, 14]);
|
||||
});
|
||||
|
||||
it('can remove multiple ranges near the start', function() {
|
||||
is.removeRange(7, 15);
|
||||
expect(is.getArray()).to.eql([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.eql(
|
||||
[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.eql(
|
||||
[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.eql([0, 1, 13, 15, 16, 17]);
|
||||
});
|
||||
|
||||
it('can remove multiple ranges and reduce others', function() {
|
||||
is.removeRange(0, 10);
|
||||
expect(is.getArray()).to.eql([10, 12, 13, 15, 16, 17]);
|
||||
});
|
||||
|
||||
it('can remove all ranges', function() {
|
||||
is.removeRange(0, 18);
|
||||
expect(is.getArray()).to.eql([]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
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.eql(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.eql(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.eql(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.eql(sis.getArray());
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.math');
|
||||
goog.require('goog.object');
|
||||
goog.require('ol.structs.IntegerSet');
|
||||
273
test/spec/ol/style/atlasmanager.test.js
Normal file
273
test/spec/ol/style/atlasmanager.test.js
Normal file
@@ -0,0 +1,273 @@
|
||||
goog.provide('ol.test.style.AtlasManager');
|
||||
|
||||
|
||||
describe('ol.style.Atlas', function() {
|
||||
|
||||
var defaultRender = function(context, x, y) {
|
||||
};
|
||||
|
||||
describe('#constructor', function() {
|
||||
|
||||
it('inits the atlas', function() {
|
||||
var atlas = new ol.style.Atlas(256, 1);
|
||||
expect(atlas.emptyBlocks_).to.eql(
|
||||
[{x: 0, y: 0, width: 256, height: 256}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#add (squares with same size)', function() {
|
||||
|
||||
it('adds one entry', function() {
|
||||
var atlas = new ol.style.Atlas(128, 1);
|
||||
var info = atlas.add('1', 32, 32, defaultRender);
|
||||
|
||||
expect(info).to.eql(
|
||||
{offsetX: 1, offsetY: 1, image: atlas.canvas_});
|
||||
|
||||
expect(atlas.get('1')).to.eql(info);
|
||||
});
|
||||
|
||||
it('adds two entries', function() {
|
||||
var atlas = new ol.style.Atlas(128, 1);
|
||||
|
||||
atlas.add('1', 32, 32, defaultRender);
|
||||
var info = atlas.add('2', 32, 32, defaultRender);
|
||||
|
||||
expect(info).to.eql(
|
||||
{offsetX: 34, offsetY: 1, image: atlas.canvas_});
|
||||
|
||||
expect(atlas.get('2')).to.eql(info);
|
||||
});
|
||||
|
||||
it('adds three entries', function() {
|
||||
var atlas = new ol.style.Atlas(128, 1);
|
||||
|
||||
atlas.add('1', 32, 32, defaultRender);
|
||||
atlas.add('2', 32, 32, defaultRender);
|
||||
var info = atlas.add('3', 32, 32, defaultRender);
|
||||
|
||||
expect(info).to.eql(
|
||||
{offsetX: 67, offsetY: 1, image: atlas.canvas_});
|
||||
|
||||
expect(atlas.get('3')).to.eql(info);
|
||||
});
|
||||
|
||||
it('adds four entries (new row)', function() {
|
||||
var atlas = new ol.style.Atlas(128, 1);
|
||||
|
||||
atlas.add('1', 32, 32, defaultRender);
|
||||
atlas.add('2', 32, 32, defaultRender);
|
||||
atlas.add('3', 32, 32, defaultRender);
|
||||
var info = atlas.add('4', 32, 32, defaultRender);
|
||||
|
||||
expect(info).to.eql(
|
||||
{offsetX: 1, offsetY: 34, image: atlas.canvas_});
|
||||
|
||||
expect(atlas.get('4')).to.eql(info);
|
||||
});
|
||||
|
||||
it('returns null when an entry is too big', function() {
|
||||
var atlas = new ol.style.Atlas(128, 1);
|
||||
|
||||
atlas.add('1', 32, 32, defaultRender);
|
||||
atlas.add('2', 32, 32, defaultRender);
|
||||
atlas.add('3', 32, 32, defaultRender);
|
||||
var info = atlas.add(4, 100, 100, defaultRender);
|
||||
|
||||
expect(info).to.eql(null);
|
||||
});
|
||||
|
||||
it('fills up the whole atlas', function() {
|
||||
var atlas = new ol.style.Atlas(128, 1);
|
||||
|
||||
for (var i = 1; i <= 16; i++) {
|
||||
expect(atlas.add(i.toString(), 28, 28, defaultRender)).to.be.ok();
|
||||
}
|
||||
|
||||
// there is no more space for items of this size, the next one will fail
|
||||
expect(atlas.add('17', 28, 28, defaultRender)).to.eql(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#add (rectangles with different sizes)', function() {
|
||||
|
||||
it('adds a bunch of rectangles', function() {
|
||||
var atlas = new ol.style.Atlas(128, 1);
|
||||
|
||||
expect(atlas.add('1', 64, 32, defaultRender)).to.eql(
|
||||
{offsetX: 1, offsetY: 1, image: atlas.canvas_});
|
||||
|
||||
expect(atlas.add('2', 64, 32, defaultRender)).to.eql(
|
||||
{offsetX: 1, offsetY: 34, image: atlas.canvas_});
|
||||
|
||||
expect(atlas.add('3', 64, 32, defaultRender)).to.eql(
|
||||
{offsetX: 1, offsetY: 67, image: atlas.canvas_});
|
||||
|
||||
// this one can not be added anymore
|
||||
expect(atlas.add('4', 64, 32, defaultRender)).to.eql(null);
|
||||
|
||||
// but there is still room for smaller ones
|
||||
expect(atlas.add('5', 40, 32, defaultRender)).to.eql(
|
||||
{offsetX: 66, offsetY: 1, image: atlas.canvas_});
|
||||
|
||||
expect(atlas.add('6', 40, 32, defaultRender)).to.eql(
|
||||
{offsetX: 66, offsetY: 34, image: atlas.canvas_});
|
||||
});
|
||||
|
||||
it('fills up the whole atlas (rectangles in portrait format)', function() {
|
||||
var atlas = new ol.style.Atlas(128, 1);
|
||||
|
||||
for (var i = 1; i <= 32; i++) {
|
||||
expect(atlas.add(i.toString(), 28, 14, defaultRender)).to.be.ok();
|
||||
}
|
||||
|
||||
// there is no more space for items of this size, the next one will fail
|
||||
expect(atlas.add('33', 28, 14, defaultRender)).to.eql(null);
|
||||
});
|
||||
|
||||
it('fills up the whole atlas (rectangles in landscape format)', function() {
|
||||
var atlas = new ol.style.Atlas(128, 1);
|
||||
|
||||
for (var i = 1; i <= 32; i++) {
|
||||
expect(atlas.add(i.toString(), 14, 28, defaultRender)).to.be.ok();
|
||||
}
|
||||
|
||||
// there is no more space for items of this size, the next one will fail
|
||||
expect(atlas.add('33', 14, 28, defaultRender)).to.eql(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#add (rendering)', function() {
|
||||
|
||||
it('calls the render callback with the right values', function() {
|
||||
var atlas = new ol.style.Atlas(128, 1);
|
||||
var rendererCallback = sinon.spy();
|
||||
atlas.add('1', 32, 32, rendererCallback);
|
||||
|
||||
expect(rendererCallback.calledOnce).to.be.ok();
|
||||
expect(rendererCallback.calledWith(atlas.context_, 1, 1)).to.be.ok();
|
||||
|
||||
rendererCallback = sinon.spy();
|
||||
atlas.add('2', 32, 32, rendererCallback);
|
||||
|
||||
expect(rendererCallback.calledOnce).to.be.ok();
|
||||
expect(rendererCallback.calledWith(atlas.context_, 34, 1)).to.be.ok();
|
||||
});
|
||||
|
||||
it('is possible to actually draw on the canvas', function() {
|
||||
var atlas = new ol.style.Atlas(128, 1);
|
||||
|
||||
var rendererCallback = function(context, x, y) {
|
||||
context.fillStyle = '#FFA500';
|
||||
context.fillRect(x, y, 32, 32);
|
||||
};
|
||||
|
||||
expect(atlas.add('1', 32, 32, rendererCallback)).to.be.ok();
|
||||
expect(atlas.add('2', 32, 32, rendererCallback)).to.be.ok();
|
||||
// no error, ok
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('ol.style.AtlasManager', function() {
|
||||
|
||||
var defaultRender = function(context, x, y) {
|
||||
};
|
||||
|
||||
describe('#constructor', function() {
|
||||
|
||||
it('inits the atlas manager', function() {
|
||||
var manager = new ol.style.AtlasManager();
|
||||
expect(manager.atlases_).to.not.be.empty();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#add', function() {
|
||||
|
||||
it('adds one entry', function() {
|
||||
var manager = new ol.style.AtlasManager({initialSize: 128});
|
||||
var info = manager.add('1', 32, 32, defaultRender);
|
||||
|
||||
expect(info).to.eql({
|
||||
offsetX: 1, offsetY: 1, image: manager.atlases_[0].canvas_,
|
||||
hitOffsetX: undefined, hitOffsetY: undefined, hitImage: undefined});
|
||||
|
||||
expect(manager.getInfo('1')).to.eql(info);
|
||||
});
|
||||
|
||||
it('adds one entry (also to the hit detection atlas)', function() {
|
||||
var manager = new ol.style.AtlasManager({initialSize: 128});
|
||||
var info = manager.add('1', 32, 32, defaultRender, defaultRender);
|
||||
|
||||
expect(info).to.eql({
|
||||
offsetX: 1, offsetY: 1, image: manager.atlases_[0].canvas_,
|
||||
hitOffsetX: 1, hitOffsetY: 1,
|
||||
hitImage: manager.hitAtlases_[0].canvas_});
|
||||
|
||||
expect(manager.getInfo('1')).to.eql(info);
|
||||
});
|
||||
|
||||
it('creates a new atlas if needed', function() {
|
||||
var manager = new ol.style.AtlasManager({initialSize: 128});
|
||||
expect(manager.add('1', 100, 100, defaultRender, defaultRender))
|
||||
.to.be.ok();
|
||||
var info = manager.add('2', 100, 100, defaultRender, defaultRender);
|
||||
expect(info).to.be.ok();
|
||||
expect(info.image.width).to.eql(256);
|
||||
expect(manager.atlases_).to.have.length(2);
|
||||
expect(info.hitImage.width).to.eql(256);
|
||||
expect(manager.hitAtlases_).to.have.length(2);
|
||||
});
|
||||
|
||||
it('creates new atlases until one is large enough', function() {
|
||||
var manager = new ol.style.AtlasManager({initialSize: 128});
|
||||
expect(manager.add('1', 100, 100, defaultRender, defaultRender))
|
||||
.to.be.ok();
|
||||
expect(manager.atlases_).to.have.length(1);
|
||||
expect(manager.hitAtlases_).to.have.length(1);
|
||||
var info = manager.add('2', 500, 500, defaultRender, defaultRender);
|
||||
expect(info).to.be.ok();
|
||||
expect(info.image.width).to.eql(512);
|
||||
expect(manager.atlases_).to.have.length(3);
|
||||
expect(info.hitImage.width).to.eql(512);
|
||||
expect(manager.hitAtlases_).to.have.length(3);
|
||||
});
|
||||
|
||||
it('checks all existing atlases and create a new if needed', function() {
|
||||
var manager = new ol.style.AtlasManager({initialSize: 128});
|
||||
expect(manager.add('1', 100, 100, defaultRender, defaultRender))
|
||||
.to.be.ok();
|
||||
expect(manager.add('2', 100, 100, defaultRender, defaultRender))
|
||||
.to.be.ok();
|
||||
expect(manager.atlases_).to.have.length(2);
|
||||
expect(manager.hitAtlases_).to.have.length(2);
|
||||
var info = manager.add(3, 500, 500, defaultRender, defaultRender);
|
||||
expect(info).to.be.ok();
|
||||
expect(info.image.width).to.eql(512);
|
||||
expect(manager.atlases_).to.have.length(3);
|
||||
expect(info.hitImage.width).to.eql(512);
|
||||
expect(manager.hitAtlases_).to.have.length(3);
|
||||
});
|
||||
|
||||
it('returns null if the size exceeds the maximum size', function() {
|
||||
var manager = new ol.style.AtlasManager(
|
||||
{initialSize: 128, maxSize: 2048});
|
||||
expect(manager.add('1', 100, 100, defaultRender, defaultRender))
|
||||
.to.be.ok();
|
||||
expect(manager.add('2', 2048, 2048, defaultRender, defaultRender))
|
||||
.to.eql(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getInfo', function() {
|
||||
|
||||
it('returns null if no entry for the given id', function() {
|
||||
var manager = new ol.style.AtlasManager({initialSize: 128});
|
||||
expect(manager.getInfo('123456')).to.eql(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
goog.require('ol.style.Atlas');
|
||||
goog.require('ol.style.AtlasManager');
|
||||
240
test/spec/ol/style/circlestyle.test.js
Normal file
240
test/spec/ol/style/circlestyle.test.js
Normal file
@@ -0,0 +1,240 @@
|
||||
goog.provide('ol.test.style.Circle');
|
||||
|
||||
|
||||
describe('ol.style.Circle', function() {
|
||||
|
||||
describe('#constructor', function() {
|
||||
|
||||
it('creates a canvas if no atlas is used (no fill-style)', function() {
|
||||
var style = new ol.style.Circle({radius: 10});
|
||||
expect(style.getImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getSize()).to.eql([21, 21]);
|
||||
expect(style.getImageSize()).to.eql([21, 21]);
|
||||
expect(style.getOrigin()).to.eql([0, 0]);
|
||||
expect(style.getAnchor()).to.eql([10.5, 10.5]);
|
||||
// hit-detection image is created, because no fill style is set
|
||||
expect(style.getImage()).to.not.be(style.getHitDetectionImage());
|
||||
expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getHitDetectionImageSize()).to.eql([21, 21]);
|
||||
expect(style.getHitDetectionOrigin()).to.eql([0, 0]);
|
||||
});
|
||||
|
||||
it('creates a canvas if no atlas is used (fill-style)', function() {
|
||||
var style = new ol.style.Circle({
|
||||
radius: 10,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#FFFF00'
|
||||
})
|
||||
});
|
||||
expect(style.getImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getSize()).to.eql([21, 21]);
|
||||
expect(style.getImageSize()).to.eql([21, 21]);
|
||||
expect(style.getOrigin()).to.eql([0, 0]);
|
||||
expect(style.getAnchor()).to.eql([10.5, 10.5]);
|
||||
// no hit-detection image is created, because fill style is set
|
||||
expect(style.getImage()).to.be(style.getHitDetectionImage());
|
||||
expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getHitDetectionImageSize()).to.eql([21, 21]);
|
||||
expect(style.getHitDetectionOrigin()).to.eql([0, 0]);
|
||||
});
|
||||
|
||||
it('adds itself to an atlas manager (no fill-style)', function() {
|
||||
var atlasManager = new ol.style.AtlasManager({initialSize: 512});
|
||||
var style = new ol.style.Circle({radius: 10, atlasManager: atlasManager});
|
||||
expect(style.getImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getSize()).to.eql([21, 21]);
|
||||
expect(style.getImageSize()).to.eql([512, 512]);
|
||||
expect(style.getOrigin()).to.eql([1, 1]);
|
||||
expect(style.getAnchor()).to.eql([10.5, 10.5]);
|
||||
// hit-detection image is created, because no fill style is set
|
||||
expect(style.getImage()).to.not.be(style.getHitDetectionImage());
|
||||
expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getHitDetectionImageSize()).to.eql([512, 512]);
|
||||
expect(style.getHitDetectionOrigin()).to.eql([1, 1]);
|
||||
});
|
||||
|
||||
it('adds itself to an atlas manager (fill-style)', function() {
|
||||
var atlasManager = new ol.style.AtlasManager({initialSize: 512});
|
||||
var style = new ol.style.Circle({
|
||||
radius: 10,
|
||||
atlasManager: atlasManager,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#FFFF00'
|
||||
})
|
||||
});
|
||||
expect(style.getImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getSize()).to.eql([21, 21]);
|
||||
expect(style.getImageSize()).to.eql([512, 512]);
|
||||
expect(style.getOrigin()).to.eql([1, 1]);
|
||||
expect(style.getAnchor()).to.eql([10.5, 10.5]);
|
||||
// no hit-detection image is created, because fill style is set
|
||||
expect(style.getImage()).to.be(style.getHitDetectionImage());
|
||||
expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getHitDetectionImageSize()).to.eql([512, 512]);
|
||||
expect(style.getHitDetectionOrigin()).to.eql([1, 1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getChecksum', function() {
|
||||
|
||||
it('calculates the same hash code for default options', function() {
|
||||
var style1 = new ol.style.Circle();
|
||||
var style2 = new ol.style.Circle();
|
||||
expect(style1.getChecksum()).to.eql(style2.getChecksum());
|
||||
});
|
||||
|
||||
it('calculates not the same hash code (radius)', function() {
|
||||
var style1 = new ol.style.Circle();
|
||||
var style2 = new ol.style.Circle({
|
||||
radius: 5
|
||||
});
|
||||
expect(style1.getChecksum()).to.not.eql(style2.getChecksum());
|
||||
});
|
||||
|
||||
it('calculates the same hash code (radius)', function() {
|
||||
var style1 = new ol.style.Circle({
|
||||
radius: 5
|
||||
});
|
||||
var style2 = new ol.style.Circle({
|
||||
radius: 5
|
||||
});
|
||||
expect(style1.getChecksum()).to.eql(style2.getChecksum());
|
||||
});
|
||||
|
||||
it('calculates not the same hash code (color)', function() {
|
||||
var style1 = new ol.style.Circle({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
})
|
||||
});
|
||||
var style2 = new ol.style.Circle({
|
||||
radius: 5,
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3'
|
||||
})
|
||||
});
|
||||
expect(style1.getChecksum()).to.not.eql(style2.getChecksum());
|
||||
});
|
||||
|
||||
it('calculates the same hash code (everything set)', function() {
|
||||
var style1 = new ol.style.Circle({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3',
|
||||
lineCap: 'round',
|
||||
lineDash: [5, 15, 25],
|
||||
lineJoin: 'miter',
|
||||
miterLimit: 4,
|
||||
width: 2
|
||||
})
|
||||
});
|
||||
var style2 = new ol.style.Circle({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3',
|
||||
lineCap: 'round',
|
||||
lineDash: [5, 15, 25],
|
||||
lineJoin: 'miter',
|
||||
miterLimit: 4,
|
||||
width: 2
|
||||
})
|
||||
});
|
||||
expect(style1.getChecksum()).to.eql(style2.getChecksum());
|
||||
});
|
||||
|
||||
it('calculates not the same hash code (stroke width differs)', function() {
|
||||
var style1 = new ol.style.Circle({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3',
|
||||
lineCap: 'round',
|
||||
lineDash: [5, 15, 25],
|
||||
lineJoin: 'miter',
|
||||
miterLimit: 4,
|
||||
width: 3
|
||||
})
|
||||
});
|
||||
var style2 = new ol.style.Circle({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3',
|
||||
lineCap: 'round',
|
||||
lineDash: [5, 15, 25],
|
||||
lineJoin: 'miter',
|
||||
miterLimit: 4,
|
||||
width: 2
|
||||
})
|
||||
});
|
||||
expect(style1.getChecksum()).to.not.eql(style2.getChecksum());
|
||||
});
|
||||
|
||||
it('invalidates a cached checksum if values change (fill)', function() {
|
||||
var style1 = new ol.style.Circle({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3'
|
||||
})
|
||||
});
|
||||
var style2 = new ol.style.Circle({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3'
|
||||
})
|
||||
});
|
||||
expect(style1.getChecksum()).to.eql(style2.getChecksum());
|
||||
|
||||
style1.getFill().setColor('red');
|
||||
expect(style1.getChecksum()).to.not.eql(style2.getChecksum());
|
||||
});
|
||||
|
||||
it('invalidates a cached checksum if values change (stroke)', function() {
|
||||
var style1 = new ol.style.Circle({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3'
|
||||
})
|
||||
});
|
||||
var style2 = new ol.style.Circle({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3'
|
||||
})
|
||||
});
|
||||
expect(style1.getChecksum()).to.eql(style2.getChecksum());
|
||||
|
||||
style1.getStroke().setWidth(4);
|
||||
expect(style1.getChecksum()).to.not.eql(style2.getChecksum());
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
goog.require('ol.style.AtlasManager');
|
||||
goog.require('ol.style.Circle');
|
||||
goog.require('ol.style.Fill');
|
||||
goog.require('ol.style.Stroke');
|
||||
@@ -1,36 +1,297 @@
|
||||
goog.provide('ol.test.style.RegularShape');
|
||||
|
||||
describe('ol.style.RegularShape', function() {
|
||||
it('can use radius', function() {
|
||||
var style = new ol.style.RegularShape({
|
||||
radius: 5,
|
||||
radius2: 10
|
||||
|
||||
describe('#constructor', function() {
|
||||
|
||||
it('can use radius', function() {
|
||||
var style = new ol.style.RegularShape({
|
||||
radius: 5,
|
||||
radius2: 10
|
||||
});
|
||||
expect(style.getRadius()).to.eql(5);
|
||||
expect(style.getRadius2()).to.eql(10);
|
||||
});
|
||||
|
||||
it('can use radius1 as an alias for radius', function() {
|
||||
var style = new ol.style.RegularShape({
|
||||
radius1: 5,
|
||||
radius2: 10
|
||||
});
|
||||
expect(style.getRadius()).to.eql(5);
|
||||
expect(style.getRadius2()).to.eql(10);
|
||||
});
|
||||
|
||||
it('will use radius for radius2 if radius2 not defined', function() {
|
||||
var style = new ol.style.RegularShape({
|
||||
radius: 5
|
||||
});
|
||||
expect(style.getRadius()).to.eql(5);
|
||||
expect(style.getRadius2()).to.eql(5);
|
||||
});
|
||||
|
||||
it('will use radius1 for radius2 if radius2 not defined', function() {
|
||||
var style = new ol.style.RegularShape({
|
||||
radius1: 5
|
||||
});
|
||||
expect(style.getRadius()).to.eql(5);
|
||||
expect(style.getRadius2()).to.eql(5);
|
||||
});
|
||||
|
||||
it('creates a canvas if no atlas is used (no fill-style)', function() {
|
||||
var style = new ol.style.RegularShape({radius: 10});
|
||||
expect(style.getImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getSize()).to.eql([21, 21]);
|
||||
expect(style.getImageSize()).to.eql([21, 21]);
|
||||
expect(style.getOrigin()).to.eql([0, 0]);
|
||||
expect(style.getAnchor()).to.eql([10.5, 10.5]);
|
||||
// hit-detection image is created, because no fill style is set
|
||||
expect(style.getImage()).to.not.be(style.getHitDetectionImage());
|
||||
expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getHitDetectionImageSize()).to.eql([21, 21]);
|
||||
expect(style.getHitDetectionOrigin()).to.eql([0, 0]);
|
||||
});
|
||||
|
||||
it('creates a canvas if no atlas is used (fill-style)', function() {
|
||||
var style = new ol.style.RegularShape({
|
||||
radius: 10,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#FFFF00'
|
||||
})
|
||||
});
|
||||
expect(style.getImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getSize()).to.eql([21, 21]);
|
||||
expect(style.getImageSize()).to.eql([21, 21]);
|
||||
expect(style.getOrigin()).to.eql([0, 0]);
|
||||
expect(style.getAnchor()).to.eql([10.5, 10.5]);
|
||||
// no hit-detection image is created, because fill style is set
|
||||
expect(style.getImage()).to.be(style.getHitDetectionImage());
|
||||
expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getHitDetectionImageSize()).to.eql([21, 21]);
|
||||
expect(style.getHitDetectionOrigin()).to.eql([0, 0]);
|
||||
});
|
||||
|
||||
it('adds itself to an atlas manager (no fill-style)', function() {
|
||||
var atlasManager = new ol.style.AtlasManager({initialSize: 512});
|
||||
var style = new ol.style.RegularShape(
|
||||
{radius: 10, atlasManager: atlasManager});
|
||||
expect(style.getImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getSize()).to.eql([21, 21]);
|
||||
expect(style.getImageSize()).to.eql([512, 512]);
|
||||
expect(style.getOrigin()).to.eql([1, 1]);
|
||||
expect(style.getAnchor()).to.eql([10.5, 10.5]);
|
||||
// hit-detection image is created, because no fill style is set
|
||||
expect(style.getImage()).to.not.be(style.getHitDetectionImage());
|
||||
expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getHitDetectionImageSize()).to.eql([512, 512]);
|
||||
expect(style.getHitDetectionOrigin()).to.eql([1, 1]);
|
||||
});
|
||||
|
||||
it('adds itself to an atlas manager (fill-style)', function() {
|
||||
var atlasManager = new ol.style.AtlasManager({initialSize: 512});
|
||||
var style = new ol.style.RegularShape({
|
||||
radius: 10,
|
||||
atlasManager: atlasManager,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#FFFF00'
|
||||
})
|
||||
});
|
||||
expect(style.getImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getSize()).to.eql([21, 21]);
|
||||
expect(style.getImageSize()).to.eql([512, 512]);
|
||||
expect(style.getOrigin()).to.eql([1, 1]);
|
||||
expect(style.getAnchor()).to.eql([10.5, 10.5]);
|
||||
// no hit-detection image is created, because fill style is set
|
||||
expect(style.getImage()).to.be(style.getHitDetectionImage());
|
||||
expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement);
|
||||
expect(style.getHitDetectionImageSize()).to.eql([512, 512]);
|
||||
expect(style.getHitDetectionOrigin()).to.eql([1, 1]);
|
||||
});
|
||||
expect(style.getRadius()).to.eql(5);
|
||||
expect(style.getRadius2()).to.eql(10);
|
||||
});
|
||||
it('can use radius1 as an alias for radius', function() {
|
||||
var style = new ol.style.RegularShape({
|
||||
radius1: 5,
|
||||
radius2: 10
|
||||
|
||||
|
||||
describe('#getChecksum', function() {
|
||||
|
||||
it('calculates not the same hash code (radius)', function() {
|
||||
var style1 = new ol.style.RegularShape({
|
||||
radius: 4,
|
||||
radius2: 5
|
||||
});
|
||||
var style2 = new ol.style.RegularShape({
|
||||
radius: 3,
|
||||
radius2: 5
|
||||
});
|
||||
expect(style1.getChecksum()).to.not.eql(style2.getChecksum());
|
||||
});
|
||||
expect(style.getRadius()).to.eql(5);
|
||||
expect(style.getRadius2()).to.eql(10);
|
||||
});
|
||||
it('will use radius for radius2 if radius2 not defined', function() {
|
||||
var style = new ol.style.RegularShape({
|
||||
radius: 5
|
||||
|
||||
it('calculates not the same hash code (radius2)', function() {
|
||||
var style1 = new ol.style.RegularShape({
|
||||
radius: 4,
|
||||
radius2: 5
|
||||
});
|
||||
var style2 = new ol.style.RegularShape({
|
||||
radius: 4,
|
||||
radius2: 6
|
||||
});
|
||||
expect(style1.getChecksum()).to.not.eql(style2.getChecksum());
|
||||
});
|
||||
expect(style.getRadius()).to.eql(5);
|
||||
expect(style.getRadius2()).to.eql(5);
|
||||
});
|
||||
it('will use radius1 for radius2 if radius2 not defined', function() {
|
||||
var style = new ol.style.RegularShape({
|
||||
radius1: 5
|
||||
|
||||
it('calculates the same hash code (radius)', function() {
|
||||
var style1 = new ol.style.RegularShape({
|
||||
radius: 5
|
||||
});
|
||||
var style2 = new ol.style.RegularShape({
|
||||
radius: 5
|
||||
});
|
||||
expect(style1.getChecksum()).to.eql(style2.getChecksum());
|
||||
});
|
||||
expect(style.getRadius()).to.eql(5);
|
||||
expect(style.getRadius2()).to.eql(5);
|
||||
|
||||
it('calculates not the same hash code (color)', function() {
|
||||
var style1 = new ol.style.RegularShape({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
})
|
||||
});
|
||||
var style2 = new ol.style.RegularShape({
|
||||
radius: 5,
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3'
|
||||
})
|
||||
});
|
||||
expect(style1.getChecksum()).to.not.eql(style2.getChecksum());
|
||||
});
|
||||
|
||||
it('calculates the same hash code (everything set)', function() {
|
||||
var style1 = new ol.style.RegularShape({
|
||||
radius: 5,
|
||||
radius2: 3,
|
||||
angle: 1.41,
|
||||
points: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3',
|
||||
lineCap: 'round',
|
||||
lineDash: [5, 15, 25],
|
||||
lineJoin: 'miter',
|
||||
miterLimit: 4,
|
||||
width: 2
|
||||
})
|
||||
});
|
||||
var style2 = new ol.style.RegularShape({
|
||||
radius: 5,
|
||||
radius2: 3,
|
||||
angle: 1.41,
|
||||
points: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3',
|
||||
lineCap: 'round',
|
||||
lineDash: [5, 15, 25],
|
||||
lineJoin: 'miter',
|
||||
miterLimit: 4,
|
||||
width: 2
|
||||
})
|
||||
});
|
||||
expect(style1.getChecksum()).to.eql(style2.getChecksum());
|
||||
});
|
||||
|
||||
it('calculates not the same hash code (stroke width differs)', function() {
|
||||
var style1 = new ol.style.RegularShape({
|
||||
radius: 5,
|
||||
radius2: 3,
|
||||
angle: 1.41,
|
||||
points: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3',
|
||||
lineCap: 'round',
|
||||
lineDash: [5, 15, 25],
|
||||
lineJoin: 'miter',
|
||||
miterLimit: 4,
|
||||
width: 3
|
||||
})
|
||||
});
|
||||
var style2 = new ol.style.RegularShape({
|
||||
radius: 5,
|
||||
radius2: 3,
|
||||
angle: 1.41,
|
||||
points: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3',
|
||||
lineCap: 'round',
|
||||
lineDash: [5, 15, 25],
|
||||
lineJoin: 'miter',
|
||||
miterLimit: 4,
|
||||
width: 2
|
||||
})
|
||||
});
|
||||
expect(style1.getChecksum()).to.not.eql(style2.getChecksum());
|
||||
});
|
||||
|
||||
it('invalidates a cached checksum if values change (fill)', function() {
|
||||
var style1 = new ol.style.RegularShape({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3'
|
||||
})
|
||||
});
|
||||
var style2 = new ol.style.RegularShape({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3'
|
||||
})
|
||||
});
|
||||
expect(style1.getChecksum()).to.eql(style2.getChecksum());
|
||||
|
||||
style1.getFill().setColor('red');
|
||||
expect(style1.getChecksum()).to.not.eql(style2.getChecksum());
|
||||
});
|
||||
|
||||
it('invalidates a cached checksum if values change (stroke)', function() {
|
||||
var style1 = new ol.style.RegularShape({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3'
|
||||
})
|
||||
});
|
||||
var style2 = new ol.style.RegularShape({
|
||||
radius: 5,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#319FD3'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#319FD3'
|
||||
})
|
||||
});
|
||||
expect(style1.getChecksum()).to.eql(style2.getChecksum());
|
||||
|
||||
style1.getStroke().setWidth(4);
|
||||
expect(style1.getChecksum()).to.not.eql(style2.getChecksum());
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
goog.require('ol.style.AtlasManager');
|
||||
goog.require('ol.style.RegularShape');
|
||||
goog.require('ol.style.Fill');
|
||||
goog.require('ol.style.Stroke');
|
||||
|
||||
55
test/spec/ol/webgl/buffer.test.js
Normal file
55
test/spec/ol/webgl/buffer.test.js
Normal file
@@ -0,0 +1,55 @@
|
||||
goog.provide('ol.test.webgl.Buffer');
|
||||
|
||||
|
||||
describe('ol.webgl.Buffer', function() {
|
||||
|
||||
describe('constructor', function() {
|
||||
|
||||
describe('without an argument', function() {
|
||||
|
||||
var b;
|
||||
beforeEach(function() {
|
||||
b = new ol.webgl.Buffer();
|
||||
});
|
||||
|
||||
it('constructs an empty instance', function() {
|
||||
expect(b.getArray()).to.be.empty();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with a single array argument', function() {
|
||||
|
||||
var b;
|
||||
beforeEach(function() {
|
||||
b = new ol.webgl.Buffer([0, 1, 2, 3]);
|
||||
});
|
||||
|
||||
it('constructs a populated instance', function() {
|
||||
expect(b.getArray()).to.eql([0, 1, 2, 3]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with an empty instance', function() {
|
||||
|
||||
var b;
|
||||
beforeEach(function() {
|
||||
b = new ol.webgl.Buffer();
|
||||
});
|
||||
|
||||
describe('getArray', function() {
|
||||
|
||||
it('returns an empty array', function() {
|
||||
expect(b.getArray()).to.be.empty();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
goog.require('ol.webgl.Buffer');
|
||||
Reference in New Issue
Block a user