openlayers
Version:
Build tools and sources for developing OpenLayers based mapping applications
432 lines (370 loc) • 13.9 kB
JavaScript
goog.provide('ol.render.webgl.CircleReplay');
goog.require('ol');
goog.require('ol.array');
goog.require('ol.color');
goog.require('ol.extent');
goog.require('ol.obj');
goog.require('ol.geom.flat.transform');
goog.require('ol.render.webgl.circlereplay.defaultshader');
goog.require('ol.render.webgl.Replay');
goog.require('ol.render.webgl');
goog.require('ol.webgl');
goog.require('ol.webgl.Buffer');
/**
* @constructor
* @extends {ol.render.webgl.Replay}
* @param {number} tolerance Tolerance.
* @param {ol.Extent} maxExtent Max extent.
* @struct
*/
ol.render.webgl.CircleReplay = function(tolerance, maxExtent) {
ol.render.webgl.Replay.call(this, tolerance, maxExtent);
/**
* @private
* @type {ol.render.webgl.circlereplay.defaultshader.Locations}
*/
this.defaultLocations_ = null;
/**
* @private
* @type {Array.<Array.<Array.<number>|number>>}
*/
this.styles_ = [];
/**
* @private
* @type {Array.<number>}
*/
this.styleIndices_ = [];
/**
* @private
* @type {number}
*/
this.radius_ = 0;
/**
* @private
* @type {{fillColor: (Array.<number>|null),
* strokeColor: (Array.<number>|null),
* lineDash: Array.<number>,
* lineWidth: (number|undefined),
* changed: boolean}|null}
*/
this.state_ = {
fillColor: null,
strokeColor: null,
lineDash: null,
lineWidth: undefined,
changed: false
};
};
ol.inherits(ol.render.webgl.CircleReplay, ol.render.webgl.Replay);
/**
* @private
* @param {Array.<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {number} end End.
* @param {number} stride Stride.
*/
ol.render.webgl.CircleReplay.prototype.drawCoordinates_ = function(
flatCoordinates, offset, end, stride) {
var numVertices = this.vertices.length;
var numIndices = this.indices.length;
var n = numVertices / 4;
var i, ii;
for (i = offset, ii = end; i < ii; i += stride) {
this.vertices[numVertices++] = flatCoordinates[i];
this.vertices[numVertices++] = flatCoordinates[i + 1];
this.vertices[numVertices++] = 0;
this.vertices[numVertices++] = this.radius_;
this.vertices[numVertices++] = flatCoordinates[i];
this.vertices[numVertices++] = flatCoordinates[i + 1];
this.vertices[numVertices++] = 1;
this.vertices[numVertices++] = this.radius_;
this.vertices[numVertices++] = flatCoordinates[i];
this.vertices[numVertices++] = flatCoordinates[i + 1];
this.vertices[numVertices++] = 2;
this.vertices[numVertices++] = this.radius_;
this.vertices[numVertices++] = flatCoordinates[i];
this.vertices[numVertices++] = flatCoordinates[i + 1];
this.vertices[numVertices++] = 3;
this.vertices[numVertices++] = this.radius_;
this.indices[numIndices++] = n;
this.indices[numIndices++] = n + 1;
this.indices[numIndices++] = n + 2;
this.indices[numIndices++] = n + 2;
this.indices[numIndices++] = n + 3;
this.indices[numIndices++] = n;
n += 4;
}
};
/**
* @inheritDoc
*/
ol.render.webgl.CircleReplay.prototype.drawCircle = function(circleGeometry, feature) {
var radius = circleGeometry.getRadius();
var stride = circleGeometry.getStride();
if (radius) {
this.startIndices.push(this.indices.length);
this.startIndicesFeature.push(feature);
if (this.state_.changed) {
this.styleIndices_.push(this.indices.length);
this.state_.changed = false;
}
this.radius_ = radius;
var flatCoordinates = circleGeometry.getFlatCoordinates();
flatCoordinates = ol.geom.flat.transform.translate(flatCoordinates, 0, 2,
stride, -this.origin[0], -this.origin[1]);
this.drawCoordinates_(flatCoordinates, 0, 2, stride);
} else {
if (this.state_.changed) {
this.styles_.pop();
if (this.styles_.length) {
var lastState = this.styles_[this.styles_.length - 1];
this.state_.fillColor = /** @type {Array.<number>} */ (lastState[0]);
this.state_.strokeColor = /** @type {Array.<number>} */ (lastState[1]);
this.state_.lineWidth = /** @type {number} */ (lastState[2]);
this.state_.changed = false;
}
}
}
};
/**
* @inheritDoc
**/
ol.render.webgl.CircleReplay.prototype.finish = function(context) {
// create, bind, and populate the vertices buffer
this.verticesBuffer = new ol.webgl.Buffer(this.vertices);
// create, bind, and populate the indices buffer
this.indicesBuffer = new ol.webgl.Buffer(this.indices);
this.startIndices.push(this.indices.length);
//Clean up, if there is nothing to draw
if (this.styleIndices_.length === 0 && this.styles_.length > 0) {
this.styles_ = [];
}
this.vertices = null;
this.indices = null;
};
/**
* @inheritDoc
*/
ol.render.webgl.CircleReplay.prototype.getDeleteResourcesFunction = function(context) {
// We only delete our stuff here. The shaders and the program may
// be used by other CircleReplay instances (for other layers). And
// they will be deleted when disposing of the ol.webgl.Context
// object.
ol.DEBUG && console.assert(this.verticesBuffer,
'verticesBuffer must not be null');
ol.DEBUG && console.assert(this.indicesBuffer,
'indicesBuffer must not be null');
var verticesBuffer = this.verticesBuffer;
var indicesBuffer = this.indicesBuffer;
return function() {
context.deleteBuffer(verticesBuffer);
context.deleteBuffer(indicesBuffer);
};
};
/**
* @inheritDoc
*/
ol.render.webgl.CircleReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) {
// get the program
var fragmentShader, vertexShader;
fragmentShader = ol.render.webgl.circlereplay.defaultshader.fragment;
vertexShader = ol.render.webgl.circlereplay.defaultshader.vertex;
var program = context.getProgram(fragmentShader, vertexShader);
// get the locations
var locations;
if (!this.defaultLocations_) {
locations =
new ol.render.webgl.circlereplay.defaultshader.Locations(gl, program);
this.defaultLocations_ = locations;
} else {
locations = this.defaultLocations_;
}
context.useProgram(program);
// enable the vertex attrib arrays
gl.enableVertexAttribArray(locations.a_position);
gl.vertexAttribPointer(locations.a_position, 2, ol.webgl.FLOAT,
false, 16, 0);
gl.enableVertexAttribArray(locations.a_instruction);
gl.vertexAttribPointer(locations.a_instruction, 1, ol.webgl.FLOAT,
false, 16, 8);
gl.enableVertexAttribArray(locations.a_radius);
gl.vertexAttribPointer(locations.a_radius, 1, ol.webgl.FLOAT,
false, 16, 12);
// Enable renderer specific uniforms.
gl.uniform2fv(locations.u_size, size);
gl.uniform1f(locations.u_pixelRatio, pixelRatio);
return locations;
};
/**
* @inheritDoc
*/
ol.render.webgl.CircleReplay.prototype.shutDownProgram = function(gl, locations) {
gl.disableVertexAttribArray(locations.a_position);
gl.disableVertexAttribArray(locations.a_instruction);
gl.disableVertexAttribArray(locations.a_radius);
};
/**
* @inheritDoc
*/
ol.render.webgl.CircleReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) {
if (!ol.obj.isEmpty(skippedFeaturesHash)) {
this.drawReplaySkipping_(gl, context, skippedFeaturesHash);
} else {
ol.DEBUG && console.assert(this.styles_.length === this.styleIndices_.length,
'number of styles and styleIndices match');
//Draw by style groups to minimize drawElements() calls.
var i, start, end, nextStyle;
end = this.startIndices[this.startIndices.length - 1];
for (i = this.styleIndices_.length - 1; i >= 0; --i) {
start = this.styleIndices_[i];
nextStyle = this.styles_[i];
this.setFillStyle_(gl, /** @type {Array.<number>} */ (nextStyle[0]));
this.setStrokeStyle_(gl, /** @type {Array.<number>} */ (nextStyle[1]),
/** @type {number} */ (nextStyle[2]));
this.drawElements(gl, context, start, end);
end = start;
}
}
};
/**
* @inheritDoc
*/
ol.render.webgl.CircleReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash,
featureCallback, opt_hitExtent) {
ol.DEBUG && console.assert(this.styles_.length === this.styleIndices_.length,
'number of styles and styleIndices match');
ol.DEBUG && console.assert(this.startIndices.length - 1 === this.startIndicesFeature.length,
'number of startIndices and startIndicesFeature match');
var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex;
featureIndex = this.startIndices.length - 2;
end = this.startIndices[featureIndex + 1];
for (i = this.styleIndices_.length - 1; i >= 0; --i) {
nextStyle = this.styles_[i];
this.setFillStyle_(gl, /** @type {Array.<number>} */ (nextStyle[0]));
this.setStrokeStyle_(gl, /** @type {Array.<number>} */ (nextStyle[1]),
/** @type {number} */ (nextStyle[2]));
groupStart = this.styleIndices_[i];
while (featureIndex >= 0 &&
this.startIndices[featureIndex] >= groupStart) {
start = this.startIndices[featureIndex];
feature = this.startIndicesFeature[featureIndex];
featureUid = ol.getUid(feature).toString();
if (skippedFeaturesHash[featureUid] === undefined &&
feature.getGeometry() &&
(opt_hitExtent === undefined || ol.extent.intersects(
/** @type {Array<number>} */ (opt_hitExtent),
feature.getGeometry().getExtent()))) {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
this.drawElements(gl, context, start, end);
var result = featureCallback(feature);
if (result) {
return result;
}
}
featureIndex--;
end = start;
}
}
return undefined;
};
/**
* @private
* @param {WebGLRenderingContext} gl gl.
* @param {ol.webgl.Context} context Context.
* @param {Object} skippedFeaturesHash Ids of features to skip.
*/
ol.render.webgl.CircleReplay.prototype.drawReplaySkipping_ = function(gl, context, skippedFeaturesHash) {
ol.DEBUG && console.assert(this.startIndices.length - 1 === this.startIndicesFeature.length,
'number of startIndices and startIndicesFeature match');
var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart;
featureIndex = this.startIndices.length - 2;
end = start = this.startIndices[featureIndex + 1];
for (i = this.styleIndices_.length - 1; i >= 0; --i) {
nextStyle = this.styles_[i];
this.setFillStyle_(gl, /** @type {Array.<number>} */ (nextStyle[0]));
this.setStrokeStyle_(gl, /** @type {Array.<number>} */ (nextStyle[1]),
/** @type {number} */ (nextStyle[2]));
groupStart = this.styleIndices_[i];
while (featureIndex >= 0 &&
this.startIndices[featureIndex] >= groupStart) {
featureStart = this.startIndices[featureIndex];
feature = this.startIndicesFeature[featureIndex];
featureUid = ol.getUid(feature).toString();
if (skippedFeaturesHash[featureUid]) {
if (start !== end) {
this.drawElements(gl, context, start, end);
}
end = featureStart;
}
featureIndex--;
start = featureStart;
}
if (start !== end) {
this.drawElements(gl, context, start, end);
}
start = end = groupStart;
}
};
/**
* @private
* @param {WebGLRenderingContext} gl gl.
* @param {Array.<number>} color Color.
*/
ol.render.webgl.CircleReplay.prototype.setFillStyle_ = function(gl, color) {
gl.uniform4fv(this.defaultLocations_.u_fillColor, color);
};
/**
* @private
* @param {WebGLRenderingContext} gl gl.
* @param {Array.<number>} color Color.
* @param {number} lineWidth Line width.
*/
ol.render.webgl.CircleReplay.prototype.setStrokeStyle_ = function(gl, color, lineWidth) {
gl.uniform4fv(this.defaultLocations_.u_strokeColor, color);
gl.uniform1f(this.defaultLocations_.u_lineWidth, lineWidth);
};
/**
* @inheritDoc
*/
ol.render.webgl.CircleReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) {
ol.DEBUG && console.assert(this.state_, 'this.state_ should not be null');
var strokeStyleColor, strokeStyleWidth;
if (strokeStyle) {
var strokeStyleLineDash = strokeStyle.getLineDash();
this.state_.lineDash = strokeStyleLineDash ?
strokeStyleLineDash : ol.render.webgl.defaultLineDash;
strokeStyleColor = strokeStyle.getColor();
if (!(strokeStyleColor instanceof CanvasGradient) &&
!(strokeStyleColor instanceof CanvasPattern)) {
strokeStyleColor = ol.color.asArray(strokeStyleColor).map(function(c, i) {
return i != 3 ? c / 255 : c;
}) || ol.render.webgl.defaultStrokeStyle;
} else {
strokeStyleColor = ol.render.webgl.defaultStrokeStyle;
}
strokeStyleWidth = strokeStyle.getWidth();
strokeStyleWidth = strokeStyleWidth !== undefined ?
strokeStyleWidth : ol.render.webgl.defaultLineWidth;
} else {
strokeStyleColor = [0, 0, 0, 0];
strokeStyleWidth = 0;
}
var fillStyleColor = fillStyle ? fillStyle.getColor() : [0, 0, 0, 0];
if (!(fillStyleColor instanceof CanvasGradient) &&
!(fillStyleColor instanceof CanvasPattern)) {
fillStyleColor = ol.color.asArray(fillStyleColor).map(function(c, i) {
return i != 3 ? c / 255 : c;
}) || ol.render.webgl.defaultFillStyle;
} else {
fillStyleColor = ol.render.webgl.defaultFillStyle;
}
if (!this.state_.strokeColor || !ol.array.equals(this.state_.strokeColor, strokeStyleColor) ||
!this.state_.fillColor || !ol.array.equals(this.state_.fillColor, fillStyleColor) ||
this.state_.lineWidth !== strokeStyleWidth) {
this.state_.changed = true;
this.state_.fillColor = fillStyleColor;
this.state_.strokeColor = strokeStyleColor;
this.state_.lineWidth = strokeStyleWidth;
this.styles_.push([fillStyleColor, strokeStyleColor, strokeStyleWidth]);
}
};