Skip to content

Commit

Permalink
fix: completely refactor resizable
Browse files Browse the repository at this point in the history
* fix: refactor resizable to behaviour closer to common drawing tool UX patterns

* fix: completely refactor resizable flag for select mode
  • Loading branch information
JamesLMilner committed Mar 25, 2024
1 parent 0a88c2b commit 9573164
Show file tree
Hide file tree
Showing 10 changed files with 527 additions and 358 deletions.
5 changes: 3 additions & 2 deletions development/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ const getModes = () => {
midpoints: true,
draggable: true,
deletable: true,
resizable: "opposite-web-mercator",
},
},
},
Expand All @@ -120,7 +121,7 @@ const getModes = () => {
coordinates: {
midpoints: false,
draggable: true,
resizable: "opposite",
resizable: "center-web-mercator",
deletable: true,
},
},
Expand All @@ -131,7 +132,7 @@ const getModes = () => {
coordinates: {
midpoints: false,
draggable: true,
resizable: "opposite",
resizable: "opposite-web-mercator",
deletable: true,
},
},
Expand Down
4 changes: 2 additions & 2 deletions e2e/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const example = {
draggable: true,
coordinates: {
draggable: true,
resizable: "opposite-fixed",
resizable: "opposite-web-mercator",
},
},
},
Expand All @@ -90,7 +90,7 @@ const example = {
draggable: true,
coordinates: {
draggable: true,
resizable: "center-fixed",
resizable: "center-web-mercator",
},
},
},
Expand Down
4 changes: 2 additions & 2 deletions e2e/tests/leaflet.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ test.describe("select mode", () => {
await page.mouse.click(mapDiv.width - 10, mapDiv.height / 2);

// Dragged the square up and to the left
await expectGroupPosition({ page, x: 584, y: 304 });
await expectGroupPosition({ page, x: 490, y: 408 });
});

