# geom-2d

2D polygon utilities: orientation checks, convex hull, point-in-polygon, polygon containment, and intersection testing.

---

## Types

```typescript
type Point2D = readonly [number, number];
type Polygon2D = Point2D[];
```

---

## Constants

```typescript
const Clockwise: unique symbol;
const CounterClockWise: unique symbol;
```

---

## getPointsOrientation

```typescript
function getPointsOrientation(p: Point2D, q: Point2D, r: Point2D): PointsOrientation
```

Returns the turn direction formed by the three points `p → q → r`.

- Returns `Clockwise` when the turn is clockwise.
- Returns `CounterClockWise` when the turn is counter-clockwise.
- Returns `Collinear` (neither) when the points are collinear.

---

## computeConvexHull

```typescript
function computeConvexHull(points: Point2D[]): Point2D[]
```

Computes the convex hull of a set of 2D points using a Graham scan variant.

Returns a **closed polygon**: the first point is repeated as the last element so that every consecutive pair `[i, i+1]` is an edge (compatible with `intersectingPolygons`).

```typescript
const hull = computeConvexHull([
    [0, 0], [1, 0], [1, 1], [0, 1],
]);
// hull.length === 5  (4 unique corners + closing point)
// hull[0] === hull[hull.length - 1]
```

---

## pointInPolygon

```typescript
function pointInPolygon(point: Point2D, poly: Polygon2D): boolean
```

Ray-casting point-in-polygon test. Casts a horizontal ray to positive infinity and counts edge crossings.

Handles **both open and closed polygons**:
- **Open polygon** `[p0, p1, …, pn-1]` — the closing edge `pn-1 → p0` is added implicitly.
- **Closed polygon** `[p0, p1, …, pn-1, p0]` — the duplicate closing edge is zero-length and contributes nothing, so results are identical.

```typescript
const square = [[0,0],[1,0],[1,1],[0,1]];
pointInPolygon([0.5, 0.5], square); // true
pointInPolygon([2,   2  ], square); // false
```

---

## normalizePolygon

```typescript
function normalizePolygon(poly: Polygon2D, orientation: PolygonOrientation): Polygon2D
```

Returns `poly` in the requested orientation (`Clockwise` or `CounterClockWise`). If the polygon is already in the correct orientation it is returned as-is. If not, a **new reversed array** is returned — the original is never mutated.

---

## polygonInPolygon

```typescript
function polygonInPolygon(sourcePolygon: Polygon2D, targetPolygon: Polygon2D): boolean
```

Returns `true` if every vertex of `sourcePolygon` lies inside `targetPolygon`.

---

## intersectingPolygons

```typescript
function intersectingPolygons(poly1: Polygon2D, poly2: Polygon2D): boolean
```

Returns `true` if `poly1` and `poly2` intersect, using the Separating Axis Theorem (SAT). Both polygons are normalised to clockwise orientation before testing.
