Handle center constraint properly

This commit is contained in:
Andreas Hocevar
2020-11-23 13:12:32 +01:00
parent 0e2d17b9c6
commit 231390a1f0
2 changed files with 93 additions and 39 deletions

View File

@@ -1116,10 +1116,11 @@ class View extends BaseObject {
/** /**
* Returns the size of the viewport minus padding. * Returns the size of the viewport minus padding.
* @private * @private
* @param {number=} opt_rotation Take into account the rotation of the viewport when giving the size
* @return {import("./size.js").Size} Viewport size reduced by the padding. * @return {import("./size.js").Size} Viewport size reduced by the padding.
*/ */
getViewportSizeMinusPadding_() { getViewportSizeMinusPadding_(opt_rotation) {
let size = this.getViewportSize_(); let size = this.getViewportSize_(opt_rotation);
const padding = this.padding; const padding = this.padding;
if (padding) { if (padding) {
size = [ size = [
@@ -1134,18 +1135,21 @@ class View extends BaseObject {
* @return {State} View state. * @return {State} View state.
*/ */
getState() { getState() {
const projection = this.getProjection();
const resolution = /** @type {number} */ (this.getResolution());
const rotation = this.getRotation();
let center = /** @type {import("./coordinate.js").Coordinate} */ (this.getCenterInternal()); let center = /** @type {import("./coordinate.js").Coordinate} */ (this.getCenterInternal());
const padding = this.padding; const padding = this.padding;
if (padding) { if (padding) {
const reducedSize = this.getViewportSizeMinusPadding_(); const reducedSize = this.getViewportSizeMinusPadding_();
center = this.calculateShiftedCenter(center, this.getViewportSize_(), [ center = calculateCenterOn(
reducedSize[0] / 2 + padding[3], center,
reducedSize[1] / 2 + padding[0], this.getViewportSize_(),
]); [reducedSize[0] / 2 + padding[3], reducedSize[1] / 2 + padding[0]],
resolution,
rotation
);
} }
const projection = this.getProjection();
const resolution = /** @type {number} */ (this.getResolution());
const rotation = this.getRotation();
return { return {
center: center.slice(0), center: center.slice(0),
projection: projection !== undefined ? projection : null, projection: projection !== undefined ? projection : null,
@@ -1371,33 +1375,42 @@ class View extends BaseObject {
*/ */
centerOnInternal(coordinate, size, position) { centerOnInternal(coordinate, size, position) {
this.setCenterInternal( this.setCenterInternal(
this.calculateShiftedCenter(coordinate, size, position) calculateCenterOn(
coordinate,
size,
position,
this.getResolution(),
this.getRotation()
)
); );
} }
/** /**
* @param {import("./coordinate.js").Coordinate} coordinate Coordinate. * Calculates the shift between map and viewport center.
* @param {import("./size.js").Size} size Box pixel size. * @param {import("./coordinate.js").Coordinate} center Center.
* @param {import("./pixel.js").Pixel} position Position on the view to center on. * @param {number} resolution Resolution.
* @return {import("./coordinate.js").Coordinate} Shifted center. * @param {number} rotation Rotation.
* @param {import("./size.js").Size} size Size.
* @return {Array<number>|undefined} Center shift.
*/ */
calculateShiftedCenter(coordinate, size, position) { calculateCenterShift(center, resolution, rotation, size) {
// calculate rotated position let centerShift;
const rotation = this.getRotation(); const padding = this.padding;
const cosAngle = Math.cos(-rotation); if (padding && center) {
let sinAngle = Math.sin(-rotation); const reducedSize = this.getViewportSizeMinusPadding_(-rotation);
let rotX = coordinate[0] * cosAngle - coordinate[1] * sinAngle; const shiftedCenter = calculateCenterOn(
let rotY = coordinate[1] * cosAngle + coordinate[0] * sinAngle; center,
const resolution = this.getResolution(); size,
rotX += (size[0] / 2 - position[0]) * resolution; [reducedSize[0] / 2 + padding[3], reducedSize[1] / 2 + padding[0]],
rotY += (position[1] - size[1] / 2) * resolution; resolution,
rotation
// go back to original angle );
sinAngle = -sinAngle; // go back to original rotation centerShift = [
const centerX = rotX * cosAngle - rotY * sinAngle; center[0] - shiftedCenter[0],
const centerY = rotY * cosAngle + rotX * sinAngle; center[1] - shiftedCenter[1],
];
return [centerX, centerY]; }
return centerShift;
} }
/** /**
@@ -1600,7 +1613,13 @@ class View extends BaseObject {
this.targetCenter_, this.targetCenter_,
newResolution, newResolution,
size, size,
isMoving isMoving,
this.calculateCenterShift(
this.targetCenter_,
newResolution,
newRotation,
size
)
); );
if (this.get(ViewProperty.ROTATION) !== newRotation) { if (this.get(ViewProperty.ROTATION) !== newRotation) {
@@ -1645,7 +1664,14 @@ class View extends BaseObject {
const newCenter = this.constraints_.center( const newCenter = this.constraints_.center(
this.targetCenter_, this.targetCenter_,
newResolution, newResolution,
size size,
false,
this.calculateCenterShift(
this.targetCenter_,
newResolution,
newRotation,
size
)
); );
if (duration === 0 && !this.cancelAnchor_) { if (duration === 0 && !this.cancelAnchor_) {
@@ -1986,4 +2012,29 @@ export function isNoopAnimation(animation) {
return true; return true;
} }
/**
* @param {import("./coordinate.js").Coordinate} coordinate Coordinate.
* @param {import("./size.js").Size} size Box pixel size.
* @param {import("./pixel.js").Pixel} position Position on the view to center on.
* @param {number} resolution Resolution.
* @param {number} rotation Rotation.
* @return {import("./coordinate.js").Coordinate} Shifted center.
*/
function calculateCenterOn(coordinate, size, position, resolution, rotation) {
// calculate rotated position
const cosAngle = Math.cos(-rotation);
let sinAngle = Math.sin(-rotation);
let rotX = coordinate[0] * cosAngle - coordinate[1] * sinAngle;
let rotY = coordinate[1] * cosAngle + coordinate[0] * sinAngle;
rotX += (size[0] / 2 - position[0]) * resolution;
rotY += (position[1] - size[1] / 2) * resolution;
// go back to original angle
sinAngle = -sinAngle; // go back to original rotation
const centerX = rotX * cosAngle - rotY * sinAngle;
const centerY = rotY * cosAngle + rotX * sinAngle;
return [centerX, centerY];
}
export default View; export default View;

View File

@@ -4,7 +4,7 @@
import {clamp} from './math.js'; import {clamp} from './math.js';
/** /**
* @typedef {function((import("./coordinate.js").Coordinate|undefined), number, import("./size.js").Size, boolean=): (import("./coordinate.js").Coordinate|undefined)} Type * @typedef {function((import("./coordinate.js").Coordinate|undefined), number, import("./size.js").Size, boolean=, Array<number>=): (import("./coordinate.js").Coordinate|undefined)} Type
*/ */
/** /**
@@ -21,16 +21,19 @@ export function createExtent(extent, onlyCenter, smooth) {
* @param {number} resolution Resolution. * @param {number} resolution Resolution.
* @param {import("./size.js").Size} size Viewport size; unused if `onlyCenter` was specified. * @param {import("./size.js").Size} size Viewport size; unused if `onlyCenter` was specified.
* @param {boolean=} opt_isMoving True if an interaction or animation is in progress. * @param {boolean=} opt_isMoving True if an interaction or animation is in progress.
* @param {Array<number>=} opt_centerShift Shift between map center and viewport center.
* @return {import("./coordinate.js").Coordinate|undefined} Center. * @return {import("./coordinate.js").Coordinate|undefined} Center.
*/ */
function (center, resolution, size, opt_isMoving) { function (center, resolution, size, opt_isMoving, opt_centerShift) {
if (center) { if (center) {
const viewWidth = onlyCenter ? 0 : size[0] * resolution; const viewWidth = onlyCenter ? 0 : size[0] * resolution;
const viewHeight = onlyCenter ? 0 : size[1] * resolution; const viewHeight = onlyCenter ? 0 : size[1] * resolution;
let minX = extent[0] + viewWidth / 2; const shiftX = opt_centerShift ? opt_centerShift[0] : 0;
let maxX = extent[2] - viewWidth / 2; const shiftY = opt_centerShift ? opt_centerShift[1] : 0;
let minY = extent[1] + viewHeight / 2; let minX = extent[0] + viewWidth / 2 + shiftX;
let maxY = extent[3] - viewHeight / 2; let maxX = extent[2] - viewWidth / 2 + shiftX;
let minY = extent[1] + viewHeight / 2 + shiftY;
let maxY = extent[3] - viewHeight / 2 + shiftY;
// note: when zooming out of bounds, min and max values for x and y may // note: when zooming out of bounds, min and max values for x and y may
// end up inverted (min > max); this has to be accounted for // end up inverted (min > max); this has to be accounted for