**A collection of functions for working with points in a 2D coordinate system, along with additional utility functions.**

[![CI](https://github.com/Arman2409/pointscape/actions/workflows/ci.yml/badge.svg)](https://github.com/Arman2409/pointscape/actions/workflows/ci.yml)
[![npm](https://img.shields.io/npm/v/pointscape.svg)](https://www.npmjs.com/package/pointscape)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)

**[Live demos →](https://arman2409.github.io/pointscape/)**

## Table of contents

- [What's new](#whats-new-23x)
- [How to use](#how-to-use) — install, imports, develops, publishes
- [Examples](#examples)
- [Point class](#point-class)
- [Function reference](#the-list-of-available-functions)
- [Changelog](./CHANGELOG.md)

---

## What's new (2.3.0)

- **`scale` and `rotate` argument order** — points array comes first: `scale(points, factorX, factorY)` and `rotate(points, center, angle)`.
- **Source maps** — published builds include `.map` files for easier debugging.

**Upgrading from 2.2.x?** Swap argument order for `scale` and `rotate` (see [CHANGELOG](./CHANGELOG.md#230--2026-05-29)).

See **[CHANGELOG.md](./CHANGELOG.md)** for full notes.

---

## How to use

Use **Node 18+**. The repo pins **Node 20** for development (**[`.nvmrc`](./.nvmrc)** — `nvm use` recommended).

### Install

npm:

```bash
npm install pointscape
```

pnpm / Yarn:

```bash
pnpm add pointscape
# or
yarn add pointscape
```

### What ships on npm

The published tarball contains only **`dist/`** (ESM + CJS + types), **README**, **CHANGELOG**, and **LICENSE**—see [`package.json` → `files`](./package.json). Import ESM with `import` or CommonJS with `require` per your bundler; types are exposed through [`exports`](./package.json) for both.

1. **Default import** — one object with every export (larger surface; fine for scripts).

```typescript
import pointscape from "pointscape";
```

2. **Named imports** (preferred for tree-shaking) — see [Types and imports](#types-and-imports-typescript) below.

### Types and imports (TypeScript)

Use **named imports** so bundlers can tree-shake unused functions:

```typescript
import { distance, collision, nearest, Point } from "pointscape";
import type { Bounds, Line, PointType } from "pointscape";
```

Functions take any **`{ x: number; y: number }`**; you do **not** have to allocate a `Point` class instance. **`PointType`** matches instances of **`Point`** (the class exported from this package).

### Developing

Open a PR against the default branch; **CI** runs **`npm ci`**, **tests**, and **`npm run build`** on every push/PR (see **[`.github/workflows/ci.yml`](./.github/workflows/ci.yml)**).

```bash
git clone https://github.com/Arman2409/pointscape.git
cd pointscape
nvm use   # optional: picks Node from .nvmrc
npm ci
npm test
npm run build
```

### Publishing on npm

1. Bump **`version`** in `package.json` and update **`CHANGELOG.md`**.
2. **`npm login`** with an account that [**maintains** `pointscape\*\*](https://www.npmjs.com/package/pointscape) on the public registry (`https://registry.npmjs.org/`).
3. **`npm publish`** — the **`prepack`** script rebuilds **`dist/`** first.

Preview tarball contents locally: **`npm run publish:dry-run`**.

See also [npm `files` publish guide](https://docs.npmjs.com/cli/v10/configuring-npm/package-json#files) — only `dist/`, readme, changelog, and license are published.

## Examples

Most of the functions are designed for working with points in a 2D coordinate system.

```typescript
import { distance, middle, angle, center, perimeter } from "pointscape";

const point1 = { x: 0, y: 0 };
const point2 = { x: 10, y: 10 };

// Plain { x, y } objects work for all point APIs

const distanceBetweenPoints = distance(point1, point2);
// result: 14.142135623730951

const middlePoint = middle(point1, point2);
// result: {x: 5, y: 5}

const angleBetweenPoints = angle(point1, point2);
// result: 0.7853981633974483

const point3 = { x: 0, y: 10 },
    point4 = { x: 10, y: 0 };

const perimeterOfPoints = perimeter([point1, point2, point3, point4]);
// result: 48.2842712474619
```

### Point class

The `Point` class wraps any `{ x, y }` and exposes all geometric operations as chainable methods. Every method that returns a point returns a `Point` instance, so calls can be chained.

```typescript
import { Point } from "pointscape";

const a = new Point(10, 20);
const b = new Point(50, 80);

// Basic methods
a.distanceTo(b); // 72.11...
a.angleTo(b); // number in radians
a.equals(b); // false
a.clone(); // Point(10, 20)
a.toString(); // "Point(10, 20)"

// Factory — convert a plain { x, y } to a Point instance
const c = Point.from({ x: 0, y: 0 });

// Chainable — move() and lerp() return Point instances
a.move(5, -5) // Point(15, 15)
    .lerp(b, 0.5) // Point(32.5, 47.5)
    .distanceTo(b); // number

// Rotate a point around a center
a.rotateAround({ x: 0, y: 0 }, Math.PI); // Point(-10, -20)

// Array-based methods — also return Point or Point | null
a.nearestFromPoints([b, c]); // Point | null
a.farthestFromPoints([b, c]); // Point | null
```

Plain `{ x: number; y: number }` objects work for every function — you don't need to allocate a `Point` instance unless you want the method API.

There are other utility functions as well.

```typescript
import { inRange, chunk, randomBoolean } from "pointscape";

//  Helper functions for math

const isInTheRange = inRange(1, 0, 10);
// result: true

// Helper functions for arrays

const chunks = chunk([1, 1, 1, 1], 2);
// result: [[1, 1], [1, 1]]

// Helper functions for randomization

const randomBool = randomBoolean();
// result: true or false
```

## The list of available functions

### Function Categories

- [Points](#points)
    - [Geometry](#geometry)
    - [Positioning](#positioning)
    - [Relationships](#relationships)
- [Math](#math)
- [Arrays](#arrays)
- [Randomization](#randomization)

#### Points

##### Geometry

[area](#geometry)

[triangleArea](#geometry)

[perimeter](#geometry)

[center](#geometry)

[square](#geometry)

[rectangle](#geometry)

[pentagon](#geometry)

[triangle](#geometry)

[circleArea](#geometry)

[middle](#geometry)

##### Positioning

[distance](#positioning)

[positionInCircle](#positioning)

[angle](#positioning)

[nearest](#positioning)

[randomPoint](#positioning)

[randomPointInDistance](#positioning)

[randomPoints](#positioning)

[farthest](#positioning)

[rotate](#positioning)

[inLine](#positioning)

[cross](#positioning)

[move](#positioning)

[lerp](#positioning)

##### Relationships

[collision](#relationships)

[collisionInArray](#relationships)

[possibleConnections](#relationships)

[sort](#relationships)

[scale](#relationships)

[pointWithoutCollision](#relationships)

#### Math

[degreesToRadians](#math)

[radiansToDegrees](#math)

[inRange](#math)

[roundToPrecision](#math)

[average](#math)

#### Arrays

[intersection](#arrays)

[difference](#arrays)

[chunk](#arrays)

[removeDuplicate](#arrays)

[sample](#arrays)

#### Randomization

[randomNumber](#randomization)

[randomBoolean](#randomization)

[uniqueId](#randomization)

## Function documentation

### Points

- <b id="distance">distance</b>

```typescript
interface Point {
    x: number;
    y: number;
}

(point1: Point, point2: Point) => number;
```

Returns the distance beetween two points, each point is an object with x and y properties.

- <b id="area">area</b>

```typescript
(points: Point[]) => number;
```

Returns the area enclosed by the given points.
Takes an array of points as argument, where each point is an object with x and y properties.

- <b id="trianglearea">triangleArea</b>

```typescript
(point1: Point, point2: Point, point3: Point) => number;
```

Returns the area of the triangle formed by three points.

- <b id="collision">collision</b>

```typescript
(
    point1: Point,
    point2: Point,
    collisionDistance: number,
    [callback]?: () => void
) => boolean;
```

Returns **`true`** if the **Euclidean distance** between the two points is less than or equal to **`collisionDistance`** (treat **`collisionDistance` as a non-negative radius for a circle** around each position). Optionally invokes **`callback`** when a collision is detected.

- <b id="collisioninarray">collisionInArray</b>

```typescript
  (initialPoint: Point, points: Point[], radius: number) => Point[]
```

Returns every point in **`points`** whose distance to **`initialPoint`** is within **`radius`**, using the same circular rule as [`collision`](#collision).

- <b id="positionincircle">positionInCircle</b>

```typescript
(point: Point, radius: number, angleInRadians: number) => Point;
```

Returns the x and y coordinates for the current point in the circle, given its center point, radius, and angle.

- <b id="angle">angle</b>

```typescript
(point1: Point, point2: Point) => number;
```

Returns the angle formed by the connection of two points.

- <b id="middle">middle</b>

```typescript
(point1: Point, point2: Point) => Point;
```

Returns the midpoint between two points.

- <b id="nearest">nearest</b>

```typescript
(point: Point, points: Point[]) => Point | null;
```

Returns the nearest point to the given point from the array, or **`null`** if **`points`** is empty.

- <b id="perimeter">perimeter</b>

```typescript
(points: Point[]) => number;
```

Returns the perimeter of the figure formed by the given points.

- <b id="pointwithoutcollision">pointWithoutCollision(minX, maxX, minY, maxY, distance, points)</b>

```typescript
interface Bounds {
    min: number;
    max: number;
}

(xBounds: Bounds, yBounds: Bounds, distance: number, points: Point[]) =>
    Point | string;
```

Returns a point that doesn't collide with any of the given points within the specified distance, if such a point exists, otherwise returns error string.

- <b id="randompoint">randomPoint</b>

```typescript
([xBounds]: Bounds, [yBounds]: Bounds) => Point;
```

Returns a random point within the given dimensions, if provided, otherwise in 100 units on both axes.

- <b id="randompointindistance">randomPointInDistance</b>

```typescript
(point: Point, distance: number) => Point;
```

Returns a random point within the given distance from the specified point.

- <b id="randompoints">randomPoints</b>

```typescript
   ([xBounds]: Bounds, [yBounds]: Bounds, quantity: number,) => Point[]
```

Returns a specified quantity of random points within the given dimensions, if dimensions are provided, otherwise in the range of 100.

- <b id="possibleconnections">possibleConnections</b>

```typescript
(pointsCount: number) => number;
```

Returns the quantity of possible connections among given quantity of points.

- <b id="circlearea">circleArea</b>

```typescript
(radius: number) => number;
```

Returns the area of the circle given its radius.

- <b id="center">center</b>

```typescript
(points: Point[]) => Point;
```

Returns the center of given points.

- <b id="farthest">farthest</b>

```typescript
(point: Point, points: Point[]) => Point | null;
```

Returns the farthest point from the given point among the given array; **`null`** if the array is empty.

- <b id="rotate">rotate</b>

```typescript
  (points: Point[], centerPoint: Point, angleInRadians: number) => Point[]
```

Returns the points rotated around the given center point.

- <b id="sort">sort</b>

```typescript
  (points: Point[], coordinate?: "x" | "y") => Point[]
```

**Mutates `points`** in place (`Array.sort`) and returns the same array reference. Omit **`coordinate`** to sort primarily by **`x`**; **`"y"`** sorts by **`y`**.

- <b id="scale">scale</b>

```typescript
  (points: Point[], scaleFactorX: number, scaleFactorY: number) => Point[]
```

Returns the scaled points.

- <b id="inline">inLine</b>

```typescript
interface Line {
    start: Point;
    end: Point;
}

(point: Point, line: Line) => boolean;
```

Returns boolean value indicating whether or not the given coordinates are on line defined by two other points.

- <b id="cross">cross</b>

```typescript
(line1: Line, line2: Line) => boolean;
```

Returns boolean value indicating if two lines each defined by two points intersect.

- <b id="move">move</b>

```typescript
(point: Point, xStep: number, yStep: number) => Point;
```

Returns a point of with the new coordinates.

- <b id="lerp">lerp</b>

```typescript
(point1: Point, point2: Point, t: number) => Point;
```

Returns the linearly interpolated point between **`point1`** and **`point2`** at parameter **`t`** (0 = `point1`, 1 = `point2`, 0.5 = midpoint). Values of `t` outside `[0, 1]` extrapolate beyond the two points.

- <b id="square">square</b>

```typescript
 (point: Point, size: number, [direction]: "left" | "right" | "down" | "up" ) => Point[]
```

Returns an array of points representing a shape of square.Takes four parameters: starting coordinates (x and y), size of square side, and direction which should be one of the values "left", "right", "up",
"down".

- <b id="rectangle">rectangle(point, width, height, [direction])</b>

```typescript
 (point: Point, width: number, height: number, [direction]: "left" | "right" | "down" | "up" ) => Point[]
```

Returns vertices of a rectangle starting at **`point`**, advancing **`width`** then **`height`** along the directional path (same directional convention as [square](#square)).

- <b id="triangle">triangle(point, size, [direction])</b>

```typescript
 (point: Point, size: number, [direction]: "left" | "right" | "down" | "up" ) => Point[]
```

Returns an array of points representing a shape of triangle.Takes same parameters as [square](#square) function.

- <b id="pentagon">pentagon(center, radius, [angle])</b>

```typescript
 (centerPoint: Point, radius: number, angle?: number) => Point[]
```

Returns vertices of a pentagon around **`centerPoint`** with **`radius`** and rotation **`angle`** (degrees, default **`0`**).

### Math

- <b id="degreestoradians">degreesToRadians</b>

```typescript
(degrees: number) => number;
```

Converts degrees to radians.

- <b id="radianstodegrees">radiansToDegrees</b>

```typescript
(radians: number) => number;
```

Converts radians to degrees.

- <b id="inrange">inRange</b>

```typescript
(number: number, min: number, max: number) => boolean;
```

Returns true if the given number is within the specified range.

- <b id="roundtoprecision">roundToPrecision</b>

```typescript
(number: number, precision: -100 | -10 | 0 | 10 | 100 | number) => number;
```

Rounds the number to the given precision.

- <b id="average">average</b>

```typescript
(numbers: number[]) => number;
```

Returns the average of all numbers in an array.

### Arrays

- <b id="intersection">intersection</b>

```typescript
 (arr1: any[], arr2: any[]) => any[]
```

Returns the array of intersection of two arrays.

- <b id="difference">difference</b>

```typescript
 (arr1: any[], arr2: any[]) => any[]
```

Returns the array of difference of two arrays.

- <b id="chunk">chunk</b>

```typescript
 (arr: any[], perArr: number) => any[][]
```

Returns an array splited into chunks based on elements count per chunk.

- <b id="removeDuplicates">removeDuplicates(arr)</b>

```typescript
 (arr: any[]) => any[]
```

Returns the array without duplicates.

- <b id="sample">sample</b>

```typescript
  (arr: any[], [size]: number[]) => any[]
```

Returns a random sample from an array with optional size argument for sampling length. If not specified, it returns only one element.

### Randomization

- <b id="randomnumber">randomNumber</b>

```typescript
(min: number, max: number) => number;
```

Returns a random number within the given range.

- <b id="randomboolean">randomBoolean</b>

```typescript
() => boolean;
```

Returns a random boolean value.

- <b id="uniqueid">uniqueId</b>

```typescript
  ([other ids]: string[]) => string
```

Returns a unique ID that's different from the provided IDs, or a random ID if no other IDs are given.
