npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@abd2oo1/math-engine

v1.1.4

Published

A lightweight 2D math engine for spatial applications such as seating chart libraries, canvas editors, and game engines. Built with TypeScript, zero dependencies.

Readme

math-engine-2d

A lightweight 2D math engine for spatial applications such as seating chart libraries, canvas editors, and game engines. Built with TypeScript, zero dependencies.


Installation

npm install math-engine-2d

Usage

import math from 'math-engine-2d'

// use any module
math.vector.add({ x: 1, y: 2 }, { x: 3, y: 4 }) // { x: 4, y: 6 }
math.circle.pointInsideCircle({ x: 1, y: 1 }, { x: 0, y: 0, r: 5 }) // true

Modules


vector

2D vector operations. All functions take { x, y } objects and return new vectors without mutating the input.

add(a, b)

Adds two vectors together.

math.vector.add({ x: 1, y: 2 }, { x: 3, y: 4 })
// { x: 4, y: 6 }

subtract(a, b)

Subtracts vector b from vector a.

math.vector.subtract({ x: 5, y: 5 }, { x: 2, y: 3 })
// { x: 3, y: 2 }

scale(v, t)

Scales a vector by a scalar value.

math.vector.scale({ x: 2, y: 3 }, 2)
// { x: 4, y: 6 }

dot(a, b)

Returns the dot product of two vectors. Used for finding the angle between two rows or projecting a point onto a line.

math.vector.dot({ x: 1, y: 0 }, { x: 0, y: 1 })
// 0

magnitude(v)

Returns the length of a vector.

math.vector.magnitude({ x: 3, y: 4 })
// 5

normalize(v)

Returns a unit vector (length = 1) in the same direction.

math.vector.normalize({ x: 3, y: 4 })
// { x: 0.6, y: 0.8 }

lerp(a, b, t)

Linearly interpolates between two vectors by t (0 to 1). Used for placing seats evenly along a straight row.

math.vector.lerp({ x: 0, y: 0 }, { x: 100, y: 0 }, 0.5)
// { x: 50, y: 0 }

distance(a, b)

Returns the distance between two points.

math.vector.distance({ x: 0, y: 0 }, { x: 3, y: 4 })
// 5

perpendicular(v)

Returns a vector perpendicular to the given vector. Used for row orientation and normal directions.

math.vector.perpendicular({ x: 1, y: 0 })
// { x: 0, y: 1 }

circle

Operations on circles { x, y, r } where x, y is the center and r is the radius.

pointInsideCircle(point, circle)

Checks if a point is inside a circle. Used for seat hit testing.

math.circle.pointInsideCircle({ x: 1, y: 1 }, { x: 0, y: 0, r: 5 })
// true

pointOutsideCircle(point, circle)

Checks if a point is outside a circle.

math.circle.pointOutsideCircle({ x: 10, y: 10 }, { x: 0, y: 0, r: 5 })
// true

pointOnCircle(point, circle)

Checks if a point lies exactly on the circle boundary, with floating point tolerance.

math.circle.pointOnCircle({ x: 5, y: 0 }, { x: 0, y: 0, r: 5 })
// true

distanceBetweenCircles(a, b)

Returns the distance between two circle centers.

math.circle.distanceBetweenCircles({ x: 0, y: 0, r: 5 }, { x: 10, y: 0, r: 5 })
// 10

circlesOverlap(a, b)

Checks if two circles overlap. Used for detecting if two seats are too close.

math.circle.circlesOverlap({ x: 0, y: 0, r: 5 }, { x: 8, y: 0, r: 5 })
// true

pointOnCircleEdge(circle, angle)

Returns the point on the circle edge at a given angle in radians. Used for placing seats on a curved row.

math.circle.pointOnCircleEdge({ x: 0, y: 0, r: 100 }, 0)
// { x: 100, y: 0 }

