From de9f6e2dc5d0ee4e77bd3f252fb01669fbb9dddd Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 25 Apr 2022 10:28:40 -0600 Subject: [PATCH] Handle rotation with non-square tiles --- src/ol/renderer/webgl/TileLayer.js | 36 ++++++---- .../cases/webgl-tile-non-square/expected.png | Bin 0 -> 4695 bytes .../cases/webgl-tile-non-square/main.js | 62 ++++++++++++++++++ 3 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 test/rendering/cases/webgl-tile-non-square/expected.png create mode 100644 test/rendering/cases/webgl-tile-non-square/main.js diff --git a/src/ol/renderer/webgl/TileLayer.js b/src/ol/renderer/webgl/TileLayer.js index 565496d06e..3b16543cc5 100644 --- a/src/ol/renderer/webgl/TileLayer.js +++ b/src/ol/renderer/webgl/TileLayer.js @@ -12,8 +12,11 @@ import {AttributeType} from '../../webgl/Helper.js'; import {ELEMENT_ARRAY_BUFFER, STATIC_DRAW} from '../../webgl.js'; import { apply as applyTransform, - compose as composeTransform, create as createTransform, + reset as resetTransform, + rotate as rotateTransform, + scale as scaleTransform, + translate as translateTransform, } from '../../transform.js'; import {containsCoordinate, getIntersection, isEmpty} from '../../extent.js'; import { @@ -519,6 +522,10 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { const tileSize = toSize(tileGrid.getTileSize(tileZ), this.tempSize_); const tileOrigin = tileGrid.getOrigin(tileZ); + const tileWidthWithGutter = tileSize[0] + 2 * gutter; + const tileHeightWithGutter = tileSize[1] + 2 * gutter; + const aspectRatio = tileWidthWithGutter / tileHeightWithGutter; + const centerI = (centerX - tileOrigin[0]) / (tileSize[0] * tileResolution); const centerJ = @@ -540,17 +547,20 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { const tileCenterI = tileCoord[1]; const tileCenterJ = tileCoord[2]; - composeTransform( + resetTransform(this.tileTransform_); + scaleTransform( this.tileTransform_, - 0, - 0, - 2 / ((frameState.size[0] * tileScale) / (tileSize[0] + 2 * gutter)), - -2 / ((frameState.size[1] * tileScale) / (tileSize[1] + 2 * gutter)), - viewState.rotation, - ((tileCenterI - centerI - gutter / tileSize[0]) * tileSize[0]) / - (tileSize[0] + 2 * gutter), - ((tileCenterJ - centerJ - gutter / tileSize[1]) * tileSize[1]) / - (tileSize[1] + 2 * gutter) + 2 / ((frameState.size[0] * tileScale) / tileWidthWithGutter), + -2 / ((frameState.size[1] * tileScale) / tileWidthWithGutter) + ); + rotateTransform(this.tileTransform_, viewState.rotation); + scaleTransform(this.tileTransform_, 1, 1 / aspectRatio); + translateTransform( + this.tileTransform_, + (tileSize[0] * (tileCenterI - centerI) - gutter) / + tileWidthWithGutter, + (tileSize[1] * (tileCenterJ - centerJ) - gutter) / + tileHeightWithGutter ); this.helper.setUniformMatrixValue( @@ -602,11 +612,11 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { this.helper.setUniformFloatValue(Uniforms.DEPTH, depth); this.helper.setUniformFloatValue( Uniforms.TEXTURE_PIXEL_WIDTH, - tileSize[0] + 2 * gutter + tileWidthWithGutter ); this.helper.setUniformFloatValue( Uniforms.TEXTURE_PIXEL_HEIGHT, - tileSize[1] + 2 * gutter + tileHeightWithGutter ); this.helper.setUniformFloatValue( Uniforms.TEXTURE_RESOLUTION, diff --git a/test/rendering/cases/webgl-tile-non-square/expected.png b/test/rendering/cases/webgl-tile-non-square/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..a2e0583442940e9bc2cb978736342d0b391e3fe2 GIT binary patch literal 4695 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 Nuwv{pFWc>r_&@m;5a$2@ literal 0 HcmV?d00001 diff --git a/test/rendering/cases/webgl-tile-non-square/main.js b/test/rendering/cases/webgl-tile-non-square/main.js new file mode 100644 index 0000000000..9839da3018 --- /dev/null +++ b/test/rendering/cases/webgl-tile-non-square/main.js @@ -0,0 +1,62 @@ +import DataTile from '../../../../src/ol/source/DataTile.js'; +import Map from '../../../../src/ol/Map.js'; +import Projection from '../../../../src/ol/proj/Projection.js'; +import TileLayer from '../../../../src/ol/layer/WebGLTile.js'; +import View from '../../../../src/ol/View.js'; + +const extent = [-1000, -500, 1000, 500]; +const projection = new Projection({ + code: 'test', + units: 'pixels', + extent: extent, +}); + +const width = 200; +const height = 100; + +const canvas = document.createElement('canvas'); +canvas.width = width; +canvas.height = height; + +const context = canvas.getContext('2d'); +context.strokeStyle = 'red'; +context.textAlign = 'center'; +context.font = '16px sans-serif'; +const lineHeight = 20; + +new Map({ + target: 'map', + layers: [ + new TileLayer({ + source: new DataTile({ + projection: projection, + tileSize: [width, height], + loader: function (z, x, y) { + const halfWidth = width / 2; + const halfHeight = height / 2; + context.clearRect(0, 0, width, height); + context.fillStyle = 'rgba(100, 100, 100, 0.5)'; + context.fillRect(0, 0, width, height); + context.fillStyle = 'black'; + context.fillText(`z: ${z}`, halfWidth, halfHeight - lineHeight); + context.fillText(`x: ${x}`, halfWidth, halfHeight); + context.fillText(`y: ${y}`, halfWidth, halfHeight + lineHeight); + context.strokeRect(0, 0, width, height); + return context.getImageData(0, 0, width, height).data; + }, + transition: 0, + }), + }), + ], + view: new View({ + projection: projection, + showFullExtent: true, + center: [0, 0], + rotation: Math.PI / 4, + zoom: 0, + }), +}); + +render({ + message: 'properly renders rotated non-square tiles', +});