test("selected circle can has it's shape maintained from center origin when coordinates are dragged", async ({
Expand Down Expand Up @@ -384,7 +384,7 @@ test.describe("select mode", () => {
await page.mouse.click(mapDiv.width - 10, mapDiv.height / 2);

// Dragged the square up and to the left
await expectGroupPosition({ page, x: 430, y: 150 });
await expectGroupPosition({ page, x: 447, y: 138 });
});
});

Expand Down
10 changes: 4 additions & 6 deletions guides/4.MODES.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,10 @@ const selectMode = new TerraDrawSelectMode({
draggable: 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. Defaults to geodesic transforms, unless planar options are
// used such as center-planar or opposite-planar. Will take priority over draggable
// if set to a value.
resizeable: 'opposite-planar', // can also be 'opposite-fixed', 'opposite-planar', 'center', 'center-planar'
// center-web-mercator will allow resizing of the aspect ratio from the center
// and opposite-web-mercator allows resizing from the opposite corner of the
// bounding box of the geometry.
resizeable: 'center-web-mercator', // can also be 'opposite-web-mercator'

// Can be deleted
deletable: true,
Expand Down
49 changes: 49 additions & 0 deletions src/geometry/project/web-mercator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { lngLatToWebMercatorXY, webMercatorXYToLngLat } from "./web-mercator";

describe("web mercator", () => {
describe("lngLatToWebMercatorXY", () => {
it("returns x,y of 0,0 web mercator coordinates from lng,lat of 0,0", () => {
const result = lngLatToWebMercatorXY(0, 0);
expect(result).toStrictEqual({ x: 0, y: 0 });
});

it("returns correct x,y web mercator coordinates for lng, lat", () => {
const result = lngLatToWebMercatorXY(179, 89);
expect(result).toStrictEqual({
x: 19926188.85199597,
y: 30240971.958386205,
});
});

it("returns correct x,y web mercator coordinates for lng, lat", () => {
const result = lngLatToWebMercatorXY(-179, -89);
expect(result).toStrictEqual({
x: -19926188.85199597,
y: -30240971.95838617,
});
});
});

describe("webMercatorXYToLngLat", () => {
it("returns x,y of 0,0 web mercator coordinates from lng,lat of 0,0", () => {
const result = webMercatorXYToLngLat(0, 0);
expect(result).toStrictEqual({ lng: 0, lat: 0 });
});

it("returns correct x,y web mercator coordinates for lng, lat", () => {
const result = webMercatorXYToLngLat(
19926188.85199597,
30240971.958386205,
);
expect(result).toStrictEqual({ lng: 179, lat: 89.00000000000001 }); // TODO: should we limit precision?
});

it("returns correct x,y web mercator coordinates for lng, lat", () => {
const result = webMercatorXYToLngLat(
-19926188.85199597,
-30240971.95838617,
);
expect(result).toStrictEqual({ lng: -179, lat: -89 });
});
});
});
37 changes: 37 additions & 0 deletions src/geometry/project/web-mercator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const RADIANS_TO_DEGREES = 57.29577951308232 as const; // 180 / Math.PI
const DEGREES_TO_RADIANS = 0.017453292519943295 as const; // Math.PI / 180
const R = 6378137 as const;

/**
* Convert longitude and latitude to web mercator x and y
* @param lng
* @param lat
* @returns - web mercator x and y
*/
export const lngLatToWebMercatorXY = (
lng: number,
lat: number,
): { x: number; y: number } => ({
x: lng === 0 ? 0 : lng * DEGREES_TO_RADIANS * R,
y:
lat === 0
? 0
: Math.log(Math.tan(Math.PI / 4 + (lat * DEGREES_TO_RADIANS) / 2)) * R,
});

/**
* Convert web mercator x and y to longitude and latitude
* @param x - web mercator x
* @param y - web mercator y
* @returns - longitude and latitude
*/
export const webMercatorXYToLngLat = (
x: number,
y: number,
): { lng: number; lat: number } => ({
lng: x === 0 ? 0 : RADIANS_TO_DEGREES * (x / R),
lat:
y === 0
? 0
: (2 * Math.atan(Math.exp(y / R)) - Math.PI / 2) * RADIANS_TO_DEGREES,
});
110 changes: 17 additions & 93 deletions src/modes/select/behaviors/drag-coordinate-resize.behavior.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ describe("DragCoordinateResizeBehavior", () => {
new PixelDistanceBehavior(config),
selectionPointBehavior,
new MidPointBehavior(config, selectionPointBehavior),
10,
);
});
});
Expand All @@ -66,7 +65,6 @@ describe("DragCoordinateResizeBehavior", () => {
pixelDistanceBehavior,
selectionPointBehavior,
midpointBehavior,
10,
);
});