math.circle.pointOnCircleEdge({ x: 0, y: 0, r: 100 }, Math.PI / 2)
// { x: 0, y: 100 }

circleBounds(circle)

Returns the bounding box of a circle. Used for culling — skipping seats that are off screen.

math.circle.circleBounds({ x: 50, y: 50, r: 20 })
// { left: 30, top: 30, right: 70, bottom: 70 }

rectangle

Operations on rectangles { x, y, width, height } where x, y is the top-left corner.

pointInsideRectangle(point, rect)

Checks if a point is inside a rectangle. Used for drag selection.

math.rectangle.pointInsideRectangle({ x: 50, y: 50 }, { x: 0, y: 0, width: 100, height: 100 })
// true

rectangleContainsRectangle(a, b)

Checks if rectangle a fully contains rectangle b. Used for checking if a section is inside a pricing zone.

math.rectangle.rectangleContainsRectangle(
  { x: 0, y: 0, width: 200, height: 200 },
  { x: 50, y: 50, width: 50, height: 50 }
)
// true

rectangleIntersectsCircle(rect, circle)

Checks if a rectangle overlaps or touches a circle. Used for drag selection — does the box touch this seat?

math.rectangle.rectangleIntersectsCircle(
  { x: 0, y: 0, width: 100, height: 100 },
  { x: 110, y: 50, r: 20 }
)
// true

rectangleCenter(rect)

Returns the center point of a rectangle. Used for rotating a section around its center.

math.rectangle.rectangleCenter({ x: 0, y: 0, width: 100, height: 100 })
// { x: 50, y: 50 }

rectangleBounds(rect)

Returns the bounding box of a rectangle.

math.rectangle.rectangleBounds({ x: 10, y: 20, width: 100, height: 50 })
// { left: 10, top: 20, right: 110, bottom: 70 }

bound

Axis-Aligned Bounding Box (AABB) operations. All bounds use { left, top, right, bottom }.

createBounds(x, y, width, height)

Creates a bounding box from position and size.

math.bound.createBounds(10, 20, 100, 50)
// { left: 10, top: 20, right: 110, bottom: 70 }

pointInBounds(point, bounds)

Checks if a point is inside a bounding box. Used for drag selection.

math.bound.pointInBounds({ x: 50, y: 50 }, { left: 0, top: 0, right: 100, bottom: 100 })
// true

boundsIntersect(a, b)

Checks if two bounding boxes overlap. Used for section collision detection.

math.bound.boundsIntersect(
  { left: 0, top: 0, right: 100, bottom: 100 },
  { left: 50, top: 50, right: 150, bottom: 150 }
)
// true

expandBounds(bounds, padding)

Expands a bounding box by a padding value on all sides.

math.bound.expandBounds({ left: 10, top: 10, right: 90, bottom: 90 }, 10)
// { left: 0, top: 0, right: 100, bottom: 100 }

mergeBounds(a, b)

Merges two bounding boxes into one that contains both. Used for wrapping an entire section.

math.bound.mergeBounds(
  { left: 0, top: 0, right: 50, bottom: 50 },
  { left: 50, top: 50, right: 100, bottom: 100 }
)
// { left: 0, top: 0, right: 100, bottom: 100 }

boundsCenter(bounds)

Returns the center point of a bounding box.

math.bound.boundsCenter({ left: 0, top: 0, right: 100, bottom: 100 })
// { x: 50, y: 50 }

boundsSize(bounds)

Returns the width and height of a bounding box.

math.bound.boundsSize({ left: 0, top: 0, right: 100, bottom: 50 })
// { width: 100, height: 50 }

moveBounds(bounds, dx, dy)

Moves a bounding box by dx and dy.

math.bound.moveBounds({ left: 0, top: 0, right: 100, bottom: 100 }, 50, 50)
// { left: 50, top: 50, right: 150, bottom: 150 }

selectionBounds(point1, point2)

Creates a bounding box from two points. Used for drag selection on canvas.

