Skip to content

Commit

Permalink
feat: add planar options, opposite-planar and center-planar for resiz…
Browse files Browse the repository at this point in the history
…eable in select mode (#207)
  • Loading branch information
JamesLMilner committed Mar 5, 2024
1 parent 0ce778f commit 2a75186
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 14 deletions.
4 changes: 2 additions & 2 deletions guides/4.MODES.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ const selectMode = new TerraDrawSelectMode({
// [Requires draggable to be true] allow resizing of the geometry from
// a given origin. Center fixed will allow resizing on fixed aspect ratio from the center
// and opposite-fixed allows resizing from the opposite corner of the bounding box
// of the geometry.
resizeable: 'center-fixed', // can also be 'opposite-fixed'
// of the geometry. Defaults to geodesic transforms, unless planar options are used.
resizeable: 'center-fixed', // can also be 'opposite-fixed', 'opposite-planar', 'center', 'center-planar'

// Can be deleted
deletable: true,
Expand Down
68 changes: 56 additions & 12 deletions src/modes/select/behaviors/drag-coordinate-resize.behavior.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import { limitPrecision } from "../../../geometry/limit-decimal-precision";
import { transformScale } from "../../../geometry/transform/scale";
import { pixelDistance } from "../../../geometry/measure/pixel-distance";

export type ResizeOptions = "center-fixed" | "opposite-fixed" | "opposite";
export type ResizeOptions =
| "center-fixed"
| "opposite-fixed"
| "opposite"
| "center"
| "center-planar"
| "opposite-planar";

type OppositeMapIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;

Expand Down Expand Up @@ -127,11 +133,17 @@ export class DragCoordinateResizeBehavior extends TerraDrawModeBehavior {
// Get the origin for the scaling to occur from
let origin: Position | undefined;
let oppositeIndex: OppositeMapIndex | undefined;
if (resizeOption === "center-fixed") {

if (
resizeOption === "center-fixed" ||
resizeOption === "center" ||
resizeOption === "center-planar"
) {
origin = this.getCenterOrigin(feature);
} else if (
resizeOption === "opposite-fixed" ||
resizeOption === "opposite"
resizeOption === "opposite" ||
resizeOption === "opposite-planar"
) {
const { origin: oppositeOrigin, index } = this.getOppositeOrigin(
feature,
Expand Down Expand Up @@ -194,25 +206,29 @@ export class DragCoordinateResizeBehavior extends TerraDrawModeBehavior {

if (resizeOption === "center-fixed" || resizeOption === "opposite-fixed") {
transformScale(feature, scale, origin as Position);
} else if (resizeOption === "opposite") {
} else if (resizeOption === "opposite" || resizeOption === "center") {
// Quick check to ensure it's viable to even scale
// TODO: We could probably be smarter about this as this will
// break in some instances
if (distanceSelectedToCursor > distanceOriginToCursor) {
return false;
}

const validX =
!isNaN(xScale) && xScale > 0 && yScale < Number.MAX_SAFE_INTEGER;
const validY =
!isNaN(yScale) && yScale > 0 && yScale < Number.MAX_SAFE_INTEGER;
if (!this.validateScale(xScale, yScale)) {
return false;
}

if (validX && validY) {
transformScale(feature, xScale, origin as Position, "x");
transformScale(feature, yScale, origin as Position, "y");
} else {
transformScale(feature, xScale, origin as Position, "x");
transformScale(feature, yScale, origin as Position, "y");
} else if (
resizeOption === "center-planar" ||
resizeOption === "opposite-planar"
) {
if (!this.validateScale(xScale, yScale)) {
return false;
}

this.scalePlanar(updatedCoords, originX, originY, xScale, yScale);
} else {
throw new Error("Invalid resize option");
}
Expand Down Expand Up @@ -268,6 +284,34 @@ export class DragCoordinateResizeBehavior extends TerraDrawModeBehavior {
return true;
}

private validateScale(xScale: number, yScale: number) {
const validX =
!isNaN(xScale) && xScale > 0 && yScale < Number.MAX_SAFE_INTEGER;
const validY =
!isNaN(yScale) && yScale > 0 && yScale < Number.MAX_SAFE_INTEGER;

return validX && validY;
}

private scalePlanar(
coordinates: Position[],
originX: number,
originY: number,
xScale: number,
yScale: number,
) {
coordinates.forEach((coordinate) => {
const { x, y } = this.project(coordinate[0], coordinate[1]);

const updatedX = originX + (x - originX) * xScale;
const updatedY = originY + (y - originY) * yScale;
const updatedCoordinate = this.unproject(updatedX, updatedY);

coordinate[0] = updatedCoordinate.lng;
coordinate[1] = updatedCoordinate.lat;
});
}

private getCenterOrigin(feature: Feature<Polygon | LineString>) {
return centroid(feature);
}
Expand Down

0 comments on commit 2a75186

Please sign in to comment.