Merge pull request #33 from tsauerwein/webgl-point-symbol-atlas

[webgl-point] Use the atlas manager for symbols [WIP]
This commit is contained in:
Tobias Sauerwein
2014-11-20 09:50:03 +01:00
10 changed files with 861 additions and 115 deletions

View File

@@ -0,0 +1,52 @@
<!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>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>

View 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 default size so that all symbols fit into
// a single atlas image
size: 512,
maxSize: ol.has.WEBGL_MAX_TEXTURE_SIZE});
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 = 30000;
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 - 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: 4
})
});

View File

@@ -0,0 +1,52 @@
<!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>Regular shape symbols</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">Regular shape symbols</h4>
<p id="shortdesc">Drawing stars with regular shape symbols.</p>
<div id="docs">
<p>See the <a href="symbol-stars.js" target="_blank">symbol-stars.js source</a> to see how this is done.</p>
</div>
<div id="tags">symbol, regular, star, 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-stars" type="text/javascript"></script>
</body>
</html>

89
examples/symbol-stars.js Normal file
View File

@@ -0,0 +1,89 @@
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.Fill');
goog.require('ol.style.RegularShape');
goog.require('ol.style.Stroke');
goog.require('ol.style.Style');
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;
var symbols = [];
var i, j;
for (i = 0; i < symbolInfo.length; ++i) {
var info = symbolInfo[i];
for (j = 0; j < radiuses.length; ++j) {
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
})
}));
}
}
var featureCount = 5000;
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 - 1)]
})
);
features[i] = feature;
}
var vectorSource = new ol.source.Vector({
features: features
});
var vector = new ol.layer.Vector({
source: vectorSource
});
var map = new ol.Map({
layers: [vector],
target: document.getElementById('map'),
view: new ol.View({
center: [0, 0],
zoom: 3
})
});

View File

@@ -5399,7 +5399,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;
@@ -5443,6 +5444,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
@@ -5616,7 +5627,8 @@ olx.style.IconOptions.prototype.src;
* radius2: number,
* angle: number,
* snapToPixel: (boolean|undefined),
* stroke: (ol.style.Stroke|undefined)}}
* stroke: (ol.style.Stroke|undefined),
* atlasManager: (ol.style.AtlasManager|undefined)}}
* @api
*/
olx.style.RegularShapeOptions;
@@ -5689,6 +5701,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),

View File

@@ -143,16 +143,16 @@ ol.style.AtlasManager.prototype.add =
* @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} 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.
* @type {number}
*/
this.space_ = space;

View File