math.bound.selectionBounds({ x: 20, y: 80 }, { x: 80, y: 20 })
// { left: 20, top: 20, right: 80, bottom: 80 }

boundsContainsBounds(a, b)

Checks if bounding box a fully contains bounding box b.

math.bound.boundsContainsBounds(
  { left: 0, top: 0, right: 200, bottom: 200 },
  { left: 50, top: 50, right: 100, bottom: 100 }
)
// true

boundsFromPoints(points)

Computes a bounding box that wraps all given points. Used for wrapping a row of seats.

math.bound.boundsFromPoints([
  { x: 10, y: 20 },
  { x: 80, y: 5 },
  { x: 50, y: 90 }
])
// { left: 10, top: 5, right: 80, bottom: 90 }

intersect

Intersection tests between shapes.

pointInRect(point, bounds)

Checks if a point is inside a bounding box.

math.intersect.pointInRect({ x: 50, y: 50 }, { left: 0, top: 0, right: 100, bottom: 100 })
// true

rectIntersect(a, b)

Checks if two bounding boxes overlap.

math.intersect.rectIntersect(
  { left: 0, top: 0, right: 100, bottom: 100 },
  { left: 80, top: 80, right: 200, bottom: 200 }
)
// true

lineIntersect(a, b, c, d)

Checks if two line segments intersect. Used for detecting if two row lines cross.

math.intersect.lineIntersect(
  { x: 0, y: 0 }, { x: 100, y: 100 },
  { x: 100, y: 0 }, { x: 0,   y: 100 }
)
// true

circleIntersect(a, b)

Checks if two circles overlap. Used for detecting if two seats are too close.

math.intersect.circleIntersect(
  { x: 0, y: 0, r: 10 },
  { x: 15, y: 0, r: 10 }
)
// true

pointInCircle(point, circle)

Checks if a point is inside a circle. Used for seat hit testing.

math.intersect.pointInCircle({ x: 3, y: 3 }, { x: 0, y: 0, r: 10 })
// true

circleIntersectRect(circle, bounds)

Checks if a circle overlaps or touches a rectangle. Used for partial seat selection.

math.intersect.circleIntersectRect(
  { x: 105, y: 50, r: 10 },
  { left: 0, top: 0, right: 100, bottom: 100 }
)
// true

pointToSegmentDistance(point, a, b)

Returns the shortest distance from a point to a line segment. Used for snapping a seat to a row line.

math.intersect.pointToSegmentDistance(
  { x: 50, y: 50 },
  { x: 0,  y: 0  },
  { x: 100, y: 0 }
)
// 50

pointInPolygon(point, polygon)

Checks if a point is inside a polygon using ray casting. Used for pricing zones.

const zone = [
  { x: 0,   y: 0   },
  { x: 100, y: 0   },
  { x: 100, y: 100 },
  { x: 0,   y: 100 }
]
math.intersect.pointInPolygon({ x: 50, y: 50 }, zone)
// true

trigonometry

Trigonometry utilities for angles, arcs, and rotations. Angles are in radians unless stated otherwise.

degToRad(deg)

Converts degrees to radians.

math.trigonometry.degToRad(180)
// 3.14159...

radToDeg(rad)

Converts radians to degrees.

math.trigonometry.radToDeg(Math.PI)
// 180

pointOnCircle(center, radius, angle)

Returns the point on a circle at a given angle. Used for placing seats on a curved row.

math.trigonometry.pointOnCircle({ x: 0, y: 0 }, 100, 0)
// { x: 100, y: 0 }

angleBetweenTwoPoints(point1, point2)

Returns the angle between two points in radians using atan2.

math.trigonometry.angleBetweenTwoPoints({ x: 0, y: 0 }, { x: 100, y: 0 })
// 0

rotatePoint(point, origin, angle)

Rotates a point around an origin by a given angle. Used for rotating entire sections.