Expand Down Expand Up @@ -138,7 +136,10 @@ describe("DragCoordinateResizeBehavior", () => {
it("returns early if nothing is being dragged", () => {
jest.spyOn(config.store, "updateGeometry");

dragMaintainedShapeBehavior.drag(mockDrawEvent(), "center-fixed");
dragMaintainedShapeBehavior.drag(
mockDrawEvent(),
"center-web-mercator",
);

expect(config.store.updateGeometry).toBeCalledTimes(0);
});
Expand All @@ -147,98 +148,15 @@ describe("DragCoordinateResizeBehavior", () => {
createStorePoint(config);
jest.spyOn(config.store, "updateGeometry");

dragMaintainedShapeBehavior.drag(mockDrawEvent(), "center-fixed");
dragMaintainedShapeBehavior.drag(
mockDrawEvent(),
"center-web-mercator",
);

expect(config.store.updateGeometry).toBeCalledTimes(0);
});

describe("center-fixed", () => {
it("updates the Polygon coordinate if within pointer distance", () => {
const id = createStorePolygon(config);

dragMaintainedShapeBehavior.startDragging(id, 0);

jest.spyOn(config.store, "updateGeometry");

// Mock the projection for the cooridinates of the bounding box
// when measuring against them to prevent overlap
for (let i = 0; i < 10; i++) {
(config.project as jest.Mock)
.mockReturnValueOnce({ x: 0, y: 0 })
.mockReturnValueOnce({ x: 100, y: 100 });
}

dragMaintainedShapeBehavior.drag(mockDrawEvent(), "center-fixed");

expect(config.store.updateGeometry).toBeCalledTimes(1);
});

it("updates the LineString coordinate if within pointer distance", () => {
const id = createLineString(config);
jest.spyOn(config.store, "updateGeometry");

dragMaintainedShapeBehavior.startDragging(id, 0);

// Mock the projection for the cooridinates of the bounding box
// when measuring against them to prevent overlap
for (let i = 0; i < 10; i++) {
(config.project as jest.Mock)
.mockReturnValueOnce({ x: 0, y: 0 })
.mockReturnValueOnce({ x: 100, y: 100 });
}

dragMaintainedShapeBehavior.drag(mockDrawEvent(), "center-fixed");

expect(config.store.updateGeometry).toBeCalledTimes(1);
});
});

describe("opposite-fixed", () => {
it("updates the Polygon coordinate if within pointer distance", () => {
const id = createStorePolygon(config);

dragMaintainedShapeBehavior.startDragging(id, 0);

jest.spyOn(config.store, "updateGeometry");

// Mock the projection for the cooridinates of the bounding box
// when measuring against them to prevent overlap
for (let i = 0; i < 10; i++) {
(config.project as jest.Mock)
.mockReturnValueOnce({ x: 0, y: 0 })
.mockReturnValueOnce({ x: 100, y: 100 });
}

dragMaintainedShapeBehavior.drag(mockDrawEvent(), "opposite-fixed");

expect(config.store.updateGeometry).toBeCalledTimes(1);
});

it("updates the LineString coordinate if within pointer distance", () => {
const id = createLineString(config);
jest.spyOn(config.store, "updateGeometry");

dragMaintainedShapeBehavior.startDragging(id, 0);

// (config.project as jest.Mock)
// .mockReturnValueOnce({ x: 0, y: 0 })
// .mockReturnValueOnce({ x: 0, y: 1 });

// Mock the projection for the cooridinates of the bounding box
// when measuring against them to prevent overlap
for (let i = 0; i < 10; i++) {
(config.project as jest.Mock)
.mockReturnValueOnce({ x: 0, y: 0 })
.mockReturnValueOnce({ x: 100, y: 100 });
}

dragMaintainedShapeBehavior.drag(mockDrawEvent(), "opposite-fixed");

expect(config.store.updateGeometry).toBeCalledTimes(1);
});
});

describe("opposite", () => {
describe("opposite-web-mercator", () => {
it("updates the Polygon coordinate if within pointer distance", () => {
const id = createStorePolygon(config);

Expand All @@ -254,7 +172,10 @@ describe("DragCoordinateResizeBehavior", () => {
.mockReturnValueOnce({ x: 100, y: 100 });
}

dragMaintainedShapeBehavior.drag(mockDrawEvent(), "opposite");
dragMaintainedShapeBehavior.drag(
mockDrawEvent(),
"opposite-web-mercator",
);

expect(config.store.updateGeometry).toBeCalledTimes(1);
});
Expand All @@ -273,7 +194,10 @@ describe("DragCoordinateResizeBehavior", () => {
.mockReturnValueOnce({ x: 100, y: 100 });
}

dragMaintainedShapeBehavior.drag(mockDrawEvent(), "opposite");
dragMaintainedShapeBehavior.drag(
mockDrawEvent(),
"opposite-web-mercator",
);

expect(config.store.updateGeometry).toBeCalledTimes(1);
});
Expand Down
Loading

0 comments on commit 9573164

Please sign in to comment.