@@ -1,5 +1,6 @@
goog.provide('ol.style.Circle');
goog.require('goog.asserts');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('ol.color');
@@ -26,12 +27,17 @@ ol.style.Circle = function(opt_options) {
var options = goog.isDef(opt_options) ? opt_options : {};
/**
* @private
* @type {Array.<ol.structs.Checksum>|null}
*/
this.checksums_ = null;
/**
* @private
* @type {HTMLCanvasElement}
*/
this.canvas_ = /** @type {HTMLCanvasElement} */
(goog.dom.createElement(goog.dom.TagName.CANVAS));
this.canvas_ = null;
/**
* @private
@@ -47,9 +53,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
@@ -59,29 +65,29 @@ 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.anchor_ = null;
/**
* @private
* @type {ol.Size}
*/
this.size_ = [size, size];
this.size_ = null;
/**
* @private
* @type {Array.<ol.structs.Checksum>|null}
* @type {ol.Size}
*/
this.checksums_ = null;
this.imageSize_ = null;
this.render_(options.atlasManager);
/**
* @type {boolean}
@@ -149,7 +155,7 @@ ol.style.Circle.prototype.getImageState = function() {
* @inheritDoc
*/
ol.style.Circle.prototype.getImageSize = function() {
return this.size_;
return this.imageSize_;
};
@@ -208,16 +214,20 @@ ol.style.Circle.prototype.unlistenImageChange = goog.nullFunction;
/**
* @private
* @return {number} Size.
* @typedef {{strokeStyle: (string|undefined), strokeWidth: number,
* size: number}}
*/
ol.style.Circle.prototype.render_ = function() {
var canvas = this.canvas_;
var strokeStyle, strokeWidth;
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 strokeStyle, strokeWidth = 0, imageSize;
if (!goog.isNull(this.stroke_)) {
strokeStyle = ol.color.asString(this.stroke_.getColor());
strokeWidth = this.stroke_.getWidth();
if (!goog.isDef(strokeWidth)) {
@@ -227,54 +237,112 @@ ol.style.Circle.prototype.render_ = function() {
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
};
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);
} else {
// an atlas manager is used, add the symbol to an atlas
size = Math.round(size);
var id = this.getChecksum();
var info = atlasManager.add(
id, size, size, goog.bind(this.draw_, this, renderOptions));
goog.asserts.assert(info !== null, 'circle radius is too large');
this.canvas_ = info.image;
this.origin_ = [info.offsetX, info.offsetY];
imageSize = info.image.width;
}
this.anchor_ = [size / 2, size / 2];
this.size_ = [size, size];
this.imageSize_ = [imageSize, imageSize];
// deal with the hit detection canvas
if (!goog.isNull(this.fill_)) {
this.hitDetectionCanvas_ = this.canvas_;
} else {
this.createHitDetectionCanvas_(renderOptions);
}
};
/**
* @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;
context.lineWidth = strokeWidth;
context.strokeStyle = renderOptions.strokeStyle;
context.lineWidth = renderOptions.strokeWidth;
context.stroke();
}
context.closePath();
};
// deal with the hit detection canvas
if (!goog.isNull(this.fill_)) {
this.hitDetectionCanvas_ = canvas;
} else {
this.hitDetectionCanvas_ = /** @type {HTMLCanvasElement} */
(goog.dom.createElement(goog.dom.TagName.CANVAS));
canvas = this.hitDetectionCanvas_;
/**
* @private
* @param {ol.style.Circle.RenderOptions} renderOptions
*/
ol.style.Circle.prototype.createHitDetectionCanvas_ = function(renderOptions) {
this.hitDetectionCanvas_ = /** @type {HTMLCanvasElement} */
(goog.dom.createElement(goog.dom.TagName.CANVAS));
var canvas = this.hitDetectionCanvas_;
canvas.height = size;
canvas.width = size;
canvas.height = renderOptions.size;
canvas.width = renderOptions.size;
context = /** @type {CanvasRenderingContext2D} */
(canvas.getContext('2d'));
context.arc(size / 2, size / 2, this.radius_, 0, 2 * Math.PI, true);
var context = /** @type {CanvasRenderingContext2D} */
(canvas.getContext('2d'));
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 = strokeStyle;
context.lineWidth = strokeWidth;
context.stroke();
}
context.fillStyle = ol.render.canvas.defaultFillStyle;
context.fill();
if (!goog.isNull(this.stroke_)) {
context.strokeStyle = renderOptions.strokeStyle;
context.lineWidth = renderOptions.strokeWidth;
context.stroke();
}
return size;
};

View File

