Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/beige-pots-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bnidev/js-utils": minor
---

feat(math): add `distance` utility to calculate the distance between two points
5 changes: 5 additions & 0 deletions .changeset/four-news-stick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bnidev/js-utils": minor
---

feat(math): add `degreesToRadians` and `radiansToDegrees` utilities to convert between degrees and radians
5 changes: 5 additions & 0 deletions .changeset/full-toys-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bnidev/js-utils": minor
---

feat(math): add `haversineDistance` utility to calculate the distance between two geographic coordinates
5 changes: 5 additions & 0 deletions .changeset/two-bobcats-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bnidev/js-utils": minor
---

feat(math): add `pointInCircle` utility to check whether a point lies inside or on the boundary of a circle
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export * from './async'

export * from './dom'

export * from './math'

export * from './object'

export * from './string'
Expand Down
32 changes: 32 additions & 0 deletions src/math/__tests__/degreesToRadians.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expect, it } from 'vitest'
import { degreesToRadians } from '../degreesToRadians'

describe('degreesToRadians', () => {
it('converts 0 degrees to 0 radians', () => {
expect(degreesToRadians(0)).toBe(0)
})

it('converts 180 degrees to PI radians', () => {
expect(degreesToRadians(180)).toBeCloseTo(Math.PI)
})

it('converts 90 degrees to PI/2 radians', () => {
expect(degreesToRadians(90)).toBeCloseTo(Math.PI / 2)
})

it('converts negative degrees', () => {
expect(degreesToRadians(-180)).toBeCloseTo(-Math.PI)
})

it('throws if input is not a number', () => {
// @ts-expect-error
expect(() => degreesToRadians('foo')).toThrow(TypeError)
})

it('throws if input is undefined or null', () => {
// @ts-expect-error
expect(() => degreesToRadians(undefined)).toThrow(TypeError)
// @ts-expect-error
expect(() => degreesToRadians(null)).toThrow(TypeError)
})
})
24 changes: 24 additions & 0 deletions src/math/__tests__/distance.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { describe, expect, it } from 'vitest'
import { distance } from '../distance'

describe('distance', () => {
it('calculates distance between (0,0) and (3,4)', () => {
expect(distance(0, 0, 3, 4)).toBe(5)
})

it('returns 0 for the same point', () => {
expect(distance(1, 1, 1, 1)).toBe(0)
})

it('calculates distance with negative coordinates', () => {
expect(distance(-1, -1, 2, 3)).toBe(5)
})

it('calculates distance for horizontal line', () => {
expect(distance(2, 5, 7, 5)).toBe(5)
})

it('calculates distance for vertical line', () => {
expect(distance(3, 2, 3, 7)).toBe(5)
})
})
84 changes: 84 additions & 0 deletions src/math/__tests__/haversineDistance.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { describe, expect, it } from 'vitest'
import { haversineDistance } from '../haversineDistance'

describe('haversineDistance', () => {
it('returns 0 for identical points', () => {
expect(haversineDistance(10, 20, 10, 20)).toBe(0)
})

it('calculates distance in meters', () => {
const dist = haversineDistance(
52.2296756,
21.0122287,
41.89193,
12.51133,
'meters'
)
expect(dist).toBeGreaterThan(1_200_000)
expect(dist).toBeLessThan(1_250_000)
})

it('calculates distance in kilometers', () => {
const dist = haversineDistance(
52.2296756,
21.0122287,
41.89193,
12.51133,
'kilometers'
)
expect(dist).toBeGreaterThan(1220)
expect(dist).toBeLessThan(1230)
})

it('calculates distance in miles', () => {
const dist = haversineDistance(
52.2296756,
21.0122287,
41.89193,
12.51133,
'miles'
)
expect(dist).toBeGreaterThan(750)
expect(dist).toBeLessThan(770)
})

it('calculates distance in yards', () => {
const dist = haversineDistance(
52.2296756,
21.0122287,
41.89193,
12.51133,
'yards'
)
expect(dist).toBeGreaterThan(1_320_000)
expect(dist).toBeLessThan(1_340_000)
})

it('does not throws for valid unit', () => {
expect(() => haversineDistance(0, 0, 1, 1, 'miles')).not.toThrow()
expect(() => haversineDistance(0, 0, 1, 1, 'kilometers')).not.toThrow()
expect(() => haversineDistance(0, 0, 1, 1, 'meters')).not.toThrow()
expect(() => haversineDistance(0, 0, 1, 1, 'yards')).not.toThrow()
})

it('throws for invalid unit', () => {
// @ts-expect-error
expect(() => haversineDistance(0, 0, 1, 1, 'feet')).toThrow(TypeError)
})

it('throws for missing coordinates', () => {
// @ts-expect-error
expect(() => haversineDistance(undefined, 0, 1, 1)).toThrow()
})

it('throws when coordinates are not numbers', () => {
// @ts-expect-error
expect(() => haversineDistance('a', 0, 1, 1)).toThrow()
// @ts-expect-error
expect(() => haversineDistance(0, 'b', 1, 1)).toThrow()
// @ts-expect-error
expect(() => haversineDistance(0, 0, 'c', 1)).toThrow()
// @ts-expect-error
expect(() => haversineDistance(0, 0, 1, 'd')).toThrow()
})
})
31 changes: 31 additions & 0 deletions src/math/__tests__/pointInCircle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { describe, expect, it } from 'vitest'
import { pointInCircle } from '../pointInCircle'

