Merge pull request #8969 from tschaub/inverse-transforms
Create and use inverse pixel transforms during render
This commit is contained in:
@@ -3,7 +3,7 @@ import View from '../src/ol/View.js';
|
|||||||
import TileLayer from '../src/ol/layer/Tile.js';
|
import TileLayer from '../src/ol/layer/Tile.js';
|
||||||
import {fromLonLat} from '../src/ol/proj.js';
|
import {fromLonLat} from '../src/ol/proj.js';
|
||||||
import BingMaps from '../src/ol/source/BingMaps.js';
|
import BingMaps from '../src/ol/source/BingMaps.js';
|
||||||
import {getPixelFromPixel} from '../src/ol/render.js';
|
import {getRenderPixel} from '../src/ol/render.js';
|
||||||
|
|
||||||
const key = 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5';
|
const key = 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5';
|
||||||
|
|
||||||
@@ -51,8 +51,8 @@ container.addEventListener('mouseout', function() {
|
|||||||
// after rendering the layer, show an oversampled version around the pointer
|
// after rendering the layer, show an oversampled version around the pointer
|
||||||
imagery.on('postrender', function(event) {
|
imagery.on('postrender', function(event) {
|
||||||
if (mousePosition) {
|
if (mousePosition) {
|
||||||
const pixel = getPixelFromPixel(event, mousePosition);
|
const pixel = getRenderPixel(event, mousePosition);
|
||||||
const offset = getPixelFromPixel(event, [mousePosition[0] + radius, mousePosition[1]]);
|
const offset = getRenderPixel(event, [mousePosition[0] + radius, mousePosition[1]]);
|
||||||
const half = Math.sqrt(Math.pow(offset[0] - pixel[0], 2) + Math.pow(offset[1] - pixel[1], 2));
|
const half = Math.sqrt(Math.pow(offset[0] - pixel[0], 2) + Math.pow(offset[1] - pixel[1], 2));
|
||||||
const context = event.context;
|
const context = event.context;
|
||||||
const centerX = pixel[0];
|
const centerX = pixel[0];
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {DEVICE_PIXEL_RATIO} from './has.js';
|
|||||||
import {
|
import {
|
||||||
apply as applyTransform,
|
apply as applyTransform,
|
||||||
create as createTransform,
|
create as createTransform,
|
||||||
invert as invertTransform,
|
|
||||||
multiply as multiplyTransform,
|
multiply as multiplyTransform,
|
||||||
scale as scaleTransform
|
scale as scaleTransform
|
||||||
} from './transform.js';
|
} from './transform.js';
|
||||||
@@ -92,22 +91,22 @@ export function toContext(context, opt_options) {
|
|||||||
*/
|
*/
|
||||||
export function getVectorContext(event) {
|
export function getVectorContext(event) {
|
||||||
const frameState = event.frameState;
|
const frameState = event.frameState;
|
||||||
const transform = multiplyTransform(invertTransform(event.pixelTransform.slice()), frameState.coordinateToPixelTransform);
|
const transform = multiplyTransform(event.inversePixelTransform.slice(), frameState.coordinateToPixelTransform);
|
||||||
return new CanvasImmediateRenderer(
|
return new CanvasImmediateRenderer(
|
||||||
event.context, frameState.pixelRatio, frameState.extent, transform,
|
event.context, frameState.pixelRatio, frameState.extent, transform,
|
||||||
frameState.viewState.rotation);
|
frameState.viewState.rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the pixel of the event's canvas context from the map viewport's css pixel
|
* Gets the pixel of the event's canvas context from the map viewport's CSS pixel.
|
||||||
* @param {import("./render/Event.js").default} event Render event.
|
* @param {import("./render/Event.js").default} event Render event.
|
||||||
* @param {import("./pixel.js").Pixel} pixel Css pixel relative to the top-left
|
* @param {import("./pixel.js").Pixel} pixel CSS pixel relative to the top-left
|
||||||
* corner of the map viewport.
|
* corner of the map viewport.
|
||||||
* @returns {import("./pixel.js").Pixel} Pixel on the event's canvas context.
|
* @returns {import("./pixel.js").Pixel} Pixel on the event's canvas context.
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
export function getPixelFromPixel(event, pixel) {
|
export function getRenderPixel(event, pixel) {
|
||||||
const result = pixel.slice(0);
|
const result = pixel.slice(0);
|
||||||
applyTransform(invertTransform(event.pixelTransform.slice()), result);
|
applyTransform(event.inversePixelTransform.slice(), result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,22 +8,23 @@ class RenderEvent extends Event {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("./EventType.js").default} type Type.
|
* @param {import("./EventType.js").default} type Type.
|
||||||
* @param {import("../transform.js").Transform=} opt_pixelTransform Transform.
|
* @param {import("../transform.js").Transform=} opt_inversePixelTransform Transform for
|
||||||
|
* CSS pixels to rendered pixels.
|
||||||
* @param {import("../PluggableMap.js").FrameState=} opt_frameState Frame state.
|
* @param {import("../PluggableMap.js").FrameState=} opt_frameState Frame state.
|
||||||
* @param {?CanvasRenderingContext2D=} opt_context Context.
|
* @param {?CanvasRenderingContext2D=} opt_context Context.
|
||||||
* @param {?import("../webgl/Helper.js").default=} opt_glContext WebGL Context.
|
* @param {?import("../webgl/Helper.js").default=} opt_glContext WebGL Context.
|
||||||
*/
|
*/
|
||||||
constructor(type, opt_pixelTransform, opt_frameState, opt_context, opt_glContext) {
|
constructor(type, opt_inversePixelTransform, opt_frameState, opt_context, opt_glContext) {
|
||||||
|
|
||||||
super(type);
|
super(type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform from css pixels (relative to the top-left corner of the map viewport)
|
* Transform from CSS pixels (relative to the top-left corner of the map viewport)
|
||||||
* to render pixel on this event's `context`.
|
* to rendered pixels on this event's `context`.
|
||||||
* @type {import("../transform.js").Transform|undefined}
|
* @type {import("../transform.js").Transform|undefined}
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
this.pixelTransform = opt_pixelTransform;
|
this.inversePixelTransform = opt_inversePixelTransform;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object representing the current render frame state.
|
* An object representing the current render frame state.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {getWidth} from '../extent.js';
|
|||||||
import {TRUE} from '../functions.js';
|
import {TRUE} from '../functions.js';
|
||||||
import {visibleAtResolution} from '../layer/Layer.js';
|
import {visibleAtResolution} from '../layer/Layer.js';
|
||||||
import {shared as iconImageCache} from '../style/IconImageCache.js';
|
import {shared as iconImageCache} from '../style/IconImageCache.js';
|
||||||
import {compose as composeTransform, invert as invertTransform, setFromArray as transformSetFromArray} from '../transform.js';
|
import {compose as composeTransform, makeInverse} from '../transform.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @abstract
|
* @abstract
|
||||||
@@ -66,8 +66,7 @@ class MapRenderer extends Disposable {
|
|||||||
-viewState.rotation,
|
-viewState.rotation,
|
||||||
-viewState.center[0], -viewState.center[1]);
|
-viewState.center[0], -viewState.center[1]);
|
||||||
|
|
||||||
invertTransform(
|
makeInverse(pixelToCoordinateTransform, coordinateToPixelTransform);
|
||||||
transformSetFromArray(pixelToCoordinateTransform, coordinateToPixelTransform));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import ViewHint from '../../ViewHint.js';
|
|||||||
import {containsExtent, intersects} from '../../extent.js';
|
import {containsExtent, intersects} from '../../extent.js';
|
||||||
import {getIntersection, isEmpty} from '../../extent.js';
|
import {getIntersection, isEmpty} from '../../extent.js';
|
||||||
import CanvasLayerRenderer from './Layer.js';
|
import CanvasLayerRenderer from './Layer.js';
|
||||||
import {compose as composeTransform, toString as transformToString} from '../../transform.js';
|
import {compose as composeTransform, makeInverse, toString as transformToString} from '../../transform.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @classdesc
|
* @classdesc
|
||||||
@@ -93,16 +93,17 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
|
|||||||
width = height = size;
|
width = height = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = this.context;
|
// set forward and inverse pixel transforms
|
||||||
const canvas = context.canvas;
|
composeTransform(this.pixelTransform_,
|
||||||
|
|
||||||
const pixelTransform = composeTransform(this.pixelTransform_,
|
|
||||||
frameState.size[0] / 2, frameState.size[1] / 2,
|
frameState.size[0] / 2, frameState.size[1] / 2,
|
||||||
1 / pixelRatio, 1 / pixelRatio,
|
1 / pixelRatio, 1 / pixelRatio,
|
||||||
rotation,
|
rotation,
|
||||||
-width / 2, -height / 2
|
-width / 2, -height / 2
|
||||||
);
|
);
|
||||||
const canvasTransform = transformToString(pixelTransform);
|
makeInverse(this.inversePixelTransform_, this.pixelTransform_);
|
||||||
|
|
||||||
|
const context = this.context;
|
||||||
|
const canvas = context.canvas;
|
||||||
|
|
||||||
if (canvas.width != width || canvas.height != height) {
|
if (canvas.width != width || canvas.height != height) {
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
@@ -150,6 +151,7 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
|
|||||||
canvas.style.opacity = opacity;
|
canvas.style.opacity = opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const canvasTransform = transformToString(this.pixelTransform_);
|
||||||
if (canvasTransform !== canvas.style.transform) {
|
if (canvasTransform !== canvas.style.transform) {
|
||||||
canvas.style.transform = canvasTransform;
|
canvas.style.transform = canvasTransform;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import RenderEvent from '../../render/Event.js';
|
|||||||
import RenderEventType from '../../render/EventType.js';
|
import RenderEventType from '../../render/EventType.js';
|
||||||
import {rotateAtOffset} from '../../render/canvas.js';
|
import {rotateAtOffset} from '../../render/canvas.js';
|
||||||
import LayerRenderer from '../Layer.js';
|
import LayerRenderer from '../Layer.js';
|
||||||
import {invert as invertTransform, create as createTransform, apply as applyTransform, compose as composeTransform} from '../../transform.js';
|
import {create as createTransform, apply as applyTransform, compose as composeTransform} from '../../transform.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @abstract
|
* @abstract
|
||||||
@@ -28,19 +28,29 @@ class CanvasLayerRenderer extends LayerRenderer {
|
|||||||
this.renderedResolution;
|
this.renderedResolution;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A temporary transform.
|
* A temporary transform. The values in this transform should only be used in a
|
||||||
|
* function that sets the values.
|
||||||
* @private
|
* @private
|
||||||
* @type {import("../../transform.js").Transform}
|
* @type {import("../../transform.js").Transform}
|
||||||
*/
|
*/
|
||||||
this.tempTransform_ = createTransform();
|
this.tempTransform_ = createTransform();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The transform for rendered pixels to viewport CSS pixels.
|
* The transform for rendered pixels to viewport CSS pixels. This transform must
|
||||||
|
* be set when rendering a frame and may be used by other functions after rendering.
|
||||||
* @private
|
* @private
|
||||||
* @type {import("../../transform.js").Transform}
|
* @type {import("../../transform.js").Transform}
|
||||||
*/
|
*/
|
||||||
this.pixelTransform_ = createTransform();
|
this.pixelTransform_ = createTransform();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The transform for viewport CSS pixels to rendered pixels. This transform must
|
||||||
|
* be set when rendering a frame and may be used by other functions after rendering.
|
||||||
|
* @private
|
||||||
|
* @type {import("../../transform.js").Transform}
|
||||||
|
*/
|
||||||
|
this.inversePixelTransform_ = createTransform();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @protected
|
* @protected
|
||||||
* @type {CanvasRenderingContext2D}
|
* @type {CanvasRenderingContext2D}
|
||||||
@@ -102,8 +112,7 @@ class CanvasLayerRenderer extends LayerRenderer {
|
|||||||
applyTransform(frameState.coordinateToPixelTransform, bottomRight);
|
applyTransform(frameState.coordinateToPixelTransform, bottomRight);
|
||||||
applyTransform(frameState.coordinateToPixelTransform, bottomLeft);
|
applyTransform(frameState.coordinateToPixelTransform, bottomLeft);
|
||||||
|
|
||||||
const inverted = invertTransform(this.pixelTransform_.slice());
|
const inverted = this.inversePixelTransform_;
|
||||||
|
|
||||||
applyTransform(inverted, topLeft);
|
applyTransform(inverted, topLeft);
|
||||||
applyTransform(inverted, topRight);
|
applyTransform(inverted, topRight);
|
||||||
applyTransform(inverted, bottomRight);
|
applyTransform(inverted, bottomRight);
|
||||||
@@ -122,46 +131,32 @@ class CanvasLayerRenderer extends LayerRenderer {
|
|||||||
* @param {import("../../render/EventType.js").default} type Event type.
|
* @param {import("../../render/EventType.js").default} type Event type.
|
||||||
* @param {CanvasRenderingContext2D} context Context.
|
* @param {CanvasRenderingContext2D} context Context.
|
||||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
* @param {import("../../transform.js").Transform} pixelTransform Transform.
|
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
dispatchComposeEvent_(type, context, frameState, pixelTransform) {
|
dispatchRenderEvent_(type, context, frameState) {
|
||||||
const layer = this.getLayer();
|
const layer = this.getLayer();
|
||||||
if (layer.hasListener(type)) {
|
if (layer.hasListener(type)) {
|
||||||
const composeEvent = new RenderEvent(type, pixelTransform, frameState,
|
const event = new RenderEvent(type, this.inversePixelTransform_, frameState, context, null);
|
||||||
context, null);
|
layer.dispatchEvent(event);
|
||||||
layer.dispatchEvent(composeEvent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {CanvasRenderingContext2D} context Context.
|
* @param {CanvasRenderingContext2D} context Context.
|
||||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
* @param {import("../../transform.js").Transform=} opt_transform Transform.
|
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
preRender(context, frameState, opt_transform) {
|
preRender(context, frameState) {
|
||||||
this.dispatchComposeEvent_(RenderEventType.PRERENDER, context, frameState, opt_transform);
|
this.dispatchRenderEvent_(RenderEventType.PRERENDER, context, frameState);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {CanvasRenderingContext2D} context Context.
|
* @param {CanvasRenderingContext2D} context Context.
|
||||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
* @param {import("../../transform.js").Transform=} opt_transform Transform.
|
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
postRender(context, frameState, opt_transform) {
|
postRender(context, frameState) {
|
||||||
this.dispatchComposeEvent_(RenderEventType.POSTRENDER, context, frameState, opt_transform);
|
this.dispatchRenderEvent_(RenderEventType.POSTRENDER, context, frameState);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {CanvasRenderingContext2D} context Context.
|
|
||||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
|
||||||
* @param {import("../../transform.js").Transform=} opt_transform Transform.
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
dispatchRenderEvent(context, frameState, opt_transform) {
|
|
||||||
this.dispatchComposeEvent_(RenderEventType.RENDER, context, frameState, opt_transform);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -194,7 +189,7 @@ class CanvasLayerRenderer extends LayerRenderer {
|
|||||||
* returned, and empty array will be returned.
|
* returned, and empty array will be returned.
|
||||||
*/
|
*/
|
||||||
getDataAtPixel(pixel, frameState, hitTolerance) {
|
getDataAtPixel(pixel, frameState, hitTolerance) {
|
||||||
const renderPixel = applyTransform(invertTransform(this.pixelTransform_.slice()), pixel.slice());
|
const renderPixel = applyTransform(this.inversePixelTransform_, pixel.slice());
|
||||||
const context = this.context;
|
const context = this.context;
|
||||||
|
|
||||||
let data;
|
let data;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import TileRange from '../../TileRange.js';
|
|||||||
import TileState from '../../TileState.js';
|
import TileState from '../../TileState.js';
|
||||||
import {createEmpty, getIntersection, getTopLeft} from '../../extent.js';
|
import {createEmpty, getIntersection, getTopLeft} from '../../extent.js';
|
||||||
import CanvasLayerRenderer from './Layer.js';
|
import CanvasLayerRenderer from './Layer.js';
|
||||||
import {compose as composeTransform, toString as transformToString} from '../../transform.js';
|
import {compose as composeTransform, makeInverse, toString as transformToString} from '../../transform.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @classdesc
|
* @classdesc
|
||||||
@@ -222,15 +222,18 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const canvas = context.canvas;
|
const canvas = context.canvas;
|
||||||
const canvasScale = tileResolution / frameState.viewState.resolution / tilePixelRatio;
|
const canvasScale = tileResolution / frameState.viewState.resolution / tilePixelRatio;
|
||||||
const pixelTransform = composeTransform(this.pixelTransform_,
|
|
||||||
|
// set forward and inverse pixel transforms
|
||||||
|
composeTransform(this.pixelTransform_,
|
||||||
frameState.size[0] / 2, frameState.size[1] / 2,
|
frameState.size[0] / 2, frameState.size[1] / 2,
|
||||||
canvasScale, canvasScale,
|
canvasScale, canvasScale,
|
||||||
rotation,
|
rotation,
|
||||||
-width / 2, -height / 2
|
-width / 2, -height / 2
|
||||||
);
|
);
|
||||||
const canvasTransform = transformToString(pixelTransform);
|
makeInverse(this.inversePixelTransform_, this.pixelTransform_);
|
||||||
|
|
||||||
if (canvas.width != width || canvas.height != height) {
|
if (canvas.width != width || canvas.height != height) {
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
@@ -243,7 +246,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
|||||||
this.clipUnrotated(context, frameState, layerState.extent);
|
this.clipUnrotated(context, frameState, layerState.extent);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.preRender(context, frameState, pixelTransform);
|
this.preRender(context, frameState);
|
||||||
|
|
||||||
this.renderedTiles.length = 0;
|
this.renderedTiles.length = 0;
|
||||||
/** @type {Array<number>} */
|
/** @type {Array<number>} */
|
||||||
@@ -291,7 +294,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
|||||||
projection, extent, z, tileLayer.getPreload());
|
projection, extent, z, tileLayer.getPreload());
|
||||||
this.scheduleExpireCache(frameState, tileSource);
|
this.scheduleExpireCache(frameState, tileSource);
|
||||||
|
|
||||||
this.postRender(context, frameState, pixelTransform);
|
this.postRender(context, frameState);
|
||||||
|
|
||||||
if (layerState.extent) {
|
if (layerState.extent) {
|
||||||
context.restore();
|
context.restore();
|
||||||
@@ -302,6 +305,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
|||||||
canvas.style.opacity = opacity;
|
canvas.style.opacity = opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const canvasTransform = transformToString(this.pixelTransform_);
|
||||||
if (canvasTransform !== canvas.style.transform) {
|
if (canvasTransform !== canvas.style.transform) {
|
||||||
canvas.style.transform = canvasTransform;
|
canvas.style.transform = canvasTransform;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,12 @@ import {listen, unlisten} from '../../events.js';
|
|||||||
import EventType from '../../events/EventType.js';
|
import EventType from '../../events/EventType.js';
|
||||||
import rbush from 'rbush';
|
import rbush from 'rbush';
|
||||||
import {buffer, createEmpty, containsExtent, getWidth} from '../../extent.js';
|
import {buffer, createEmpty, containsExtent, getWidth} from '../../extent.js';
|
||||||
import RenderEventType from '../../render/EventType.js';
|
|
||||||
import {labelCache} from '../../render/canvas.js';
|
import {labelCache} from '../../render/canvas.js';
|
||||||
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
|
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
|
||||||
import InstructionsGroupExecutor from '../../render/canvas/ExecutorGroup.js';
|
import InstructionsGroupExecutor from '../../render/canvas/ExecutorGroup.js';
|
||||||
import CanvasLayerRenderer from './Layer.js';
|
import CanvasLayerRenderer from './Layer.js';
|
||||||
import {defaultOrder as defaultRenderOrder, getTolerance as getRenderTolerance, getSquaredTolerance as getSquaredRenderTolerance, renderFeature} from '../vector.js';
|
import {defaultOrder as defaultRenderOrder, getTolerance as getRenderTolerance, getSquaredTolerance as getSquaredRenderTolerance, renderFeature} from '../vector.js';
|
||||||
import {toString as transformToString} from '../../transform.js';
|
import {toString as transformToString, makeScale, makeInverse} from '../../transform.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @classdesc
|
* @classdesc
|
||||||
@@ -105,8 +104,9 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
|
|
||||||
const pixelRatio = frameState.pixelRatio;
|
const pixelRatio = frameState.pixelRatio;
|
||||||
|
|
||||||
// a scale transform (we could add a createScale function in ol/transform)
|
// set forward and inverse pixel transforms
|
||||||
const pixelTransform = [1 / pixelRatio, 0, 0, 1 / pixelRatio, 0, 0];
|
makeScale(this.pixelTransform_, 1 / pixelRatio, 1 / pixelRatio);
|
||||||
|
makeInverse(this.inversePixelTransform_, this.pixelTransform_);
|
||||||
|
|
||||||
// resize and clear
|
// resize and clear
|
||||||
const width = Math.round(frameState.size[0] * pixelRatio);
|
const width = Math.round(frameState.size[0] * pixelRatio);
|
||||||
@@ -114,7 +114,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
if (canvas.width != width || canvas.height != height) {
|
if (canvas.width != width || canvas.height != height) {
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
const canvasTransform = transformToString(pixelTransform);
|
const canvasTransform = transformToString(this.pixelTransform_);
|
||||||
if (canvas.style.transform !== canvasTransform) {
|
if (canvas.style.transform !== canvasTransform) {
|
||||||
canvas.style.transform = canvasTransform;
|
canvas.style.transform = canvasTransform;
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
context.clearRect(0, 0, width, height);
|
context.clearRect(0, 0, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.preRender(context, frameState, pixelTransform);
|
this.preRender(context, frameState);
|
||||||
|
|
||||||
const extent = frameState.extent;
|
const extent = frameState.extent;
|
||||||
const viewState = frameState.viewState;
|
const viewState = frameState.viewState;
|
||||||
@@ -173,15 +173,11 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.getLayer().hasListener(RenderEventType.RENDER)) {
|
|
||||||
this.dispatchRenderEvent(context, frameState, pixelTransform);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clipped) {
|
if (clipped) {
|
||||||
context.restore();
|
context.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.postRender(context, frameState, pixelTransform);
|
this.postRender(context, frameState);
|
||||||
|
|
||||||
const opacity = layerState.opacity;
|
const opacity = layerState.opacity;
|
||||||
if (opacity !== canvas.style.opacity) {
|
if (opacity !== canvas.style.opacity) {
|
||||||
|
|||||||
@@ -22,11 +22,12 @@ import {
|
|||||||
apply as applyTransform,
|
apply as applyTransform,
|
||||||
create as createTransform,
|
create as createTransform,
|
||||||
compose as composeTransform,
|
compose as composeTransform,
|
||||||
invert as invertTransform,
|
|
||||||
reset as resetTransform,
|
reset as resetTransform,
|
||||||
scale as scaleTransform,
|
scale as scaleTransform,
|
||||||
translate as translateTransform,
|
translate as translateTransform,
|
||||||
toString as transformToString
|
toString as transformToString,
|
||||||
|
makeScale,
|
||||||
|
makeInverse
|
||||||
} from '../../transform.js';
|
} from '../../transform.js';
|
||||||
import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
|
import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
|
||||||
|
|
||||||
@@ -98,6 +99,13 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
*/
|
*/
|
||||||
this.overlayPixelTransform_ = createTransform();
|
this.overlayPixelTransform_ = createTransform();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The transform for viewport CSS pixels to rendered pixels for the overlay canvas.
|
||||||
|
* @private
|
||||||
|
* @type {import("../../transform.js").Transform}
|
||||||
|
*/
|
||||||
|
this.inverseOverlayPixelTransform_ = createTransform();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declutter tree.
|
* Declutter tree.
|
||||||
* @private
|
* @private
|
||||||
@@ -384,12 +392,14 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
const rotation = frameState.viewState.rotation;
|
const rotation = frameState.viewState.rotation;
|
||||||
const size = frameState.size;
|
const size = frameState.size;
|
||||||
|
|
||||||
|
// set forward and inverse pixel transforms
|
||||||
|
makeScale(this.overlayPixelTransform_, 1 / pixelRatio, 1 / pixelRatio);
|
||||||
|
makeInverse(this.inverseOverlayPixelTransform_, this.overlayPixelTransform_);
|
||||||
|
|
||||||
// resize and clear
|
// resize and clear
|
||||||
const canvas = context.canvas;
|
const canvas = context.canvas;
|
||||||
const width = Math.round(size[0] * pixelRatio);
|
const width = Math.round(size[0] * pixelRatio);
|
||||||
const height = Math.round(size[1] * pixelRatio);
|
const height = Math.round(size[1] * pixelRatio);
|
||||||
this.overlayPixelTransform_[0] = 1 / pixelRatio;
|
|
||||||
this.overlayPixelTransform_[3] = 1 / pixelRatio;
|
|
||||||
if (canvas.width != width || canvas.height != height) {
|
if (canvas.width != width || canvas.height != height) {
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
@@ -544,7 +554,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderPixel = applyTransform(invertTransform(this.overlayPixelTransform_.slice()), pixel.slice());
|
const renderPixel = applyTransform(this.inverseOverlayPixelTransform_, pixel.slice());
|
||||||
const context = this.overlayContext_;
|
const context = this.overlayContext_;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -162,6 +162,16 @@ export function scale(transform, x, y) {
|
|||||||
return multiply(transform, set(tmp_, x, 0, 0, y, 0, 0));
|
return multiply(transform, set(tmp_, x, 0, 0, y, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a scale transform.
|
||||||
|
* @param {!Transform} target Transform to overwrite.
|
||||||
|
* @param {number} x Scale factor x.
|
||||||
|
* @param {number} y Scale factor y.
|
||||||
|
* @return {!Transform} The scale transform.
|
||||||
|
*/
|
||||||
|
export function makeScale(target, x, y) {
|
||||||
|
return set(target, x, 0, 0, y, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies translation to the given transform.
|
* Applies translation to the given transform.
|
||||||
@@ -203,30 +213,40 @@ export function compose(transform, dx1, dy1, sx, sy, angle, dx2, dy2) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Invert the given transform.
|
* Invert the given transform.
|
||||||
* @param {!Transform} transform Transform.
|
* @param {!Transform} source The source transform to invert.
|
||||||
* @return {!Transform} Inverse of the transform.
|
* @return {!Transform} The inverted (source) transform.
|
||||||
*/
|
*/
|
||||||
export function invert(transform) {
|
export function invert(source) {
|
||||||
const det = determinant(transform);
|
return makeInverse(source, source);
|
||||||
assert(det !== 0, 32); // Transformation matrix cannot be inverted
|
|
||||||
|
|
||||||
const a = transform[0];
|
|
||||||
const b = transform[1];
|
|
||||||
const c = transform[2];
|
|
||||||
const d = transform[3];
|
|
||||||
const e = transform[4];
|
|
||||||
const f = transform[5];
|
|
||||||
|
|
||||||
transform[0] = d / det;
|
|
||||||
transform[1] = -b / det;
|
|
||||||
transform[2] = -c / det;
|
|
||||||
transform[3] = a / det;
|
|
||||||
transform[4] = (c * f - d * e) / det;
|
|
||||||
transform[5] = -(a * f - b * e) / det;
|
|
||||||
|
|
||||||
return transform;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invert the given transform.
|
||||||
|
* @param {!Transform} target Transform to be set as the inverse of
|
||||||
|
* the source transform.
|
||||||
|
* @param {!Transform} source The source transform to invert.
|
||||||
|
* @return {!Transform} The inverted (target) transform.
|
||||||
|
*/
|
||||||
|
export function makeInverse(target, source) {
|
||||||
|
const det = determinant(source);
|
||||||
|
assert(det !== 0, 32); // Transformation matrix cannot be inverted
|
||||||
|
|
||||||
|
const a = source[0];
|
||||||
|
const b = source[1];
|
||||||
|
const c = source[2];
|
||||||
|
const d = source[3];
|
||||||
|
const e = source[4];
|
||||||
|
const f = source[5];
|
||||||
|
|
||||||
|
target[0] = d / det;
|
||||||
|
target[1] = -b / det;
|
||||||
|
target[2] = -c / det;
|
||||||
|
target[3] = a / det;
|
||||||
|
target[4] = (c * f - d * e) / det;
|
||||||
|
target[5] = -(a * f - b * e) / det;
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the determinant of the given matrix.
|
* Returns the determinant of the given matrix.
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ import {
|
|||||||
setFromArray,
|
setFromArray,
|
||||||
translate,
|
translate,
|
||||||
scale,
|
scale,
|
||||||
|
makeScale,
|
||||||
rotate,
|
rotate,
|
||||||
multiply,
|
multiply,
|
||||||
compose,
|
compose,
|
||||||
invert,
|
invert,
|
||||||
|
makeInverse,
|
||||||
apply
|
apply
|
||||||
} from '../../../src/ol/transform.js';
|
} from '../../../src/ol/transform.js';
|
||||||
|
|
||||||
@@ -68,6 +70,20 @@ describe('ol.transform', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('makeScale()', function() {
|
||||||
|
it('creates a scale transform', function() {
|
||||||
|
const target = create();
|
||||||
|
makeScale(target, 2, 3);
|
||||||
|
expect(target).to.eql([2, 0, 0, 3, 0, 0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the target', function() {
|
||||||
|
const target = create();
|
||||||
|
const transform = makeScale(target, 2, 3);
|
||||||
|
expect(transform).to.be(target);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('rotate()', function() {
|
describe('rotate()', function() {
|
||||||
it('applies rotation to a transform', function() {
|
it('applies rotation to a transform', function() {
|
||||||
const transform = create();
|
const transform = create();
|
||||||
@@ -110,13 +126,39 @@ describe('ol.transform', function() {
|
|||||||
|
|
||||||
describe('invert()', function() {
|
describe('invert()', function() {
|
||||||
it('inverts a transform', function() {
|
it('inverts a transform', function() {
|
||||||
let transform = [1, 0, 1, 0, 1, 0];
|
const transform = [1, 1, 1, 2, 2, 0];
|
||||||
expect(function() {
|
|
||||||
invert(transform);
|
|
||||||
}).to.throwException();
|
|
||||||
transform = [1, 1, 1, 2, 2, 0];
|
|
||||||
expect(invert(transform)).to.eql([2, -1, -1, 1, -4, 2]);
|
expect(invert(transform)).to.eql([2, -1, -1, 1, -4, 2]);
|
||||||
expect(transform).to.eql([2, -1, -1, 1, -4, 2]);
|
});
|
||||||
|
|
||||||
|
it('throws if the transform cannot be inverted', function() {
|
||||||
|
const indeterminant = [1, 0, 1, 0, 1, 0];
|
||||||
|
expect(function() {
|
||||||
|
invert(indeterminant);
|
||||||
|
}).to.throwException();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('modifies the source', function() {
|
||||||
|
const source = [1, 1, 1, 2, 2, 0];
|
||||||
|
const inverted = invert(source);
|
||||||
|
expect(inverted).to.eql([2, -1, -1, 1, -4, 2]);
|
||||||
|
expect(source).to.be(inverted);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('makeInverse()', function() {
|
||||||
|
it('makes the target the inverse of the source', function() {
|
||||||
|
const source = [1, 1, 1, 2, 2, 0];
|
||||||
|
const target = [1, 0, 0, 1, 0, 0];
|
||||||
|
makeInverse(target, source);
|
||||||
|
expect(source).to.eql([1, 1, 1, 2, 2, 0]);
|
||||||
|
expect(target).to.eql([2, -1, -1, 1, -4, 2]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the target', function() {
|
||||||
|
const source = [1, 1, 1, 2, 2, 0];
|
||||||
|
const target = [1, 0, 0, 1, 0, 0];
|
||||||
|
const inverted = makeInverse(target, source);
|
||||||
|
expect(target).to.be(inverted);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user