@@ -1,5 +1,6 @@
goog.provide('ol.style.RegularShape');
goog.require('goog.asserts');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('ol.color');
@@ -18,18 +19,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.<ol.structs.Checksum>|null}
*/
this.checksums_ = null;
/**
* @private
* @type {HTMLCanvasElement}
*/
this.canvas_ = /** @type {HTMLCanvasElement} */
(goog.dom.createElement(goog.dom.TagName.CANVAS));
this.canvas_ = null;
/**
* @private
@@ -80,19 +87,25 @@ 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;
this.render_(options.atlasManager);
/**
* @type {boolean}
@@ -151,7 +164,7 @@ ol.style.RegularShape.prototype.getImage = function(pixelRatio) {
* @inheritDoc
*/
ol.style.RegularShape.prototype.getImageSize = function() {
return this.size_;
return this.imageSize_;
};
@@ -218,16 +231,20 @@ ol.style.RegularShape.prototype.unlistenImageChange = goog.nullFunction;
/**
* @private
* @return {number} Size.
* @typedef {{strokeStyle: (string|undefined), strokeWidth: number,
* size: number}}
*/
ol.style.RegularShape.prototype.render_ = function() {
var canvas = this.canvas_;
var strokeStyle, strokeWidth;
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 strokeStyle, strokeWidth = 0, imageSize;
if (!goog.isNull(this.stroke_)) {
strokeStyle = ol.color.asString(this.stroke_.getColor());
strokeWidth = this.stroke_.getWidth();
if (!goog.isDef(strokeWidth)) {
@@ -237,17 +254,70 @@ ol.style.RegularShape.prototype.render_ = function() {
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
};
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);
} else {
// an atlas manager is used, add the symbol to an atlas
size = Math.round(size);
var id = this.getChecksum();
var info = atlasManager.add(
id, size, size, goog.bind(this.draw_, this, renderOptions));
goog.asserts.assert(info !== null, 'shape size is too large');
this.canvas_ = info.image;
this.origin_ = [info.offsetX, info.offsetY];
imageSize = info.image.width;
}
this.anchor_ = [size / 2, size / 2];
this.size_ = [size, size];
this.imageSize_ = [imageSize, imageSize];
// deal with the hit detection canvas
if (!goog.isNull(this.fill_)) {
this.hitDetectionCanvas_ = this.canvas_;
} else {
this.createHitDetectionCanvas_(renderOptions);
}
};
/**
* @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_;
@@ -255,8 +325,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_)) {
@@ -264,44 +334,77 @@ ol.style.RegularShape.prototype.render_ = function() {
context.fill();
}
if (!goog.isNull(this.stroke_)) {
context.strokeStyle = strokeStyle;
context.lineWidth = strokeWidth;
context.strokeStyle = renderOptions.strokeStyle;
context.lineWidth = renderOptions.strokeWidth;
context.stroke();
}
context.closePath();
};
// deal with the hit detection canvas
if (!goog.isNull(this.fill_)) {
this.hitDetectionCanvas_ = canvas;
} else {
this.hitDetectionCanvas_ = /** @type {HTMLCanvasElement} */
(goog.dom.createElement(goog.dom.TagName.CANVAS));
canvas = this.hitDetectionCanvas_;
/**
* @private
* @param {ol.style.RegularShape.RenderOptions} renderOptions
*/
ol.style.RegularShape.prototype.createHitDetectionCanvas_ =
function(renderOptions) {
this.hitDetectionCanvas_ = /** @type {HTMLCanvasElement} */
(goog.dom.createElement(goog.dom.TagName.CANVAS));
var canvas = this.hitDetectionCanvas_;
canvas.height = size;
canvas.width = size;
canvas.height = renderOptions.size;
canvas.width = renderOptions.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;
context.lineWidth = strokeWidth;
context.stroke();
}
var context = /** @type {CanvasRenderingContext2D} */
(canvas.getContext('2d'));
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));
}
return size;
context.fillStyle = ol.render.canvas.defaultFillStyle;
context.fill();
if (!goog.isNull(this.stroke_)) {
context.strokeStyle = renderOptions.strokeStyle;
context.lineWidth = renderOptions.strokeWidth;
context.stroke();
}
};
/**
* @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];
};

View File

@@ -3,6 +3,32 @@ goog.provide('ol.test.style.Circle');
describe('ol.style.Circle', function() {
describe('#constructor', function() {
it('creates a canvas if no atlas is used', 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]);
expect(style.getImage()).to.not.be(style.getHitDetectionImage());
expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement);
});
it('adds itself to an atlas manager', function() {
var atlasManager = new ol.style.AtlasManager({size: 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]);
expect(style.getImage()).to.not.be(style.getHitDetectionImage());
expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement);
});
});
describe('#getChecksum', function() {
it('calculates the same hash code for default options', function() {
@@ -162,6 +188,7 @@ describe('ol.style.Circle', function() {
});
});
goog.require('ol.style.AtlasManager');
goog.require('ol.style.Circle');
goog.require('ol.style.Fill');
goog.require('ol.style.Stroke');

View File

@@ -0,0 +1,210 @@
goog.provide('ol.test.style.RegularShape');
describe('ol.style.RegularShape', function() {
describe('#constructor', function() {
it('creates a canvas if no atlas is used', 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]);
expect(style.getImage()).to.not.be(style.getHitDetectionImage());
expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement);
});
it('adds itself to an atlas manager', function() {
var atlasManager = new ol.style.AtlasManager({size: 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]);
expect(style.getImage()).to.not.be(style.getHitDetectionImage());
expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement);
});
});
describe('#getChecksum', function() {
it('calculates the same hash code for default options', function() {
var style1 = new ol.style.RegularShape();
var style2 = new ol.style.RegularShape();
expect(style1.getChecksum()).to.eql(style2.getChecksum());
});
it('calculates not the same hash code (radius)', function() {
var style1 = new ol.style.RegularShape({
radius2: 5
});
var style2 = new ol.style.RegularShape({
radius: 5
});
expect(style1.getChecksum()).to.not.eql(style2.getChecksum());
});
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());
});
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');