describe('pointInCircle', () => {
it('returns true for a point inside the circle', () => {
expect(pointInCircle(1, 1, 0, 0, 2)).toBe(true)
})

it('returns true for a point on the boundary of the circle', () => {
expect(pointInCircle(2, 0, 0, 0, 2)).toBe(true)
})

it('returns false for a point outside the circle', () => {
expect(pointInCircle(3, 0, 0, 0, 2)).toBe(false)
})

it('works with negative coordinates', () => {
expect(pointInCircle(-1, -1, 0, 0, 2)).toBe(true)
expect(pointInCircle(-3, 0, 0, 0, 2)).toBe(false)
})

it('throws if any parameter is missing', () => {
// @ts-expect-error
expect(() => pointInCircle(1, 1, 0, 0)).toThrow(TypeError)
})

it('throws if any parameter is not a number', () => {
// @ts-expect-error
expect(() => pointInCircle('a', 1, 0, 0, 2)).toThrow(TypeError)
})
})
32 changes: 32 additions & 0 deletions src/math/__tests__/radiansToDegrees.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expect, it } from 'vitest'
import { radiansToDegrees } from '../radiansToDegrees'

describe('radiansToDegrees', () => {
it('converts 0 radians to 0 degrees', () => {
expect(radiansToDegrees(0)).toBe(0)
})

it('converts PI radians to 180 degrees', () => {
expect(radiansToDegrees(Math.PI)).toBe(180)
})

it('converts PI/2 radians to 90 degrees', () => {
expect(radiansToDegrees(Math.PI / 2)).toBe(90)
})

it('converts negative radians', () => {
expect(radiansToDegrees(-Math.PI)).toBe(-180)
})

it('throws if input is not a number', () => {
// @ts-expect-error
expect(() => radiansToDegrees('foo')).toThrow(TypeError)
})

it('throws if input is undefined or null', () => {
// @ts-expect-error
expect(() => radiansToDegrees(undefined)).toThrow(TypeError)
// @ts-expect-error
expect(() => radiansToDegrees(null)).toThrow(TypeError)
})
})
28 changes: 28 additions & 0 deletions src/math/degreesToRadians.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Converts degrees to radians.
* @param degrees - The angle in degrees.
* @returns The angle in radians.
*
* @category Math
*
* @example Imports
* ```ts
* // ES Module
* import { degreesToRadians } from '@bnidev/js-utils'
*
* // CommonJS
* const { degreesToRadians } = require('@bnidev/js-utils')
* ```
*
* @example Usage
* ```ts
* const radians = degreesToRadians(180) // radians will be π (approximately 3.14159)
* ```
*/
export function degreesToRadians(degrees: number): number {
if (typeof degrees !== 'number') {
throw new TypeError('Input must be a number')
}

return degrees * (Math.PI / 180)
}
34 changes: 34 additions & 0 deletions src/math/distance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Calculates the Euclidean distance between two points in a 2D space.
* @param x1 - The x-coordinate of the first point.
* @param y1 - The y-coordinate of the first point.
* @param x2 - The x-coordinate of the second point.
* @param y2 - The y-coordinate of the second point.
* @returns The distance between the two points.
*
* @category Math
*
* @example Imports
* ```ts
* // ES Module
* import { distance } from '@bnidev/js-utils'
*
* // CommonJS
* const { distance } = require('@bnidev/js-utils')
* ```
*
* @example Usage
* ```ts
* const dist = distance(0, 0, 3, 4) // dist will be 5
* ```
*/
export function distance(
x1: number,
y1: number,
x2: number,
y2: number
): number {
const dx = x2 - x1
const dy = y2 - y1
return Math.sqrt(dx * dx + dy * dy)
}
Loading