Skip to content

Commit 06771f6

Browse files
committed
feat: add geometry ellipse
1 parent 9010481 commit 06771f6

File tree

2 files changed

+163
-0
lines changed

2 files changed

+163
-0
lines changed

Geometry/Ellipse.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* This class represents an Ellipse and provides methods to calculate its area and circumference.
3+
* @see {@link https://en.wikipedia.org/wiki/Ellipse}
4+
* @class
5+
*/
6+
export default class Ellipse {
7+
/** @private */
8+
#radiusX
9+
10+
/** @private */
11+
#radiusY
12+
13+
/**
14+
* Creates an ellipse instance.
15+
* @constructor
16+
* @param {number} radiusX - The radius along the x-axis.
17+
* @param {number} radiusY - The radius along the y-axis.
18+
* @throws {Error} Will throw an error if any dimension is invalid.
19+
*/
20+
constructor(radiusX, radiusY) {
21+
this.#validateDimension(radiusX, 'radiusX')
22+
this.#validateDimension(radiusY, 'radiusY')
23+
24+
this.#radiusX = radiusX
25+
this.#radiusY = radiusY
26+
}
27+
28+
/**
29+
* Validates that a dimension is a positive number.
30+
* @private
31+
* @param {number} value - The value to validate.
32+
* @param {string} name - The name of the dimension (for error reporting).
33+
* @throws {Error} Will throw an error if the value is not a positive number.
34+
*/
35+
#validateDimension(value, name) {
36+
if (typeof value !== 'number' || isNaN(value) || value <= 0) {
37+
throw new Error(`${name} must be a positive number.`)
38+
}
39+
}
40+
41+
/**
42+
* Calculates the area of the ellipse.
43+
* @public
44+
* @returns {number} The area of the ellipse.
45+
*/
46+
area() {
47+
return Math.PI * this.#radiusX * this.#radiusY
48+
}
49+
50+
/**
51+
* Calculates the circumference of the ellipse using Ramanujan's approximation.
52+
* @public
53+
* @returns {number} The circumference of the ellipse.
54+
*/
55+
circumference() {
56+
return (
57+
Math.PI *
58+
(3 * (this.#radiusX + this.#radiusY) -
59+
Math.sqrt(
60+
(3 * this.#radiusX + this.#radiusY) *
61+
(this.#radiusX + 3 * this.#radiusY)
62+
))
63+
)
64+
}
65+
66+
/**
67+
* Returns a string representation of the ellipse.
68+
* @public
69+
* @returns {string} A string describing the ellipse's dimensions and area/circumference.
70+
*/
71+
toString() {
72+
return (
73+
`Ellipse: radiusX = ${this.#radiusX}, radiusY = ${this.#radiusY}, ` +
74+
`area = ${this.area()}, circumference = ${this.circumference()}`
75+
)
76+
}
77+
78+
/**
79+
* Gets the radius along the x-axis.
80+
* @public
81+
* @returns {number} The radius along the x-axis.
82+
*/
83+
get radiusX() {
84+
return this.#radiusX
85+
}
86+
87+
/**
88+
* Gets the radius along the y-axis.
89+
* @public
90+
* @returns {number} The radius along the y-axis.
91+
*/
92+
get radiusY() {
93+
return this.#radiusY
94+
}
95+
}

Geometry/Test/Ellipse.test.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import Ellipse from '../Ellipse'
2+
3+
describe('Ellipse', () => {
4+
describe('Constructor', () => {
5+
test('creates an ellipse with valid dimensions', () => {
6+
const ellipse = new Ellipse(5, 10)
7+
expect(ellipse).toBeInstanceOf(Ellipse)
8+
expect(ellipse.radiusX).toBe(5)
9+
expect(ellipse.radiusY).toBe(10)
10+
})
11+
12+
test('throws an error if any dimension is invalid', () => {
13+
expect(() => new Ellipse(-5, 10)).toThrow(
14+
'radiusX must be a positive number.'
15+
)
16+
expect(() => new Ellipse(5, -10)).toThrow(
17+
'radiusY must be a positive number.'
18+
)
19+
expect(() => new Ellipse(NaN, 10)).toThrow(
20+
'radiusX must be a positive number.'
21+
)
22+
expect(() => new Ellipse(5, undefined)).toThrow(
23+
'radiusY must be a positive number.'
24+
)
25+
})
26+
})
27+
28+
describe('Area Calculation', () => {
29+
test('calculates area correctly', () => {
30+
const ellipse = new Ellipse(5, 10)
31+
expect(ellipse.area()).toBeCloseTo(Math.PI * 5 * 10) // Area = π * rX * rY
32+
})
33+
})
34+
35+
describe('Circumference Calculation', () => {
36+
test('calculates circumference correctly', () => {
37+
const ellipse = new Ellipse(5, 10)
38+
expect(ellipse.circumference()).toBeCloseTo(
39+
Math.PI * (3 * (5 + 10) - Math.sqrt((3 * 5 + 10) * (5 + 3 * 10)))
40+
) // Circumference using Ramanujan's approximation
41+
})
42+
})
43+
44+
describe('Getters', () => {
45+
test('radiusX getter returns correct value', () => {
46+
const ellipse = new Ellipse(5, 10)
47+
expect(ellipse.radiusX).toBe(5)
48+
})
49+
50+
test('radiusY getter returns correct value', () => {
51+
const ellipse = new Ellipse(5, 10)
52+
expect(ellipse.radiusY).toBe(10)
53+
})
54+
})
55+
56+
describe('String Representation', () => {
57+
test('returns correct string representation', () => {
58+
const ellipse = new Ellipse(5, 10)
59+
expect(ellipse.toString()).toBe(
60+
`Ellipse: radiusX = 5, radiusY = 10, area = ${
61+
Math.PI * 5 * 10
62+
}, circumference = ${
63+
Math.PI * (3 * (5 + 10) - Math.sqrt((3 * 5 + 10) * (5 + 3 * 10)))
64+
}`
65+
)
66+
})
67+
})
68+
})

0 commit comments

Comments
 (0)