From d57a35ffcb437f9e1bce06e7d66dc2582bafa958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 3 Jun 2014 12:41:01 +0200 Subject: [PATCH 1/2] Fix stride related bug in Canvas immediate API --- src/ol/geom/flat/transformflatgeom.js | 6 ++- src/ol/geom/simplegeometry.js | 3 +- src/ol/render/canvas/canvasimmediate.js | 61 +++++++++++++------------ src/ol/render/canvas/canvasreplay.js | 6 ++- 4 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/ol/geom/flat/transformflatgeom.js b/src/ol/geom/flat/transformflatgeom.js index dc8c77e45e..574c8bcd33 100644 --- a/src/ol/geom/flat/transformflatgeom.js +++ b/src/ol/geom/flat/transformflatgeom.js @@ -5,13 +5,15 @@ goog.require('goog.vec.Mat4'); /** * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. * @param {number} stride Stride. * @param {goog.vec.Mat4.Number} transform Transform. * @param {Array.=} opt_dest Destination. * @return {Array.} Transformed coordinates. */ ol.geom.flat.transform.transform2D = - function(flatCoordinates, stride, transform, opt_dest) { + function(flatCoordinates, offset, end, stride, transform, opt_dest) { var m00 = goog.vec.Mat4.getElement(transform, 0, 0); var m10 = goog.vec.Mat4.getElement(transform, 1, 0); var m01 = goog.vec.Mat4.getElement(transform, 0, 1); @@ -21,7 +23,7 @@ ol.geom.flat.transform.transform2D = var dest = goog.isDef(opt_dest) ? opt_dest : []; var i = 0; var j, jj; - for (j = 0, jj = flatCoordinates.length; j < jj; j += stride) { + for (j = offset; j < end; j += stride) { var x = flatCoordinates[j]; var y = flatCoordinates[j + 1]; dest[i++] = m00 * x + m01 * y + m03; diff --git a/src/ol/geom/simplegeometry.js b/src/ol/geom/simplegeometry.js index 456bb3f28c..16ff543fdd 100644 --- a/src/ol/geom/simplegeometry.js +++ b/src/ol/geom/simplegeometry.js @@ -269,6 +269,7 @@ ol.geom.transformSimpleGeometry2D = } else { var stride = simpleGeometry.getStride(); return ol.geom.flat.transform.transform2D( - flatCoordinates, stride, transform, opt_dest); + flatCoordinates, 0, flatCoordinates.length, stride, + transform, opt_dest); } }; diff --git a/src/ol/render/canvas/canvasimmediate.js b/src/ol/render/canvas/canvasimmediate.js index f9ce406d53..f5b163fb92 100644 --- a/src/ol/render/canvas/canvasimmediate.js +++ b/src/ol/render/canvas/canvasimmediate.js @@ -256,7 +256,8 @@ ol.render.canvas.Immediate.prototype.drawImages_ = goog.asserts.assert(offset === 0); goog.asserts.assert(end == flatCoordinates.length); var pixelCoordinates = ol.geom.flat.transform.transform2D( - flatCoordinates, 2, this.transform_, this.pixelCoordinates_); + flatCoordinates, offset, end, 2, this.transform_, + this.pixelCoordinates_); var context = this.context_; var localTransform = this.tmpLocalTransform_; var alpha = context.globalAlpha; @@ -324,7 +325,8 @@ ol.render.canvas.Immediate.prototype.drawText_ = goog.asserts.assert(offset === 0); goog.asserts.assert(end == flatCoordinates.length); var pixelCoordinates = ol.geom.flat.transform.transform2D( - flatCoordinates, stride, this.transform_, this.pixelCoordinates_); + flatCoordinates, offset, end, stride, this.transform_, + this.pixelCoordinates_); var context = this.context_; for (; offset < end; offset += stride) { var x = pixelCoordinates[offset] + this.textOffsetX_; @@ -354,41 +356,47 @@ ol.render.canvas.Immediate.prototype.drawText_ = /** - * @param {Array.} pixelCoordinates Pixel coordinates. + * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. * @param {number} end End. + * @param {number} stride Stride. * @param {boolean} close Close. * @private * @return {number} end End. */ ol.render.canvas.Immediate.prototype.moveToLineTo_ = - function(pixelCoordinates, offset, end, close) { + function(flatCoordinates, offset, end, stride, close) { var context = this.context_; - context.moveTo(pixelCoordinates[offset], pixelCoordinates[offset + 1]); + var pixelCoordinates = ol.geom.flat.transform.transform2D( + flatCoordinates, offset, end, stride, this.transform_, + this.pixelCoordinates_); + context.moveTo(pixelCoordinates[0], pixelCoordinates[1]); var i; - for (i = offset + 2; i < end; i += 2) { + for (i = 2; i < pixelCoordinates.length; i += 2) { context.lineTo(pixelCoordinates[i], pixelCoordinates[i + 1]); } if (close) { - context.lineTo(pixelCoordinates[offset], pixelCoordinates[offset + 1]); + context.lineTo(pixelCoordinates[0], pixelCoordinates[1]); } return end; }; /** - * @param {Array.} pixelCoordinates Pixel coordinates. + * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. * @param {Array.} ends Ends. + * @param {number} stride Stride. * @private * @return {number} End. */ ol.render.canvas.Immediate.prototype.drawRings_ = - function(pixelCoordinates, offset, ends) { + function(flatCoordinates, offset, ends, stride) { var context = this.context_; var i, ii; for (i = 0, ii = ends.length; i < ii; ++i) { - offset = this.moveToLineTo_(pixelCoordinates, offset, ends[i], true); + offset = this.moveToLineTo_( + flatCoordinates, offset, ends[i], stride, true); context.closePath(); // FIXME is this needed here? } return offset; @@ -569,11 +577,11 @@ ol.render.canvas.Immediate.prototype.drawLineStringGeometry = } if (!goog.isNull(this.strokeState_)) { this.setContextStrokeState_(this.strokeState_); - var pixelCoordinates = ol.geom.transformSimpleGeometry2D( - lineStringGeometry, this.transform_, this.pixelCoordinates_); var context = this.context_; + var flatCoordinates = lineStringGeometry.getFlatCoordinates(); context.beginPath(); - this.moveToLineTo_(pixelCoordinates, 0, pixelCoordinates.length, false); + this.moveToLineTo_(flatCoordinates, 0, flatCoordinates.length, + lineStringGeometry.getStride(), false); context.stroke(); } if (this.text_ !== '') { @@ -598,18 +606,18 @@ ol.render.canvas.Immediate.prototype.drawMultiLineStringGeometry = if (!ol.extent.intersects(this.extent_, geometryExtent)) { return; } - var pixelCoordinates; if (!goog.isNull(this.strokeState_)) { this.setContextStrokeState_(this.strokeState_); - pixelCoordinates = ol.geom.transformSimpleGeometry2D( - multiLineStringGeometry, this.transform_, this.pixelCoordinates_); var context = this.context_; - context.beginPath(); - var ends = multiLineStringGeometry.getEnds(); + var flatCoordinates = multiLineStringGeometry.getFlatCoordinates(); var offset = 0; + var ends = multiLineStringGeometry.getEnds(); + var stride = multiLineStringGeometry.getStride(); + context.beginPath(); var i, ii; for (i = 0, ii = ends.length; i < ii; ++i) { - offset = this.moveToLineTo_(pixelCoordinates, offset, ends[i], false); + offset = this.moveToLineTo_( + flatCoordinates, offset, ends[i], stride, false); } context.stroke(); } @@ -633,7 +641,6 @@ ol.render.canvas.Immediate.prototype.drawPolygonGeometry = if (!ol.extent.intersects(this.extent_, polygonGeometry.getExtent())) { return; } - var pixelCoordinates; if (!goog.isNull(this.strokeState_) || !goog.isNull(this.fillState_)) { if (!goog.isNull(this.fillState_)) { this.setContextFillState_(this.fillState_); @@ -641,11 +648,10 @@ ol.render.canvas.Immediate.prototype.drawPolygonGeometry = if (!goog.isNull(this.strokeState_)) { this.setContextStrokeState_(this.strokeState_); } - pixelCoordinates = ol.geom.transformSimpleGeometry2D( - polygonGeometry, this.transform_, this.pixelCoordinates_); var context = this.context_; context.beginPath(); - this.drawRings_(pixelCoordinates, 0, polygonGeometry.getEnds()); + this.drawRings_(polygonGeometry.getOrientedFlatCoordinates(), + 0, polygonGeometry.getEnds(), polygonGeometry.getStride()); if (!goog.isNull(this.fillState_)) { context.fill(); } @@ -672,7 +678,6 @@ ol.render.canvas.Immediate.prototype.drawMultiPolygonGeometry = if (!ol.extent.intersects(this.extent_, multiPolygonGeometry.getExtent())) { return; } - var pixelCoordinates; if (!goog.isNull(this.strokeState_) || !goog.isNull(this.fillState_)) { if (!goog.isNull(this.fillState_)) { this.setContextFillState_(this.fillState_); @@ -680,16 +685,16 @@ ol.render.canvas.Immediate.prototype.drawMultiPolygonGeometry = if (!goog.isNull(this.strokeState_)) { this.setContextStrokeState_(this.strokeState_); } - pixelCoordinates = ol.geom.transformSimpleGeometry2D( - multiPolygonGeometry, this.transform_, this.pixelCoordinates_); var context = this.context_; - var endss = multiPolygonGeometry.getEndss(); + var flatCoordinates = multiPolygonGeometry.getOrientedFlatCoordinates(); var offset = 0; + var endss = multiPolygonGeometry.getEndss(); + var stride = multiPolygonGeometry.getStride(); var i, ii; for (i = 0, ii = endss.length; i < ii; ++i) { var ends = endss[i]; context.beginPath(); - offset = this.drawRings_(pixelCoordinates, offset, ends); + offset = this.drawRings_(flatCoordinates, offset, ends, stride); if (!goog.isNull(this.fillState_)) { context.fill(); } diff --git a/src/ol/render/canvas/canvasreplay.js b/src/ol/render/canvas/canvasreplay.js index 1285071ff4..0886db74b4 100644 --- a/src/ol/render/canvas/canvasreplay.js +++ b/src/ol/render/canvas/canvasreplay.js @@ -229,7 +229,8 @@ ol.render.canvas.Replay.prototype.replay_ = function( pixelCoordinates = this.pixelCoordinates_; } else { pixelCoordinates = ol.geom.flat.transform.transform2D( - this.coordinates, 2, transform, this.pixelCoordinates_); + this.coordinates, 0, this.coordinates.length, 2, + transform, this.pixelCoordinates_); goog.vec.Mat4.setFromArray(this.renderedTransform_, transform); goog.asserts.assert(pixelCoordinates === this.pixelCoordinates_); } @@ -1932,7 +1933,8 @@ ol.render.canvas.ReplayGroup.prototype.replay_ = function( var maxX = maxExtent[2]; var maxY = maxExtent[3]; var flatClipCoords = ol.geom.flat.transform.transform2D( - [minX, minY, minX, maxY, maxX, maxY, maxX, minY], 2, transform); + [minX, minY, minX, maxY, maxX, maxY, maxX, minY], + 0, 8, 2, transform); context.save(); context.beginPath(); context.moveTo(flatClipCoords[0], flatClipCoords[1]); From 60c756426872be13fb257bb52298a75ba3a9a308 Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Mon, 7 Jul 2014 15:25:56 +0200 Subject: [PATCH 2/2] Add test case for 3D geometries in immediate API --- test/spec/ol/render/canvasimmediate.test.js | 97 +++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 test/spec/ol/render/canvasimmediate.test.js diff --git a/test/spec/ol/render/canvasimmediate.test.js b/test/spec/ol/render/canvasimmediate.test.js new file mode 100644 index 0000000000..f6046771bf --- /dev/null +++ b/test/spec/ol/render/canvasimmediate.test.js @@ -0,0 +1,97 @@ +goog.provide('ol.test.render.canvas.Immediate'); + +describe('ol.render.canvas.Immediate', function() { + describe('#drawMultiPolygonGeometry', function() { + it('creates the correct canvas instructions for 3D geometries', function() { + var log = { + lineTo: [], + moveTo: [] + }; + // FIXME move the canvas/context mocks outside of here for reuse + var context = { + setLineDash: function() {}, + beginPath: function() {}, + closePath: function() {}, + stroke: function() {}, + lineTo: function(x, y) { + log.lineTo.push([x, y]); + }, + moveTo: function(x, y) { + log.moveTo.push([x, y]); + } + }; + var transform = [0.0004088332670837288, 0, 0, 0, 0, + -0.0004088332670837288, 0, 0, 0, 0, 1, 0, 4480.991370439071, + 1529.5752568707105, 0, 1]; + var extent = [-10960437.252092224, 2762924.0275091752, + -7572748.158493212, 3741317.9895594316]; + var canvas = new ol.render.canvas.Immediate(context, 1, extent, + transform); + canvas.strokeState_ = { + lineCap: 'round', + lineDash: [], + lineJoin: 'round', + lineWidth: 3, + miterLimit: 10, + strokeStyle: '#00FFFF' + }; + var multiPolygonGeometry = new ol.geom.MultiPolygon([ + [[[-80.736061, 28.788576000000006, 0], + [-80.763557, 28.821799999999996, 0], + [-80.817406, 28.895123999999996, 0], + [-80.891304, 29.013130000000004, 0], + [-80.916512, 29.071560000000005, 0], + [-80.899323, 29.061249000000004, 0], + [-80.862663, 28.991361999999995, 0], + [-80.736061, 28.788576000000006, 0]]], [[ + [-82.102127, 26.585724, 0], + [-82.067139, 26.497208, 0], + [-82.097641, 26.493585999999993, 0], + [-82.135895, 26.642279000000002, 0], + [-82.183495, 26.683082999999996, 0], + [-82.128838, 26.693342, 0], + [-82.102127, 26.585724, 0]]] + ]).transform('EPSG:4326', 'EPSG:3857'); + canvas.drawMultiPolygonGeometry(multiPolygonGeometry, null); + expect(log.lineTo.length).to.be(15); + expect(log.lineTo[0][0]).to.roughlyEqual(805.3521540835154, 1e-9); + expect(log.lineTo[0][1]).to.roughlyEqual(158.76358389011807, 1e-9); + expect(log.lineTo[1][0]).to.roughlyEqual(802.9014262612932, 1e-9); + expect(log.lineTo[1][1]).to.roughlyEqual(154.95335187132082, 1e-9); + expect(log.lineTo[2][0]).to.roughlyEqual(799.5382461724039, 1e-9); + expect(log.lineTo[2][1]).to.roughlyEqual(148.815592819916, 1e-9); + expect(log.lineTo[3][0]).to.roughlyEqual(798.3910020835165, 1e-9); + expect(log.lineTo[3][1]).to.roughlyEqual(145.77392230456553, 1e-9); + expect(log.lineTo[4][0]).to.roughlyEqual(799.1732925724045, 1e-9); + expect(log.lineTo[4][1]).to.roughlyEqual(146.31080369865776, 1e-9); + expect(log.lineTo[5][0]).to.roughlyEqual(800.8417299057378, 1e-9); + expect(log.lineTo[5][1]).to.roughlyEqual(149.94832216046188, 1e-9); + expect(log.lineTo[6][0]).to.roughlyEqual(806.6035275946265, 1e-9); + expect(log.lineTo[6][1]).to.roughlyEqual(160.48916296287916, 1e-9); + expect(log.lineTo[7][0]).to.roughlyEqual(806.6035275946265, 1e-9); + expect(log.lineTo[7][1]).to.roughlyEqual(160.48916296287916, 1e-9); + expect(log.lineTo[8][0]).to.roughlyEqual(746.0246888390716, 1e-9); + expect(log.lineTo[8][1]).to.roughlyEqual(278.22094795365365, 1e-9); + expect(log.lineTo[9][0]).to.roughlyEqual(744.6365089279602, 1e-9); + expect(log.lineTo[9][1]).to.roughlyEqual(278.40513424671826, 1e-9); + expect(log.lineTo[10][0]).to.roughlyEqual(742.8955268835157, 1e-9); + expect(log.lineTo[10][1]).to.roughlyEqual(270.83899948444764, 1e-9); + expect(log.lineTo[11][0]).to.roughlyEqual(740.7291979946272, 1e-9); + expect(log.lineTo[11][1]).to.roughlyEqual(268.76099731369345, 1e-9); + expect(log.lineTo[12][0]).to.roughlyEqual(743.2166987946266, 1e-9); + expect(log.lineTo[12][1]).to.roughlyEqual(268.23842607400616, 1e-9); + expect(log.lineTo[13][0]).to.roughlyEqual(744.4323460835158, 1e-9); + expect(log.lineTo[13][1]).to.roughlyEqual(273.7179168205373, 1e-9); + expect(log.lineTo[14][0]).to.roughlyEqual(744.4323460835158, 1e-9); + expect(log.lineTo[14][1]).to.roughlyEqual(273.7179168205373, 1e-9); + expect(log.moveTo.length).to.be(2); + expect(log.moveTo[0][0]).to.roughlyEqual(806.6035275946265, 1e-9); + expect(log.moveTo[0][1]).to.roughlyEqual(160.48916296287916, 1e-9); + expect(log.moveTo[1][0]).to.roughlyEqual(744.4323460835158, 1e-9); + expect(log.moveTo[1][1]).to.roughlyEqual(273.7179168205373, 1e-9); + }); + }); +}); + +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.render.canvas.Immediate');