diff --git a/examples/side-by-side.html b/examples/side-by-side.html
index 2983cd69a8..6aa2307ef5 100644
--- a/examples/side-by-side.html
+++ b/examples/side-by-side.html
@@ -60,6 +60,10 @@
Visibility: |
v/V keys |
+
+ | Animations: |
+ j/l/m/x/L/M/X keys |
+
| Reset |
0 key |
diff --git a/examples/side-by-side.js b/examples/side-by-side.js
index 8fffdf17a8..d92105bec0 100644
--- a/examples/side-by-side.js
+++ b/examples/side-by-side.js
@@ -5,6 +5,7 @@ goog.require('ol.Coordinate');
goog.require('ol.Map');
goog.require('ol.RendererHint');
goog.require('ol.View2D');
+goog.require('ol.animation');
goog.require('ol.control.MousePosition');
goog.require('ol.interaction.Keyboard');
goog.require('ol.layer.TileLayer');
@@ -17,6 +18,11 @@ if (goog.DEBUG) {
}
+var LONDON = ol.Projection.transformWithCodes(
+ new ol.Coordinate(-0.12755, 51.507222), 'EPSG:4326', 'EPSG:3857');
+var MOSCOW = ol.Projection.transformWithCodes(
+ new ol.Coordinate(37.6178, 55.7517), 'EPSG:4326', 'EPSG:3857');
+
var layer = new ol.layer.TileLayer({
source: new ol.source.MapQuestOpenAerial()
});
@@ -84,6 +90,47 @@ keyboardInteraction.addCallback('h', function() {
keyboardInteraction.addCallback('H', function() {
layer.setHue(layer.getHue() + (Math.PI / 5));
});
+keyboardInteraction.addCallback('j', function() {
+ var bounce = ol.animation.createBounce(2 * view.getResolution());
+ domMap.addPreRenderFunction(bounce);
+ webglMap.addPreRenderFunction(bounce);
+});
+keyboardInteraction.addCallback('l', function() {
+ var panFrom = ol.animation.createPanFrom(view.getCenter());
+ domMap.addPreRenderFunction(panFrom);
+ webglMap.addPreRenderFunction(panFrom);
+ view.setCenter(LONDON);
+});
+keyboardInteraction.addCallback('L', function() {
+ var start = Date.now();
+ var duration = 5000;
+ var bounce = ol.animation.createBounce(
+ 2 * view.getResolution(), duration, start);
+ var panFrom = ol.animation.createPanFrom(view.getCenter(), duration, start);
+ var spin = ol.animation.createSpin(duration, 2, start);
+ var preRenderFunctions = [bounce, panFrom, spin];
+ domMap.addPreRenderFunctions(preRenderFunctions);
+ webglMap.addPreRenderFunctions(preRenderFunctions);
+ view.setCenter(LONDON);
+});
+keyboardInteraction.addCallback('m', function() {
+ var panFrom = ol.animation.createPanFrom(view.getCenter(), 1000);
+ domMap.addPreRenderFunction(panFrom);
+ webglMap.addPreRenderFunction(panFrom);
+ view.setCenter(MOSCOW);
+});
+keyboardInteraction.addCallback('M', function() {
+ var start = Date.now();
+ var duration = 5000;
+ var bounce = ol.animation.createBounce(
+ 2 * view.getResolution(), duration, start);
+ var panFrom = ol.animation.createPanFrom(view.getCenter(), duration, start);
+ var spin = ol.animation.createSpin(duration, -2, start);
+ var preRenderFunctions = [bounce, panFrom, spin];
+ domMap.addPreRenderFunctions(preRenderFunctions);
+ webglMap.addPreRenderFunctions(preRenderFunctions);
+ view.setCenter(MOSCOW);
+});
keyboardInteraction.addCallback('o', function() {
layer.setOpacity(layer.getOpacity() - 0.1);
});
@@ -103,4 +150,14 @@ keyboardInteraction.addCallback('S', function() {
keyboardInteraction.addCallback('vV', function() {
layer.setVisible(!layer.getVisible());
});
+keyboardInteraction.addCallback('x', function() {
+ var spin = ol.animation.createSpin(2000, 2);
+ domMap.addPreRenderFunction(spin);
+ webglMap.addPreRenderFunction(spin);
+});
+keyboardInteraction.addCallback('X', function() {
+ var spin = ol.animation.createSpin(2000, -2);
+ domMap.addPreRenderFunction(spin);
+ webglMap.addPreRenderFunction(spin);
+});
domMap.getInteractions().push(keyboardInteraction);
diff --git a/src/ol/animation.js b/src/ol/animation.js
new file mode 100644
index 0000000000..6b80f0accc
--- /dev/null
+++ b/src/ol/animation.js
@@ -0,0 +1,103 @@
+// FIXME works for View2D only
+
+goog.provide('ol.animation');
+
+goog.require('goog.fx.easing');
+goog.require('ol.PreRenderFunction');
+goog.require('ol.View2D');
+goog.require('ol.easing');
+
+
+/**
+ * @param {number} resolution Resolution.
+ * @param {number=} opt_duration Duration.
+ * @param {number=} opt_start Start.
+ * @param {function(number): number=} opt_easingFunction Easing function.
+ * @return {ol.PreRenderFunction} Pre-render function.
+ */
+ol.animation.createBounce =
+ function(resolution, opt_duration, opt_start, opt_easingFunction) {
+ var start = goog.isDef(opt_start) ? opt_start : Date.now();
+ var duration = goog.isDef(opt_duration) ? opt_duration : 1000;
+ var easingFunction = goog.isDef(opt_easingFunction) ?
+ opt_easingFunction : ol.easing.upAndDown;
+ return function(map, frameState) {
+ if (frameState.time < start) {
+ frameState.animate = true;
+ return true;
+ } else if (frameState.time < start + duration) {
+ var delta = easingFunction((frameState.time - start) / duration);
+ var deltaResolution = resolution - frameState.view2DState.resolution;
+ frameState.animate = true;
+ frameState.view2DState.resolution += delta * deltaResolution;
+ return true;
+ } else {
+ return false;
+ }
+ };
+};
+
+
+/**
+ * @param {ol.Coordinate} source Source.
+ * @param {number=} opt_duration Duration.
+ * @param {number=} opt_start Start.
+ * @param {function(number): number=} opt_easingFunction Easing function.
+ * @return {ol.PreRenderFunction} Pre-render function.
+ */
+ol.animation.createPanFrom =
+ function(source, opt_duration, opt_start, opt_easingFunction) {
+ var start = goog.isDef(opt_start) ? opt_start : Date.now();
+ var sourceX = source.x;
+ var sourceY = source.y;
+ var duration = goog.isDef(opt_duration) ? opt_duration : 1000;
+ var easingFunction = goog.isDef(opt_easingFunction) ?
+ opt_easingFunction : goog.fx.easing.inAndOut;
+ return function(map, frameState) {
+ if (frameState.time < start) {
+ frameState.animate = true;
+ return true;
+ } else if (frameState.time < start + duration) {
+ var delta = 1 - easingFunction((frameState.time - start) / duration);
+ var deltaX = sourceX - frameState.view2DState.center.x;
+ var deltaY = sourceY - frameState.view2DState.center.y;
+ frameState.animate = true;
+ frameState.view2DState.center.x += delta * deltaX;
+ frameState.view2DState.center.y += delta * deltaY;
+ return true;
+ } else {
+ return false;
+ }
+ };
+};
+
+
+/**
+ * @param {number=} opt_duration Duration.
+ * @param {number=} opt_turns Turns.
+ * @param {number=} opt_start Start.
+ * @param {function(number): number=} opt_easingFunction Easing function.
+ * @return {ol.PreRenderFunction} Pre-render function.
+ */
+ol.animation.createSpin =
+ function(opt_duration, opt_turns, opt_start, opt_easingFunction) {
+ var start = goog.isDef(opt_start) ? opt_start : Date.now();
+ var duration = goog.isDef(opt_duration) ? opt_duration : 1000;
+ var turns = goog.isDef(opt_turns) ? opt_turns : 1;
+ var deltaTheta = 2 * turns * Math.PI;
+ var easingFunction = goog.isDef(opt_easingFunction) ?
+ opt_easingFunction : goog.fx.easing.inAndOut;
+ return function(map, frameState) {
+ if (frameState.time < start) {
+ frameState.animate = true;
+ return true;
+ } else if (frameState.time < start + duration) {
+ var delta = easingFunction((frameState.time - start) / duration);
+ frameState.animate = true;
+ frameState.view2DState.rotation += delta * deltaTheta;
+ return true;
+ } else {
+ return false;
+ }
+ };
+};
diff --git a/src/ol/easing.js b/src/ol/easing.js
new file mode 100644
index 0000000000..74bd1c5210
--- /dev/null
+++ b/src/ol/easing.js
@@ -0,0 +1,14 @@
+goog.provide('ol.easing');
+
+
+/**
+ * @param {number} t Input between 0 and 1.
+ * @return {number} Output between 0 and 1.
+ */
+ol.easing.upAndDown = function(t) {
+ if (t < 0.5) {
+ return goog.fx.easing.inAndOut(2 * t);
+ } else {
+ return 1 - goog.fx.easing.inAndOut(2 * (t - 0.5));
+ }
+};
diff --git a/src/ol/framestate.js b/src/ol/framestate.js
new file mode 100644
index 0000000000..032dac1ed7
--- /dev/null
+++ b/src/ol/framestate.js
@@ -0,0 +1,38 @@
+// FIXME add view3DState
+
+goog.provide('ol.FrameState');
+goog.provide('ol.PostRenderFunction');
+goog.provide('ol.PreRenderFunction');
+
+goog.require('ol.Color');
+goog.require('ol.Coordinate');
+goog.require('ol.Extent');
+goog.require('ol.Size');
+goog.require('ol.View2DState');
+goog.require('ol.layer.LayerState');
+
+
+/**
+ * @typedef {{animate: boolean,
+ * backgroundColor: ol.Color,
+ * extent: (null|ol.Extent),
+ * layersArray: Array.,
+ * layerStates: Object.,
+ * postRenderFunctions: Array.,
+ * size: ol.Size,
+ * time: number,
+ * view2DState: ol.View2DState}}
+ */
+ol.FrameState;
+
+
+/**
+ * @typedef {function(ol.Map, ?ol.FrameState): boolean}
+ */
+ol.PostRenderFunction;
+
+
+/**
+ * @typedef {function(ol.Map, ?ol.FrameState): boolean}
+ */
+ol.PreRenderFunction;
diff --git a/src/ol/iview2d.js b/src/ol/iview2d.js
index d8840a8cba..e8dcb0c4db 100644
--- a/src/ol/iview2d.js
+++ b/src/ol/iview2d.js
@@ -1,4 +1,18 @@
goog.provide('ol.IView2D');
+goog.provide('ol.View2DState');
+
+goog.require('ol.Coordinate');
+goog.require('ol.Extent');
+goog.require('ol.Projection');
+
+
+/**
+ * @typedef {{center: ol.Coordinate,
+ * projection: ol.Projection,
+ * resolution: number,
+ * rotation: number}}
+ */
+ol.View2DState;
@@ -36,3 +50,10 @@ ol.IView2D.prototype.getResolution = function() {
*/
ol.IView2D.prototype.getRotation = function() {
};
+
+
+/**
+ * @return {ol.View2DState} View2D state.
+ */
+ol.IView2D.prototype.getView2DState = function() {
+};
diff --git a/src/ol/layer/layer.js b/src/ol/layer/layer.js
index 89aead6fa9..cb06bed1ba 100644
--- a/src/ol/layer/layer.js
+++ b/src/ol/layer/layer.js
@@ -1,5 +1,6 @@
goog.provide('ol.layer.Layer');
goog.provide('ol.layer.LayerProperty');
+goog.provide('ol.layer.LayerState');
goog.require('goog.events');
goog.require('goog.events.EventType');
@@ -21,6 +22,18 @@ ol.layer.LayerProperty = {
};
+/**
+ * @typedef {{brightness: number,
+ * contrast: number,
+ * hue: number,
+ * opacity: number,
+ * ready: boolean,
+ * saturation: number,
+ * visible: boolean}}
+ */
+ol.layer.LayerState;
+
+
/**
* @constructor
@@ -103,6 +116,29 @@ goog.exportProperty(
ol.layer.Layer.prototype.getHue);
+/**
+ * @return {ol.layer.LayerState} Layer state.
+ */
+ol.layer.Layer.prototype.getLayerState = function() {
+ var brightness = this.getBrightness();
+ var contrast = this.getContrast();
+ var hue = this.getHue();
+ var opacity = this.getOpacity();
+ var ready = this.isReady();
+ var saturation = this.getSaturation();
+ var visible = this.getVisible();
+ return {
+ brightness: goog.isDef(brightness) ? brightness : 0,
+ contrast: goog.isDef(contrast) ? contrast : 1,
+ hue: goog.isDef(hue) ? hue : 0,
+ opacity: goog.isDef(opacity) ? opacity : 1,
+ ready: ready,
+ saturation: goog.isDef(saturation) ? saturation : 1,
+ visible: goog.isDef(visible) ? visible : true
+ };
+};
+
+
/**
* @return {number} Opacity.
*/
diff --git a/src/ol/map.js b/src/ol/map.js
index 8cf12961f6..01a180637c 100644
--- a/src/ol/map.js
+++ b/src/ol/map.js
@@ -29,6 +29,7 @@ goog.require('ol.Collection');
goog.require('ol.Color');
goog.require('ol.Coordinate');
goog.require('ol.Extent');
+goog.require('ol.FrameState');
goog.require('ol.MapBrowserEvent');
goog.require('ol.Object');
goog.require('ol.Pixel');
@@ -38,6 +39,7 @@ goog.require('ol.Size');
goog.require('ol.TransformFunction');
goog.require('ol.View');
goog.require('ol.View2D');
+goog.require('ol.View2DState');
goog.require('ol.control.Attribution');
goog.require('ol.control.Zoom');
goog.require('ol.interaction.DblClickZoom');
@@ -87,14 +89,6 @@ ol.DEFAULT_RENDERER_HINTS = [
];
-/**
- * @enum {string}
- */
-ol.MapEventType = {
- POSTRENDER: 'postrender'
-};
-
-
/**
* @enum {string}
*/
@@ -134,6 +128,12 @@ ol.Map = function(mapOptions) {
new goog.async.AnimationDelay(this.renderFrame_, undefined, this);
this.registerDisposable(this.animationDelay_);
+ /**
+ * @private
+ * @type {?ol.FrameState}
+ */
+ this.frameState_ = null;
+
/**
* @private
* @type {number}
@@ -231,6 +231,24 @@ ol.Map = function(mapOptions) {
goog.events.listen(this.viewportSizeMonitor_, goog.events.EventType.RESIZE,
this.handleBrowserWindowResize, false, this);
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.preRenderFunctions_ = [];
+
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.postRenderFunctions_ = [];
+
+ /**
+ * @private
+ * @type {function(this: ol.Map)}
+ */
+ this.handlePostRender_ = goog.bind(this.handlePostRender, this);
+
this.setValues(mapOptionsInternal.values);
this.handleBrowserWindowResize();
@@ -247,6 +265,26 @@ ol.Map = function(mapOptions) {
goog.inherits(ol.Map, ol.Object);
+/**
+ * @param {ol.PreRenderFunction} preRenderFunction Pre-render function.
+ */
+ol.Map.prototype.addPreRenderFunction = function(preRenderFunction) {
+ this.requestRenderFrame();
+ this.preRenderFunctions_.push(preRenderFunction);
+};
+
+
+/**
+ * @param {Array.} preRenderFunctions
+ * Pre-render functions.
+ */
+ol.Map.prototype.addPreRenderFunctions = function(preRenderFunctions) {
+ this.requestRenderFrame();
+ Array.prototype.push.apply(
+ this.preRenderFunctions_, preRenderFunctions);
+};
+
+
/**
* @return {boolean} Can rotate.
*/
@@ -432,6 +470,20 @@ ol.Map.prototype.handleMapBrowserEvent = function(mapBrowserEvent) {
};
+/**
+ * @protected
+ */
+ol.Map.prototype.handlePostRender = function() {
+ goog.array.forEach(
+ this.postRenderFunctions_,
+ function(postRenderFunction) {
+ postRenderFunction(this, this.frameState_);
+ },
+ this);
+ this.postRenderFunctions_.length = 0;
+};
+
+
/**
* @protected
*/
@@ -484,18 +536,90 @@ ol.Map.prototype.requestRenderFrame = function() {
* @private
*/
ol.Map.prototype.renderFrame_ = function(time) {
+
+ var i;
+
if (this.freezeRenderingCount_ != 0) {
return;
}
+
if (goog.DEBUG) {
this.logger.info('renderFrame_');
}
- this.renderer_.renderFrame(time);
- this.dirty_ = false;
- if (goog.DEBUG) {
- this.logger.info('postrender');
+
+ var size = this.getSize();
+ var layers = this.getLayers();
+ var layersArray = goog.isDef(layers) ?
+ /** @type {Array.} */ (layers.getArray()) : undefined;
+ var view = this.getView();
+ var view2D = goog.isDef(view) ? this.getView().getView2D() : undefined;
+ /** @type {?ol.FrameState} */
+ var frameState = null;
+ if (goog.isDef(layersArray) && goog.isDef(size) && goog.isDef(view2D) &&
+ view2D.isDef()) {
+ var backgroundColor = this.getBackgroundColor();
+ var layerStates = {};
+ goog.array.forEach(layersArray, function(layer) {
+ layerStates[goog.getUid(layer)] = layer.getLayerState();
+ });
+ var view2DState = view2D.getView2DState();
+ frameState = {
+ animate: false,
+ backgroundColor: goog.isDef(backgroundColor) ?
+ backgroundColor : new ol.Color(1, 1, 1, 1),
+ extent: null,
+ layersArray: layersArray,
+ layerStates: layerStates,
+ postRenderFunctions: [],
+ size: size,
+ view2DState: view2DState,
+ time: time
+ };
}
- this.dispatchEvent(ol.MapEventType.POSTRENDER);
+
+ this.preRenderFunctions_ = goog.array.filter(
+ this.preRenderFunctions_,
+ function(preRenderFunction) {
+ return preRenderFunction(this, frameState);
+ },
+ this);
+
+ if (!goog.isNull(frameState)) {
+ // FIXME works for View2D only
+ var center = view2DState.center;
+ var resolution = view2DState.resolution;
+ var rotation = view2DState.rotation;
+ var x = resolution * size.width / 2;
+ var y = resolution * size.height / 2;
+ var corners = [
+ new ol.Coordinate(-x, -y),
+ new ol.Coordinate(-x, y),
+ new ol.Coordinate(x, -y),
+ new ol.Coordinate(x, y)
+ ];
+ var corner;
+ for (i = 0; i < 4; ++i) {
+ corner = corners[i];
+ corner.rotate(rotation);
+ corner.add(center);
+ }
+ frameState.extent = ol.Extent.boundingExtent.apply(null, corners);
+ }
+
+ this.renderer_.renderFrame(frameState);
+
+ if (!goog.isNull(frameState)) {
+ if (frameState.animate) {
+ this.requestRenderFrame();
+ }
+ Array.prototype.push.apply(
+ this.postRenderFunctions_, frameState.postRenderFunctions);
+ }
+ this.frameState_ = frameState;
+ this.dirty_ = false;
+
+ goog.global.setTimeout(this.handlePostRender_, 0);
+
};
diff --git a/src/ol/renderer/dom/domlayerrenderer.js b/src/ol/renderer/dom/domlayerrenderer.js
index 58bbb51b2a..d8ff224761 100644
--- a/src/ol/renderer/dom/domlayerrenderer.js
+++ b/src/ol/renderer/dom/domlayerrenderer.js
@@ -1,7 +1,9 @@
goog.provide('ol.renderer.dom.Layer');
goog.require('ol.Coordinate');
+goog.require('ol.FrameState');
goog.require('ol.layer.Layer');
+goog.require('ol.layer.LayerState');
goog.require('ol.renderer.Layer');
@@ -60,6 +62,7 @@ ol.renderer.dom.Layer.prototype.handleLayerVisibleChange = function() {
/**
- * @param {number} time Time.
+ * @param {ol.FrameState} frameState Frame state.
+ * @param {ol.layer.LayerState} layerState Layer state.
*/
ol.renderer.dom.Layer.prototype.renderFrame = goog.abstractMethod;
diff --git a/src/ol/renderer/dom/dommaprenderer.js b/src/ol/renderer/dom/dommaprenderer.js
index 8b6044ec32..1d36d36eaa 100644
--- a/src/ol/renderer/dom/dommaprenderer.js
+++ b/src/ol/renderer/dom/dommaprenderer.js
@@ -9,6 +9,7 @@ goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.functions');
goog.require('ol.Coordinate');
+goog.require('ol.FrameState');
goog.require('ol.layer.TileLayer');
goog.require('ol.renderer.Map');
goog.require('ol.renderer.dom.TileLayer');
@@ -93,26 +94,20 @@ ol.renderer.dom.Map.prototype.handleViewChanged = function() {
/**
* @inheritDoc
*/
-ol.renderer.dom.Map.prototype.renderFrame = function(time) {
+ol.renderer.dom.Map.prototype.renderFrame = function(frameState) {
- var map = this.getMap();
- if (!map.isDef()) {
+ if (goog.isNull(frameState)) {
+ // FIXME remove everything
return;
}
- var requestRenderFrame = false;
- var layers = map.getLayers();
- if (goog.isDef(layers)) {
- layers.forEach(function(layer) {
- var layerRenderer = this.getLayerRenderer(layer);
- if (layerRenderer.renderFrame(time)) {
- requestRenderFrame = true;
- }
- }, this);
- }
-
- if (requestRenderFrame) {
- map.requestRenderFrame();
- }
+ goog.array.forEach(frameState.layersArray, function(layer) {
+ var layerState = frameState.layerStates[goog.getUid(layer)];
+ if (!layerState.ready) {
+ return;
+ }
+ var layerRenderer = this.getLayerRenderer(layer);
+ layerRenderer.renderFrame(frameState, layerState);
+ }, this);
};
diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js
index 0686f99db9..88969c284f 100644
--- a/src/ol/renderer/dom/domtilelayerrenderer.js
+++ b/src/ol/renderer/dom/domtilelayerrenderer.js
@@ -67,47 +67,36 @@ ol.renderer.dom.TileLayer.prototype.getTileLayer = function() {
/**
* @inheritDoc
*/
-ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) {
+ol.renderer.dom.TileLayer.prototype.renderFrame =
+ function(frameState, layerState) {
- var map = this.getMap();
- goog.asserts.assert(map.isDef());
-
- var view = map.getView().getView2D();
- var mapCenter = /** @type {!ol.Coordinate} */ (view.getCenter());
- var mapResolution = /** @type {number} */ (view.getResolution());
- var mapSize = /** @type {!ol.Size} */ (map.getSize());
- var mapRotatedExtent = /** @type {!ol.Extent} */
- (view.getRotatedExtent(mapSize));
- var mapRotation = view.getRotation();
- var mapScale = 1 / mapResolution;
+ var view2DState = frameState.view2DState;
var tileLayer = this.getTileLayer();
- var visible = tileLayer.getVisible();
- if (!visible) {
+ if (!layerState.visible) {
if (this.renderedVisible_) {
goog.style.showElement(this.target, false);
this.renderedVisible_ = false;
}
- return;
+ return false;
}
- var opacity = tileLayer.getOpacity();
- if (opacity != this.renderedOpacity_) {
- goog.style.setOpacity(this.target, opacity);
- this.renderedOpacity_ = opacity;
+ if (layerState.opacity != this.renderedOpacity_) {
+ goog.style.setOpacity(this.target, layerState.opacity);
+ this.renderedOpacity_ = layerState.opacity;
}
var tileSource = tileLayer.getTileSource();
var tileGrid = tileSource.getTileGrid();
- var z = tileGrid.getZForResolution(mapResolution);
+ var z = tileGrid.getZForResolution(view2DState.resolution);
/** @type {Object.>} */
var tilesToDrawByZ = {};
var tileRange = tileGrid.getTileRangeForExtentAndResolution(
- mapRotatedExtent, mapResolution);
+ frameState.extent, view2DState.resolution);
var allTilesLoaded = true;
@@ -173,7 +162,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) {
tileLayerZ = this.tileLayerZs_[tileLayerZKey];
} else {
tileCoordOrigin =
- tileGrid.getTileCoordForCoordAndZ(mapCenter, tileLayerZKey);
+ tileGrid.getTileCoordForCoordAndZ(view2DState.center, tileLayerZKey);
tileLayerZ = new ol.renderer.dom.TileLayerZ_(tileGrid, tileCoordOrigin);
newTileLayerZKeys[tileLayerZKey] = true;
this.tileLayerZs_[tileLayerZKey] = tileLayerZ;
@@ -204,14 +193,15 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) {
origin = tileLayerZ.getOrigin();
goog.vec.Mat4.makeIdentity(transform);
goog.vec.Mat4.translate(
- transform, mapSize.width / 2, mapSize.height / 2, 0);
- if (goog.isDef(mapRotation)) {
- goog.vec.Mat4.rotateZ(transform, mapRotation);
- }
- goog.vec.Mat4.scale(
- transform, resolution / mapResolution, resolution / mapResolution, 1);
- goog.vec.Mat4.translate(transform, (origin.x - mapCenter.x) / resolution,
- (mapCenter.y - origin.y) / resolution, 0);
+ transform, frameState.size.width / 2, frameState.size.height / 2, 0);
+ goog.vec.Mat4.rotateZ(transform, view2DState.rotation);
+ goog.vec.Mat4.scale(transform, resolution / view2DState.resolution,
+ resolution / view2DState.resolution, 1);
+ goog.vec.Mat4.translate(
+ transform,
+ (origin.x - view2DState.center.x) / resolution,
+ (view2DState.center.y - origin.y) / resolution,
+ 0);
tileLayerZ.setTransform(transform);
if (tileLayerZKey in newTileLayerZKeys) {
for (j = tileLayerZKey - 1; j >= 0; --j) {
@@ -225,16 +215,18 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) {
goog.dom.insertChildAt(this.target, tileLayerZ.target, 0);
}
} else {
- tileLayerZ.removeTilesOutsideExtent(mapRotatedExtent);
+ tileLayerZ.removeTilesOutsideExtent(frameState.extent);
}
}
- if (visible && !this.renderedVisible_) {
+ if (layerState.visible && !this.renderedVisible_) {
goog.style.showElement(this.target, true);
this.renderedVisible_ = true;
}
- return !allTilesLoaded;
+ if (!allTilesLoaded) {
+ frameState.animate = true;
+ }
};
diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js
index 3e67e48cf8..38b4af0b0a 100644
--- a/src/ol/renderer/maprenderer.js
+++ b/src/ol/renderer/maprenderer.js
@@ -7,6 +7,7 @@ goog.require('goog.functions');
goog.require('goog.fx.anim');
goog.require('goog.fx.anim.Animated');
goog.require('goog.vec.Mat4');
+goog.require('ol.FrameState');
goog.require('ol.View2D');
goog.require('ol.View2DProperty');
@@ -294,9 +295,9 @@ ol.renderer.Map.prototype.removeLayerRenderer = function(layer) {
/**
* Render.
- * @param {number} time Time.
+ * @param {?ol.FrameState} frameState Frame state.
*/
-ol.renderer.Map.prototype.renderFrame = goog.functions.FALSE;
+ol.renderer.Map.prototype.renderFrame = goog.nullFunction;
/**
diff --git a/src/ol/renderer/webgl/webgllayerrenderer.js b/src/ol/renderer/webgl/webgllayerrenderer.js
index 5a19a31306..7fce8f5256 100644
--- a/src/ol/renderer/webgl/webgllayerrenderer.js
+++ b/src/ol/renderer/webgl/webgllayerrenderer.js
@@ -1,7 +1,11 @@
+// FIXME move colorMatrix_ elsewhere?
+
goog.provide('ol.renderer.webgl.Layer');
goog.require('goog.vec.Mat4');
+goog.require('ol.FrameState');
goog.require('ol.layer.Layer');
+goog.require('ol.layer.LayerState');
goog.require('ol.renderer.Layer');
goog.require('ol.vec.Mat4');
@@ -179,8 +183,8 @@ ol.renderer.webgl.Layer.prototype.handleWebGLContextLost = goog.nullFunction;
/**
* Render.
- * @param {number} time Time.
- * @return {boolean} Request render frame.
+ * @param {ol.FrameState} frameState Frame state.
+ * @param {ol.layer.LayerState} layerState Layer state.
*/
ol.renderer.webgl.Layer.prototype.renderFrame = goog.abstractMethod;
diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js
index 4bfea15f74..83a75771d3 100644
--- a/src/ol/renderer/webgl/webglmaprenderer.js
+++ b/src/ol/renderer/webgl/webglmaprenderer.js
@@ -143,12 +143,6 @@ ol.renderer.webgl.Map = function(container, map) {
goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.RESTORED,
this.handleWebGLContextResourced, false, this);
- /**
- * @private
- * @type {ol.Color}
- */
- this.clearColor_ = new ol.Color(1, 1, 1, 1);
-
/**
* @private
* @type {{aPosition: number,
@@ -303,26 +297,6 @@ ol.renderer.webgl.Map.prototype.disposeInternal = function() {
};
-/**
- * @param {function(this: T, ol.layer.Layer, ol.renderer.webgl.Layer, number)} f
- * Function.
- * @param {T=} opt_obj Object.
- * @template T
- */
-ol.renderer.webgl.Map.prototype.forEachReadyVisibleLayer =
- function(f, opt_obj) {
- var layers = this.map.getLayers();
- if (goog.isDef(layers)) {
- layers.forEach(function(layer, index) {
- if (layer.isReady() && layer.getVisible()) {
- var layerRenderer = this.getLayerRenderer(layer);
- f.call(opt_obj, layer, layerRenderer, index);
- }
- }, this);
- }
-};
-
-
/**
* @return {WebGLRenderingContext} GL.
*/
@@ -394,12 +368,6 @@ ol.renderer.webgl.Map.prototype.getShader = function(shaderObject) {
* @inheritDoc
*/
ol.renderer.webgl.Map.prototype.handleBackgroundColorChanged = function() {
- var backgroundColor = this.getMap().getBackgroundColor();
- this.clearColor_ = new ol.Color(
- backgroundColor.r / 255,
- backgroundColor.g / 255,
- backgroundColor.b / 255,
- backgroundColor.a / 255);
this.getMap().render();
};
@@ -522,34 +490,37 @@ ol.renderer.webgl.Map.prototype.removeLayerRenderer = function(layer) {
/**
* @inheritDoc
*/
-ol.renderer.webgl.Map.prototype.renderFrame = function(time) {
+ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
- var map = this.getMap();
- if (!map.isDef()) {
- return;
+ var gl = this.getGL();
+
+ if (goog.isNull(frameState)) {
+ gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, null);
+ gl.clearColor(0, 0, 0, 0);
+ gl.clear(goog.webgl.COLOR_BUFFER_BIT);
+ return false;
}
- var requestRenderFrame = false;
-
- this.forEachReadyVisibleLayer(function(layer, layerRenderer) {
- if (layerRenderer.renderFrame(time)) {
- requestRenderFrame = true;
+ goog.array.forEach(frameState.layersArray, function(layer) {
+ var layerState = frameState.layerStates[goog.getUid(layer)];
+ if (!layerState.visible || !layerState.ready) {
+ return;
}
- });
+ var layerRenderer = this.getLayerRenderer(layer);
+ layerRenderer.renderFrame(frameState, layerState);
+ }, this);
- var size = /** @type {ol.Size} */ (this.getMap().getSize());
+ var size = frameState.size;
if (!this.canvasSize_.equals(size)) {
this.canvas_.width = size.width;
this.canvas_.height = size.height;
this.canvasSize_ = size;
}
- var gl = this.getGL();
-
gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, null);
- gl.clearColor(this.clearColor_.r, this.clearColor_.g, this.clearColor_.b,
- this.clearColor_.a);
+ var clearColor = frameState.backgroundColor;
+ gl.clearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
gl.clear(goog.webgl.COLOR_BUFFER_BIT);
gl.enable(goog.webgl.BLEND);
gl.viewport(0, 0, size.width, size.height);
@@ -589,7 +560,12 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(time) {
this.locations_.aTexCoord, 2, goog.webgl.FLOAT, false, 16, 8);
gl.uniform1i(this.locations_.uTexture, 0);
- this.forEachReadyVisibleLayer(function(layer, layerRenderer) {
+ goog.array.forEach(frameState.layersArray, function(layer) {
+ var layerState = frameState.layerStates[goog.getUid(layer)];
+ if (!layerState.visible || !layerState.ready) {
+ return;
+ }
+ var layerRenderer = this.getLayerRenderer(layer);
gl.uniformMatrix4fv(
this.locations_.uMatrix, false, layerRenderer.getMatrix());
gl.uniformMatrix4fv(
@@ -599,10 +575,6 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(time) {
gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4);
}, this);
- if (requestRenderFrame) {
- this.getMap().requestRenderFrame();
- }
-
};
diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js
index 2152b6a824..fa4160dfe1 100644
--- a/src/ol/renderer/webgl/webgltilelayerrenderer.js
+++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js
@@ -17,6 +17,7 @@ goog.require('goog.vec.Mat4');
goog.require('goog.vec.Vec4');
goog.require('goog.webgl');
goog.require('ol.Coordinate');
+goog.require('ol.FrameState');
goog.require('ol.Size');
goog.require('ol.TileState');
goog.require('ol.layer.TileLayer');
@@ -166,11 +167,12 @@ goog.inherits(ol.renderer.webgl.TileLayer, ol.renderer.webgl.Layer);
/**
+ * @param {ol.FrameState} frameState Frame state.
* @param {number} framebufferDimension Framebuffer dimension.
* @private
*/
ol.renderer.webgl.TileLayer.prototype.bindFramebuffer_ =
- function(framebufferDimension) {
+ function(frameState, framebufferDimension) {
var mapRenderer = this.getMapRenderer();
var gl = mapRenderer.getGL();
@@ -192,13 +194,8 @@ ol.renderer.webgl.TileLayer.prototype.bindFramebuffer_ =
}
} else {
var map = this.getMap();
- goog.events.listenOnce(
- map,
- ol.MapEventType.POSTRENDER,
+ frameState.postRenderFunctions.push(
goog.partial(function(gl, framebuffer, texture) {
- if (goog.DEBUG) {
- this.logger.info('freeing WebGL resources on postrender');
- }
if (!gl.isContextLost()) {
gl.deleteFramebuffer(framebuffer);
gl.deleteTexture(texture);
@@ -287,30 +284,21 @@ ol.renderer.webgl.TileLayer.prototype.handleWebGLContextLost = function() {
/**
* @inheritDoc
*/
-ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
-
- var requestRenderFrame = false;
+ol.renderer.webgl.TileLayer.prototype.renderFrame =
+ function(frameState, layerState) {
var mapRenderer = this.getMapRenderer();
- var map = this.getMap();
var gl = mapRenderer.getGL();
- var view = map.getView().getView2D();
- goog.asserts.assert(map.isDef());
- var mapSize = map.getSize();
- var mapCenter = view.getCenter();
- var mapExtent = view.getExtent(mapSize);
- var mapResolution = /** @type {number} */ (view.getResolution());
- var mapRotatedExtent = view.getRotatedExtent(mapSize);
- var mapRotation = view.getRotation();
+ var view2DState = frameState.view2DState;
var tileLayer = this.getLayer();
var tileSource = tileLayer.getTileSource();
var tileGrid = tileSource.getTileGrid();
- var z = tileGrid.getZForResolution(mapResolution);
+ var z = tileGrid.getZForResolution(view2DState.resolution);
var tileResolution = tileGrid.getResolution(z);
var tileRange = tileGrid.getTileRangeForExtentAndResolution(
- mapRotatedExtent, tileResolution);
+ frameState.extent, tileResolution);
var framebufferExtent;
@@ -339,7 +327,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
minX + framebufferExtentSize.width,
minY + framebufferExtentSize.height);
- this.bindFramebuffer_(framebufferDimension);
+ this.bindFramebuffer_(frameState, framebufferDimension);
gl.viewport(0, 0, framebufferDimension, framebufferDimension);
gl.clearColor(0, 0, 0, 0);
@@ -464,21 +452,12 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
}, this);
if (!goog.array.isEmpty(tilesToLoad)) {
- goog.events.listenOnce(
- map,
- ol.MapEventType.POSTRENDER,
+ frameState.postRenderFunctions.push(
goog.partial(function(mapRenderer, tilesToLoad) {
- if (goog.DEBUG) {
- this.logger.info(
- 'uploading ' + tilesToLoad.length + ' textures');
- }
goog.array.forEach(tilesToLoad, function(tile) {
mapRenderer.bindTileTexture(
tile, goog.webgl.LINEAR, goog.webgl.LINEAR);
});
- if (goog.DEBUG) {
- this.logger.info('uploaded textures');
- }
}, mapRenderer, tilesToLoad));
}
@@ -488,25 +467,23 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
} else {
this.renderedTileRange_ = null;
this.renderedFramebufferExtent_ = null;
- requestRenderFrame = true;
+ frameState.animate = true;
}
}
goog.vec.Mat4.makeIdentity(this.matrix_);
goog.vec.Mat4.translate(this.matrix_,
- (mapCenter.x - framebufferExtent.minX) /
+ (view2DState.center.x - framebufferExtent.minX) /
(framebufferExtent.maxX - framebufferExtent.minX),
- (mapCenter.y - framebufferExtent.minY) /
+ (view2DState.center.y - framebufferExtent.minY) /
(framebufferExtent.maxY - framebufferExtent.minY),
0);
- if (goog.isDef(mapRotation)) {
- goog.vec.Mat4.rotateZ(this.matrix_, mapRotation);
- }
+ goog.vec.Mat4.rotateZ(this.matrix_, view2DState.rotation);
goog.vec.Mat4.scale(this.matrix_,
- (mapExtent.maxX - mapExtent.minX) /
+ frameState.size.width * view2DState.resolution /
(framebufferExtent.maxX - framebufferExtent.minX),
- (mapExtent.maxY - mapExtent.minY) /
+ frameState.size.height * view2DState.resolution /
(framebufferExtent.maxY - framebufferExtent.minY),
1);
goog.vec.Mat4.translate(this.matrix_,
@@ -514,6 +491,4 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
-0.5,
0);
- return requestRenderFrame;
-
};
diff --git a/src/ol/view2d.js b/src/ol/view2d.js
index 998b226196..f07f560ad6 100644
--- a/src/ol/view2d.js
+++ b/src/ol/view2d.js
@@ -1,4 +1,5 @@
// FIXME getView3D has not return type
+// FIXME remove getExtent?
goog.provide('ol.View2D');
goog.provide('ol.View2DProperty');
@@ -92,32 +93,6 @@ ol.View2D.prototype.getExtent = function(size) {
};
-/**
- * @param {ol.Size} size Box pixel size.
- * @return {ol.Extent} Rotated extent.
- */
-ol.View2D.prototype.getRotatedExtent = function(size) {
- goog.asserts.assert(this.isDef());
- // FIXME try w/o casting
- var center = /** @type {!ol.Coordinate} */ (this.getCenter());
- var resolution = this.getResolution();
- var rotation = this.getRotation();
- var xScale = resolution * size.width / 2;
- var yScale = resolution * size.height / 2;
- var corners = [
- new ol.Coordinate(-xScale, -yScale),
- new ol.Coordinate(-xScale, yScale),
- new ol.Coordinate(xScale, -yScale),
- new ol.Coordinate(xScale, yScale)
- ];
- goog.array.forEach(corners, function(corner) {
- corner.rotate(rotation);
- corner.add(center);
- });
- return ol.Extent.boundingExtent.apply(null, corners);
-};
-
-
/**
* @inheritDoc
*/
@@ -177,6 +152,24 @@ ol.View2D.prototype.getView2D = function() {
};
+/**
+ * @inheritDoc
+ */
+ol.View2D.prototype.getView2DState = function() {
+ goog.asserts.assert(this.isDef());
+ var center = /** @type {ol.Coordinate} */ (this.getCenter());
+ var projection = /** @type {ol.Projection} */ (this.getProjection());
+ var resolution = /** @type {number} */ (this.getResolution());
+ var rotation = /** @type {number} */ (this.getRotation());
+ return {
+ center: new ol.Coordinate(center.x, center.y),
+ projection: projection,
+ resolution: resolution,
+ rotation: rotation
+ };
+};
+
+
/**
* FIXME return type
*/