math.trigonometry.rotatePoint(
  { x: 100, y: 0 },
  { cx: 0, cy: 0 },
  Math.PI / 2
)
// { x: 0, y: 100 }

distanceBetweenTwoPoints(point1, point2)

Returns the distance between two points.

math.trigonometry.distanceBetweenTwoPoints({ x: 0, y: 0 }, { x: 3, y: 4 })
// 5

normalizeAngle(angle)

Keeps an angle within the range [0, 2π]. Used to prevent angle overflow when looping around arcs.

math.trigonometry.normalizeAngle(7)
// 0.716...

arcAngleStep(seatWidth, radius)

Returns the angle step needed to space seats evenly on an arc. Used for curved row layout.

math.trigonometry.arcAngleStep(30, 200)
// 0.15

angleBetweenTwoVectors(a, b)

Returns the angle between two vectors in radians. Used for finding row orientation.

math.trigonometry.angleBetweenTwoVectors({ x: 1, y: 0 }, { x: 0, y: 1 })
// 1.5707... (90°)

angle

Additional angle utilities.

degreesToRadians(deg)

Converts degrees to radians.

math.angle.degreesToRadians(90)
// 1.5707...

radiansToDegrees(rad)

Converts radians to degrees.

math.angle.radiansToDegrees(Math.PI)
// 180

arcAngleStep(seatWidth, radius)

Returns the angle step for even seat spacing on a curved row.

math.angle.arcAngleStep(30, 200)
// 0.15

angleBetweenTwoAngles(a, b)

Returns the shortest angular distance between two angles.

math.angle.angleBetweenTwoAngles(0.1, 5.9)
// -0.483...

rotateApointAroundAnotherPoint(origin, point, angle)

Rotates a point around an origin point by a given angle.

math.angle.rotateApointAroundAnotherPoint(
  { x: 0, y: 0 },
  { x: 100, y: 0 },
  Math.PI / 2
)
// { x: 0, y: 100 }

utils

General utility functions.

clamp(value, min, max)

Clamps a value between a minimum and maximum. Returns min if below, max if above, otherwise the value itself.

math.utils.clamp(150, 0, 100)  // 100
math.utils.clamp(-10, 0, 100)  // 0
math.utils.clamp(50,  0, 100)  // 50

lrep(start, end, percentage)

Linearly interpolates between two numbers by a percentage (0 to 1).

math.utils.lrep(0, 100, 0.5)
// 50

lrepBetweenTwoPoints(startPoint, endPoint, percentage)

Linearly interpolates between two 2D points by a percentage (0 to 1).

math.utils.lrepBetweenTwoPoints({ x: 0, y: 0 }, { x: 100, y: 100 }, 0.5)
// { x: 50, y: 50 }

Example — Curved Row Layout

import math from 'math-engine-2d'

const center = { x: 300, y: 300 }
const radius = 200
const seatWidth = 30
const totalSeats = 10

const step = math.trigonometry.arcAngleStep(seatWidth, radius)

const seats = []
for (let i = 0; i < totalSeats; i++) {
  const angle = i * step
  const position = math.trigonometry.pointOnCircle(center, radius, angle)
  seats.push(position)
}

Example — Seat Hit Testing

import math from 'math-engine-2d'

const seat = { x: 100, y: 100, r: 15 }
const mouseClick = { x: 105, y: 108 }

if (math.circle.pointInsideCircle(mouseClick, seat)) {
  console.log('seat selected')
}

Example — Drag Selection

import math from 'math-engine-2d'

const selectionStart = { x: 50,  y: 50  }
const selectionEnd   = { x: 200, y: 200 }
const box = math.bound.selectionBounds(selectionStart, selectionEnd)

const seats = [
  { x: 100, y: 100, r: 15 },
  { x: 300, y: 300, r: 15 },
]

const selected = seats.filter(seat =>
  math.intersect.circleIntersectRect(seat, box)
)

License

MIT