diff --git a/src/geometry/polygon.ts b/src/geometry/polygon.ts index 29b306fbe..f79c65946 100644 --- a/src/geometry/polygon.ts +++ b/src/geometry/polygon.ts @@ -1,4 +1,6 @@ import { Point } from "./point"; +import { getCentroid, getMinMaxX, getMinMaxY, isPointInX, isPointInY } from "./polygonUtils"; +import { MinMax } from "./minMax"; /** A polygon, composed of several Points. */ export class Polygon extends Array { @@ -6,4 +8,41 @@ export class Polygon extends Array { constructor(...args: Point[]) { super(...args); } + + /** + * Get the central point (centroid) of the polygon. + */ + public getCentroid(): Point { + return getCentroid(this); + } + + /** + * Get the maximum and minimum X coordinates of the polygon. + */ + public getMinMaxX(): MinMax { + return getMinMaxX(this); + } + + /** + * Get the maximum and minimum Y coordinates of the polygon. + */ + public getMinMaxY(): MinMax { + return getMinMaxY(this); + } + + /** + * Determine if a Point is within the Polygon's X axis (same column). + */ + public isPointInX(point: Point): boolean { + const xCoords = this.getMinMaxX(); + return isPointInX(point, xCoords.min, xCoords.max); + } + + /** + * Determine if a Point is within the Polygon's Y axis (same line). + */ + public isPointInY(point: Point): boolean { + const yCoords = this.getMinMaxY(); + return isPointInY(point, yCoords.min, yCoords.max); + } } diff --git a/src/geometry/polygonUtils.ts b/src/geometry/polygonUtils.ts index 3085209ed..7733a5042 100644 --- a/src/geometry/polygonUtils.ts +++ b/src/geometry/polygonUtils.ts @@ -1,6 +1,5 @@ import { MinMax } from "./minMax"; import { Point } from "./point"; -import { Polygon } from "./polygon"; /** * Get the central point (centroid) given a list of points. @@ -38,7 +37,7 @@ export function getMinMaxY(vertices: Array): MinMax { /** * Determine if a Point is within a Polygon's Y axis. */ -export function isPointInPolygonY(centroid: Point, polygon: Polygon): boolean { +export function isPointInPolygonY(centroid: Point, polygon: Array): boolean { const yCoords = getMinMaxY(polygon); return isPointInY(centroid, yCoords.min, yCoords.max); } @@ -48,7 +47,7 @@ export function isPointInPolygonY(centroid: Point, polygon: Polygon): boolean { * * Can be used to order (sort) words in the same column. */ -export function relativeY(polygon: Polygon): number { +export function relativeY(polygon: Array): number { const sum: number = polygon .map((point) => point[1]) .reduce((prev, cur) => prev + cur); @@ -77,7 +76,7 @@ export function getMinMaxX(vertices: Array): MinMax { /** * Determine if a Point is within a Polygon's X axis. */ -export function isPointInPolygonX(centroid: Point, polygon: Polygon): boolean { +export function isPointInPolygonX(centroid: Point, polygon: Array): boolean { const xCoords = getMinMaxX(polygon); return isPointInX(centroid, xCoords.min, xCoords.max); } @@ -87,14 +86,14 @@ export function isPointInPolygonX(centroid: Point, polygon: Polygon): boolean { * * Can be used to order (sort) words in the same line. */ -export function relativeX(polygon: Polygon): number { +export function relativeX(polygon: Array): number { const sum: number = polygon .map((point) => point[0]) .reduce((prev, cur) => prev + cur); return polygon.length * sum; } -export function getMinYCoordinate(polygon: Polygon): number { +export function getMinYCoordinate(polygon: Array): number { return polygon.sort((point1, point2) => { if (point1[1] < point2[1]) { return -1; @@ -105,7 +104,7 @@ export function getMinYCoordinate(polygon: Polygon): number { })[0][1]; } -export function getMinXCoordinate(polygon: Polygon): number { +export function getMinXCoordinate(polygon: Array): number { return polygon.sort((point1, point2) => { if (point1[0] < point2[0]) { return -1; @@ -116,7 +115,7 @@ export function getMinXCoordinate(polygon: Polygon): number { })[0][0]; } -export function compareOnY(polygon1: Polygon, polygon2: Polygon): number { +export function compareOnY(polygon1: Array, polygon2: Array): number { const sort: number = getMinYCoordinate(polygon1) - getMinYCoordinate(polygon2); if (sort === 0) { @@ -125,7 +124,7 @@ export function compareOnY(polygon1: Polygon, polygon2: Polygon): number { return sort < 0 ? -1 : 1; } -export function compareOnX(polygon1: Polygon, polygon2: Polygon): number { +export function compareOnX(polygon1: Array, polygon2: Array): number { const sort: number = getMinXCoordinate(polygon1) - getMinXCoordinate(polygon2); if (sort === 0) { diff --git a/tests/geometry.spec.ts b/tests/geometry.spec.ts index 234a051f8..1a25eff80 100644 --- a/tests/geometry.spec.ts +++ b/tests/geometry.spec.ts @@ -3,49 +3,43 @@ import { expect } from "chai"; describe("Geometry functions", () => { // 90° rectangle, overlaps polygonB - function polygonA(): geometry.Polygon { - return new geometry.Polygon( - [0.123, 0.53], - [0.175, 0.53], - [0.175, 0.546], - [0.123, 0.546], - ); - } + const polygonA = new geometry.Polygon( + [0.123, 0.53], + [0.175, 0.53], + [0.175, 0.546], + [0.123, 0.546], + ); // 90° rectangle, overlaps polygonA - function polygonB(): geometry.Polygon { - return new geometry.Polygon( - [0.124, 0.535], - [0.19, 0.535], - [0.19, 0.546], - [0.124, 0.546], - ); - } + const polygonB = new geometry.Polygon( + [0.124, 0.535], + [0.19, 0.535], + [0.19, 0.546], + [0.124, 0.546], + ); // not 90° rectangle, doesn't overlap any polygons - function polygonC(): geometry.Polygon { - return new geometry.Polygon( - [0.205, 0.407], - [0.379, 0.407], - [0.381, 0.43], - [0.207, 0.43], - ); - } + const polygonC = new geometry.Polygon( + [0.205, 0.407], + [0.379, 0.407], + [0.381, 0.43], + [0.207, 0.43], + ); it("should get a polygon's bbox", () => { - const bboxA = geometry.getBbox(polygonA()); + const bboxA = geometry.getBbox(polygonA); expect(bboxA.xMin).to.be.eq(0.123); expect(bboxA.yMin).to.be.eq(0.53); expect(bboxA.xMax).to.be.eq(0.175); expect(bboxA.yMax).to.be.eq(0.546); - const bboxB = geometry.getBbox(polygonB()); + const bboxB = geometry.getBbox(polygonB); expect(bboxB.xMin).to.be.eq(0.124); expect(bboxB.yMin).to.be.eq(0.535); expect(bboxB.xMax).to.be.eq(0.19); expect(bboxB.yMax).to.be.eq(0.546); - const bboxC = geometry.getBbox(polygonC()); + const bboxC = geometry.getBbox(polygonC); expect(bboxC.xMin).to.be.eq(0.205); expect(bboxC.yMin).to.be.eq(0.407); expect(bboxC.xMax).to.be.eq(0.381); @@ -53,19 +47,19 @@ describe("Geometry functions", () => { }); it("should get a polygon's bounding box", () => { - expect(geometry.getBoundingBox(polygonA())).to.deep.ordered.members([ + expect(geometry.getBoundingBox(polygonA)).to.deep.ordered.members([ [0.123, 0.53], [0.175, 0.53], [0.175, 0.546], [0.123, 0.546], ]); - expect(geometry.getBoundingBox(polygonB())).to.deep.ordered.members([ + expect(geometry.getBoundingBox(polygonB)).to.deep.ordered.members([ [0.124, 0.535], [0.19, 0.535], [0.19, 0.546], [0.124, 0.546], ]); - expect(geometry.getBoundingBox(polygonC())).to.deep.ordered.members([ + expect(geometry.getBoundingBox(polygonC)).to.deep.ordered.members([ [0.205, 0.407], [0.381, 0.407], [0.381, 0.43], @@ -74,9 +68,11 @@ describe("Geometry functions", () => { }); it("should calculate a polygon's centroid", () => { - expect(geometry.getCentroid(polygonA())).to.have.ordered.members([ - 0.149, 0.538, - ]); + const utilsCentroid = geometry.getCentroid(polygonA); + const polygonCentroid = polygonA.getCentroid(); + const expectedCentroid = [0.149, 0.538]; + expect(utilsCentroid).to.deep.ordered.members(expectedCentroid); + expect(polygonCentroid).to.deep.ordered.members(expectedCentroid); }); it("should determine if two polygons are on the same line", () => { @@ -85,13 +81,19 @@ describe("Geometry functions", () => { // Should only be in polygon C const pointB: geometry.Point = [0.3, 0.42]; - expect(geometry.isPointInPolygonY(pointA, polygonA())).to.be.true; - expect(geometry.isPointInPolygonY(pointA, polygonB())).to.be.true; - expect(geometry.isPointInPolygonY(pointA, polygonC())).to.be.false; - - expect(geometry.isPointInPolygonY(pointB, polygonA())).to.be.false; - expect(geometry.isPointInPolygonY(pointB, polygonB())).to.be.false; - expect(geometry.isPointInPolygonY(pointB, polygonC())).to.be.true; + expect(geometry.isPointInPolygonY(pointA, polygonA)).to.be.true; + expect(polygonA.isPointInY(pointA)).to.be.true; + expect(geometry.isPointInPolygonY(pointA, polygonB)).to.be.true; + expect(polygonB.isPointInY(pointA)).to.be.true; + expect(geometry.isPointInPolygonY(pointA, polygonC)).to.be.false; + expect(polygonC.isPointInY(pointA)).to.be.false; + + expect(geometry.isPointInPolygonY(pointB, polygonA)).to.be.false; + expect(polygonA.isPointInY(pointB)).to.be.false; + expect(geometry.isPointInPolygonY(pointB, polygonB)).to.be.false; + expect(polygonB.isPointInY(pointB)).to.be.false; + expect(geometry.isPointInPolygonY(pointB, polygonC)).to.be.true; + expect(polygonC.isPointInY(pointB)).to.be.true; }); it("should determine if two polygons are on the same column", () => { @@ -100,13 +102,19 @@ describe("Geometry functions", () => { // Should only be in polygon C const pointB: geometry.Point = [0.3, 0.42]; - expect(geometry.isPointInPolygonX(pointA, polygonA())).to.be.true; - expect(geometry.isPointInPolygonX(pointA, polygonB())).to.be.true; - expect(geometry.isPointInPolygonX(pointA, polygonC())).to.be.false; - - expect(geometry.isPointInPolygonX(pointB, polygonA())).to.be.false; - expect(geometry.isPointInPolygonX(pointB, polygonB())).to.be.false; - expect(geometry.isPointInPolygonX(pointB, polygonC())).to.be.true; + expect(geometry.isPointInPolygonX(pointA, polygonA)).to.be.true; + expect(polygonA.isPointInX(pointA)).to.be.true; + expect(geometry.isPointInPolygonX(pointA, polygonB)).to.be.true; + expect(polygonB.isPointInX(pointA)).to.be.true; + expect(geometry.isPointInPolygonX(pointA, polygonC)).to.be.false; + expect(polygonC.isPointInX(pointA)).to.be.false; + + expect(geometry.isPointInPolygonX(pointB, polygonA)).to.be.false; + expect(polygonA.isPointInX(pointB)).to.be.false; + expect(geometry.isPointInPolygonX(pointB, polygonB)).to.be.false; + expect(polygonB.isPointInX(pointB)).to.be.false; + expect(geometry.isPointInPolygonX(pointB, polygonC)).to.be.true; + expect(polygonC.isPointInX(pointB)).to.be.true; }); it("should merge two Bbox", () => {