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 {fromLonLat} from '../src/ol/proj.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';
|
||||
|
||||
@@ -51,8 +51,8 @@ container.addEventListener('mouseout', function() {
|
||||
// after rendering the layer, show an oversampled version around the pointer
|
||||
imagery.on('postrender', function(event) {
|
||||
if (mousePosition) {
|
||||
const pixel = getPixelFromPixel(event, mousePosition);
|
||||
const offset = getPixelFromPixel(event, [mousePosition[0] + radius, mousePosition[1]]);
|
||||
const pixel = getRenderPixel(event, mousePosition);
|
||||
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 context = event.context;
|
||||
const centerX = pixel[0];
|
||||
|
||||
@@ -5,7 +5,6 @@ import {DEVICE_PIXEL_RATIO} from './has.js';
|
||||
import {
|
||||
apply as applyTransform,
|
||||
create as createTransform,
|
||||
invert as invertTransform,
|
||||
multiply as multiplyTransform,
|
||||
scale as scaleTransform
|
||||
} from './transform.js';
|
||||
@@ -92,22 +91,22 @@ export function toContext(context, opt_options) {
|
||||
*/
|
||||
export function getVectorContext(event) {
|
||||
const frameState = event.frameState;
|
||||
const transform = multiplyTransform(invertTransform(event.pixelTransform.slice()), frameState.coordinateToPixelTransform);
|
||||
const transform = multiplyTransform(event.inversePixelTransform.slice(), frameState.coordinateToPixelTransform);
|
||||
return new CanvasImmediateRenderer(
|
||||
event.context, frameState.pixelRatio, frameState.extent, transform,
|
||||
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("./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.
|
||||
* @returns {import("./pixel.js").Pixel} Pixel on the event's canvas context.
|
||||
* @api
|
||||
*/
|
||||
export function getPixelFromPixel(event, pixel) {
|
||||
export function getRenderPixel(event, pixel) {
|
||||
const result = pixel.slice(0);
|
||||
applyTransform(invertTransform(event.pixelTransform.slice()), result);
|
||||
applyTransform(event.inversePixelTransform.slice(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -8,22 +8,23 @@ class RenderEvent extends Event {
|
||||
|
||||
/**
|
||||
* @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 {?CanvasRenderingContext2D=} opt_context 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);
|
||||
|
||||
/**
|
||||
* Transform from css pixels (relative to the top-left corner of the map viewport)
|
||||
* to render pixel on this event's `context`.
|
||||
* Transform from CSS pixels (relative to the top-left corner of the map viewport)
|
||||
* to rendered pixels on this event's `context`.
|
||||
* @type {import("../transform.js").Transform|undefined}
|
||||
* @api
|
||||
*/
|
||||
this.pixelTransform = opt_pixelTransform;
|
||||
this.inversePixelTransform = opt_inversePixelTransform;
|
||||
|
||||
/**
|
||||
* An object representing the current render frame state.
|
||||
|
||||
@@ -9,7 +9,7 @@ import {getWidth} from '../extent.js';
|
||||
import {TRUE} from '../functions.js';
|
||||
import {visibleAtResolution} from '../layer/Layer.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
|
||||
@@ -66,8 +66,7 @@ class MapRenderer extends Disposable {
|
||||
-viewState.rotation,
|
||||
-viewState.center[0], -viewState.center[1]);
|
||||
|
||||
invertTransform(
|
||||
transformSetFromArray(pixelToCoordinateTransform, coordinateToPixelTransform));
|
||||
makeInverse(pixelToCoordinateTransform, coordinateToPixelTransform);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@ import ViewHint from '../../ViewHint.js';
|
||||
import {containsExtent, intersects} from '../../extent.js';
|
||||
import {getIntersection, isEmpty} from '../../extent.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
|
||||
@@ -93,16 +93,17 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
|
||||
width = height = size;
|
||||
}
|
||||
|
||||
const context = this.context;
|
||||
const canvas = context.canvas;
|
||||
|
||||
const pixelTransform = composeTransform(this.pixelTransform_,
|
||||
// set forward and inverse pixel transforms
|
||||
composeTransform(this.pixelTransform_,
|
||||
frameState.size[0] / 2, frameState.size[1] / 2,
|
||||
1 / pixelRatio, 1 / pixelRatio,
|
||||
rotation,
|
||||
-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) {
|
||||
canvas.width = width;
|
||||
@@ -150,6 +151,7 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
|
||||
canvas.style.opacity = opacity;
|
||||
}
|
||||
|
||||
const canvasTransform = transformToString(this.pixelTransform_);
|
||||
if (canvasTransform !== canvas.style.transform) {
|
||||
canvas.style.transform = canvasTransform;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import RenderEvent from '../../render/Event.js';
|
||||
import RenderEventType from '../../render/EventType.js';
|
||||
import {rotateAtOffset} from '../../render/canvas.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
|
||||
@@ -28,19 +28,29 @@ class CanvasLayerRenderer extends LayerRenderer {
|
||||
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
|
||||
* @type {import("../../transform.js").Transform}
|
||||
*/
|
||||
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
|
||||
* @type {import("../../transform.js").Transform}
|
||||
*/
|
||||
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
|
||||
* @type {CanvasRenderingContext2D}
|
||||
@@ -102,8 +112,7 @@ class CanvasLayerRenderer extends LayerRenderer {
|
||||
applyTransform(frameState.coordinateToPixelTransform, bottomRight);
|
||||
applyTransform(frameState.coordinateToPixelTransform, bottomLeft);
|
||||
|
||||
const inverted = invertTransform(this.pixelTransform_.slice());
|
||||
|
||||
const inverted = this.inversePixelTransform_;
|
||||
applyTransform(inverted, topLeft);
|
||||
applyTransform(inverted, topRight);
|
||||
applyTransform(inverted, bottomRight);
|
||||
@@ -122,46 +131,32 @@ class CanvasLayerRenderer extends LayerRenderer {
|
||||
* @param {import("../../render/EventType.js").default} type Event type.
|
||||
* @param {CanvasRenderingContext2D} context Context.
|
||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||
* @param {import("../../transform.js").Transform} pixelTransform Transform.
|
||||
* @private
|
||||
*/
|
||||
dispatchComposeEvent_(type, context, frameState, pixelTransform) {
|
||||
dispatchRenderEvent_(type, context, frameState) {
|
||||
const layer = this.getLayer();
|
||||
if (layer.hasListener(type)) {
|
||||
const composeEvent = new RenderEvent(type, pixelTransform, frameState,
|
||||
context, null);
|
||||
layer.dispatchEvent(composeEvent);
|
||||
const event = new RenderEvent(type, this.inversePixelTransform_, frameState, context, null);
|
||||
layer.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CanvasRenderingContext2D} context Context.
|
||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||
* @param {import("../../transform.js").Transform=} opt_transform Transform.
|
||||
* @protected
|
||||
*/
|
||||
preRender(context, frameState, opt_transform) {
|
||||
this.dispatchComposeEvent_(RenderEventType.PRERENDER, context, frameState, opt_transform);
|
||||
preRender(context, frameState) {
|
||||
this.dispatchRenderEvent_(RenderEventType.PRERENDER, context, frameState);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CanvasRenderingContext2D} context Context.
|
||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||
* @param {import("../../transform.js").Transform=} opt_transform Transform.
|
||||
* @protected
|
||||
*/
|
||||
postRender(context, frameState, opt_transform) {
|
||||
this.dispatchComposeEvent_(RenderEventType.POSTRENDER, context, frameState, opt_transform);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
postRender(context, frameState) {
|
||||
this.dispatchRenderEvent_(RenderEventType.POSTRENDER, context, frameState);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,7 +189,7 @@ class CanvasLayerRenderer extends LayerRenderer {
|
||||
* returned, and empty array will be returned.
|
||||
*/
|
||||
getDataAtPixel(pixel, frameState, hitTolerance) {
|
||||
const renderPixel = applyTransform(invertTransform(this.pixelTransform_.slice()), pixel.slice());
|
||||
const renderPixel = applyTransform(this.inversePixelTransform_, pixel.slice());
|
||||
const context = this.context;
|
||||
|
||||
let data;
|
||||
|
||||
@@ -6,7 +6,7 @@ import TileRange from '../../TileRange.js';
|
||||
import TileState from '../../TileState.js';
|
||||
import {createEmpty, getIntersection, getTopLeft} from '../../extent.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
|
||||
@@ -222,15 +222,18 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const canvas = context.canvas;
|
||||
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,
|
||||
canvasScale, canvasScale,
|
||||
rotation,
|
||||
-width / 2, -height / 2
|
||||
);
|
||||
const canvasTransform = transformToString(pixelTransform);
|
||||
makeInverse(this.inversePixelTransform_, this.pixelTransform_);
|
||||
|
||||
if (canvas.width != width || canvas.height != height) {
|
||||
canvas.width = width;
|
||||
@@ -243,7 +246,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
this.clipUnrotated(context, frameState, layerState.extent);
|
||||
}
|
||||
|
||||
this.preRender(context, frameState, pixelTransform);
|
||||
this.preRender(context, frameState);
|
||||
|
||||
this.renderedTiles.length = 0;
|
||||
/** @type {Array<number>} */
|
||||
@@ -291,7 +294,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
projection, extent, z, tileLayer.getPreload());
|
||||
this.scheduleExpireCache(frameState, tileSource);
|
||||
|
||||
this.postRender(context, frameState, pixelTransform);
|
||||
this.postRender(context, frameState);
|
||||
|
||||
if (layerState.extent) {
|
||||
context.restore();
|
||||
@@ -302,6 +305,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
canvas.style.opacity = opacity;
|
||||
}
|
||||
|
||||
const canvasTransform = transformToString(this.pixelTransform_);
|
||||
if (canvasTransform !== canvas.style.transform) {
|
||||
canvas.style.transform = canvasTransform;
|
||||
}
|
||||
|
||||
@@ -7,13 +7,12 @@ import {listen, unlisten} from '../../events.js';
|
||||
import EventType from '../../events/EventType.js';
|
||||
import rbush from 'rbush';
|
||||
import {buffer, createEmpty, containsExtent, getWidth} from '../../extent.js';
|
||||
import RenderEventType from '../../render/EventType.js';
|
||||
import {labelCache} from '../../render/canvas.js';
|
||||
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
|
||||
import InstructionsGroupExecutor from '../../render/canvas/ExecutorGroup.js';
|
||||
import CanvasLayerRenderer from './Layer.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
|
||||
@@ -105,8 +104,9 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
|
||||
const pixelRatio = frameState.pixelRatio;
|
||||
|
||||
// a scale transform (we could add a createScale function in ol/transform)
|
||||
const pixelTransform = [1 / pixelRatio, 0, 0, 1 / pixelRatio, 0, 0];
|
||||
// set forward and inverse pixel transforms
|
||||
makeScale(this.pixelTransform_, 1 / pixelRatio, 1 / pixelRatio);
|
||||
makeInverse(this.inversePixelTransform_, this.pixelTransform_);
|
||||
|
||||
// resize and clear
|
||||
const width = Math.round(frameState.size[0] * pixelRatio);
|
||||
@@ -114,7 +114,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
if (canvas.width != width || canvas.height != height) {
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const canvasTransform = transformToString(pixelTransform);
|
||||
const canvasTransform = transformToString(this.pixelTransform_);
|
||||
if (canvas.style.transform !== canvasTransform) {
|
||||
canvas.style.transform = canvasTransform;
|
||||
}
|
||||
@@ -122,7 +122,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
context.clearRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
this.preRender(context, frameState, pixelTransform);
|
||||
this.preRender(context, frameState);
|
||||
|
||||
const extent = frameState.extent;
|
||||
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) {
|
||||
context.restore();
|
||||
}
|
||||
|
||||
this.postRender(context, frameState, pixelTransform);
|
||||
this.postRender(context, frameState);
|
||||
|
||||
const opacity = layerState.opacity;
|
||||
if (opacity !== canvas.style.opacity) {
|
||||
|
||||
@@ -22,11 +22,12 @@ import {
|
||||
apply as applyTransform,
|
||||
create as createTransform,
|
||||
compose as composeTransform,
|
||||
invert as invertTransform,
|
||||
reset as resetTransform,
|
||||
scale as scaleTransform,
|
||||
translate as translateTransform,
|
||||
toString as transformToString
|
||||
toString as transformToString,
|
||||
makeScale,
|
||||
makeInverse
|
||||
} from '../../transform.js';
|
||||
import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
|
||||
|
||||
@@ -98,6 +99,13 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
*/
|
||||
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.
|
||||
* @private
|
||||
@@ -384,12 +392,14 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
const rotation = frameState.viewState.rotation;
|
||||
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
|
||||
const canvas = context.canvas;
|
||||
const width = Math.round(size[0] * 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) {
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
@@ -544,7 +554,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
return data;
|
||||
}
|
||||
|
||||
const renderPixel = applyTransform(invertTransform(this.overlayPixelTransform_.slice()), pixel.slice());
|
||||
const renderPixel = applyTransform(this.inverseOverlayPixelTransform_, pixel.slice());
|
||||
const context = this.overlayContext_;
|
||||
|
||||
try {
|
||||
|
||||
@@ -162,6 +162,16 @@ export function scale(transform, x, y) {
|
||||
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.
|
||||
@@ -203,30 +213,40 @@ export function compose(transform, dx1, dy1, sx, sy, angle, dx2, dy2) {
|
||||
|
||||
/**
|
||||
* Invert the given transform.
|
||||
* @param {!Transform} transform Transform.
|
||||
* @return {!Transform} Inverse of the transform.
|
||||
* @param {!Transform} source The source transform to invert.
|
||||
* @return {!Transform} The inverted (source) transform.
|
||||
*/
|
||||
export function invert(transform) {
|
||||
const det = determinant(transform);
|
||||
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;
|
||||
export function invert(source) {
|
||||
return makeInverse(source, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@@ -5,10 +5,12 @@ import {
|
||||
setFromArray,
|
||||
translate,
|
||||
scale,
|
||||
makeScale,
|
||||
rotate,
|
||||
multiply,
|
||||
compose,
|
||||
invert,
|
||||
makeInverse,
|
||||
apply
|
||||
} 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() {
|
||||
it('applies rotation to a transform', function() {
|
||||
const transform = create();
|
||||
@@ -110,13 +126,39 @@ describe('ol.transform', function() {
|
||||
|
||||
describe('invert()', function() {
|
||||
it('inverts a transform', function() {
|
||||
let transform = [1, 0, 1, 0, 1, 0];
|
||||
expect(function() {
|
||||
invert(transform);
|
||||
}).to.throwException();
|
||||
transform = [1, 1, 1, 2, 2, 0];
|
||||
const transform = [1, 1, 1, 2, 2, 0];
|
||||
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