diff --git a/externs/olx.js b/externs/olx.js index db910184af..f7bcedf399 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -5607,6 +5607,86 @@ olx.style.IconOptions.prototype.size; olx.style.IconOptions.prototype.src; +/** + * @typedef {{fill: (ol.style.Fill|undefined), + * points: number, + * radius: number, + * radius2: number, + * angle: number, + * snapToPixel: (boolean|undefined), + * stroke: (ol.style.Stroke|undefined)}} + * @api + */ +olx.style.RegularShapeOptions; + + +/** + * Fill style. + * @type {ol.style.Fill|undefined} + * @api + */ +olx.style.RegularShapeOptions.prototype.fill; + + +/** + * Number of points for stars and regular polygons. In case of a polygon, the + * number of points is the number of sides. + * @type {number} + * @api + */ +olx.style.RegularShapeOptions.prototype.points; + + +/** + * Shape radius. + * @type {number} + * @api + */ +olx.style.RegularShapeOptions.prototype.radius; + + +/** + * Shape secondary radius for drawing stars. If radius 2 is equal to radius, + * the regular shape will be a regular polygon instead of a star. + * Default value is equal to radius. + * @type {number} + * @api + */ +olx.style.RegularShapeOptions.prototype.radius2; + + +/** + * Shape's rotation in radians. A value of 0 will have one of the shape's point + * facing up. + * Default value is 0. + * @type {number} + * @api + */ +olx.style.RegularShapeOptions.prototype.angle; + + +/** + * If `true` integral numbers of pixels are used as the X and Y pixel + * coordinate when drawing the shape in the output canvas. If `false` + * fractional numbers may be used. Using `true` allows for "sharp" + * rendering (no blur), while using `false` allows for "accurate" + * rendering. Note that accuracy is important if the shape's + * position is animated. Without it, the shape may jitter noticeably. + * Default value is `true`. + * @type {boolean|undefined} + * @api + */ +olx.style.RegularShapeOptions.prototype.snapToPixel; + + +/** + * Stroke style. + * @type {ol.style.Stroke|undefined} + * @api + */ +olx.style.RegularShapeOptions.prototype.stroke; + + /** * @typedef {{color: (ol.Color|string|undefined), * lineCap: (string|undefined), diff --git a/src/ol/style/regularshapestyle.js b/src/ol/style/regularshapestyle.js new file mode 100644 index 0000000000..74850f1225 --- /dev/null +++ b/src/ol/style/regularshapestyle.js @@ -0,0 +1,299 @@ +goog.provide('ol.style.RegularShape'); + +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('ol.color'); +goog.require('ol.render.canvas'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Image'); +goog.require('ol.style.ImageState'); +goog.require('ol.style.Stroke'); + + + +/** + * @classdesc + * Set regular shape style for vector features. + * + * @constructor + * @param {olx.style.RegularShapeOptions=} opt_options Options. + * @extends {ol.style.Image} + * @api + */ +ol.style.RegularShape = function(opt_options) { + + var options = goog.isDef(opt_options) ? opt_options : {}; + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = /** @type {HTMLCanvasElement} */ + (goog.dom.createElement(goog.dom.TagName.CANVAS)); + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.hitDetectionCanvas_ = null; + + /** + * @private + * @type {ol.style.Fill} + */ + this.fill_ = goog.isDef(options.fill) ? options.fill : null; + + /** + * @private + * @type {Array.} + */ + this.origin_ = [0, 0]; + + /** + * @private + * @type {number} + */ + this.points_ = options.points; + + /** + * @private + * @type {number} + */ + this.radius_ = options.radius; + + /** + * @private + * @type {number} + */ + this.radius2_ = + goog.isDef(options.radius2) ? options.radius2 : options.radius; + + /** + * @private + * @type {number} + */ + this.angle_ = goog.isDef(options.angle) ? options.angle : 0; + + /** + * @private + * @type {ol.style.Stroke} + */ + this.stroke_ = goog.isDef(options.stroke) ? options.stroke : null; + + var size = this.render_(); + + /** + * @private + * @type {Array.} + */ + this.anchor_ = [size / 2, size / 2]; + + /** + * @private + * @type {ol.Size} + */ + this.size_ = [size, size]; + + /** + * @type {boolean} + */ + var snapToPixel = goog.isDef(options.snapToPixel) ? + options.snapToPixel : true; + + goog.base(this, { + opacity: 1, + rotateWithView: false, + rotation: 0, + scale: 1, + snapToPixel: snapToPixel + }); + +}; +goog.inherits(ol.style.RegularShape, ol.style.Image); + + +/** + * @inheritDoc + * @api + */ +ol.style.RegularShape.prototype.getAnchor = function() { + return this.anchor_; +}; + + +/** + * @return {ol.style.Fill} Fill style. + * @api + */ +ol.style.RegularShape.prototype.getFill = function() { + return this.fill_; +}; + + +/** + * @inheritDoc + */ +ol.style.RegularShape.prototype.getHitDetectionImage = function(pixelRatio) { + return this.hitDetectionCanvas_; +}; + + +/** + * @inheritDoc + * @api + */ +ol.style.RegularShape.prototype.getImage = function(pixelRatio) { + return this.canvas_; +}; + + +/** + * @inheritDoc + */ +ol.style.RegularShape.prototype.getImageState = function() { + return ol.style.ImageState.LOADED; +}; + + +/** + * @inheritDoc + * @api + */ +ol.style.RegularShape.prototype.getOrigin = function() { + return this.origin_; +}; + + +/** + * @return {number} Radius. + * @api + */ +ol.style.RegularShape.prototype.getRadius = function() { + return this.radius_; +}; + + +/** + * @inheritDoc + * @api + */ +ol.style.RegularShape.prototype.getSize = function() { + return this.size_; +}; + + +/** + * @return {ol.style.Stroke} Stroke style. + * @api + */ +ol.style.RegularShape.prototype.getStroke = function() { + return this.stroke_; +}; + + +/** + * @inheritDoc + */ +ol.style.RegularShape.prototype.listenImageChange = goog.nullFunction; + + +/** + * @inheritDoc + */ +ol.style.RegularShape.prototype.load = goog.nullFunction; + + +/** + * @inheritDoc + */ +ol.style.RegularShape.prototype.unlistenImageChange = goog.nullFunction; + + +/** + * @private + * @return {number} Size. + */ +ol.style.RegularShape.prototype.render_ = function() { + var canvas = this.canvas_; + var strokeStyle, strokeWidth; + + if (goog.isNull(this.stroke_)) { + strokeWidth = 0; + } else { + strokeStyle = ol.color.asString(this.stroke_.getColor()); + strokeWidth = this.stroke_.getWidth(); + if (!goog.isDef(strokeWidth)) { + strokeWidth = ol.render.canvas.defaultLineWidth; + } + } + + var size = 2 * (this.radius_ + strokeWidth) + 1; + + // draw the regular shape on the canvas + + canvas.height = size; + canvas.width = size; + + // canvas.width and height are rounded to the closest integer + size = canvas.width; + + var context = /** @type {CanvasRenderingContext2D} */ + (canvas.getContext('2d')); + var i, angle0, radiusC; + 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)); + } + + 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.stroke(); + } + + // 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_; + + 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; + context.lineWidth = strokeWidth; + context.stroke(); + } + } + + return size; +};