From 06771f6ca00040c7d6bc0b2b7c2ecb016027375a Mon Sep 17 00:00:00 2001 From: saahil-mahato Date: Tue, 1 Oct 2024 13:21:38 +0545 Subject: [PATCH] feat: add geometry ellipse --- Geometry/Ellipse.js | 95 +++++++++++++++++++++++++++++++++++ Geometry/Test/Ellipse.test.js | 68 +++++++++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 Geometry/Ellipse.js create mode 100644 Geometry/Test/Ellipse.test.js diff --git a/Geometry/Ellipse.js b/Geometry/Ellipse.js new file mode 100644 index 0000000000..7c131fa20b --- /dev/null +++ b/Geometry/Ellipse.js @@ -0,0 +1,95 @@ +/** + * This class represents an Ellipse and provides methods to calculate its area and circumference. + * @see {@link https://en.wikipedia.org/wiki/Ellipse} + * @class + */ +export default class Ellipse { + /** @private */ + #radiusX + + /** @private */ + #radiusY + + /** + * Creates an ellipse instance. + * @constructor + * @param {number} radiusX - The radius along the x-axis. + * @param {number} radiusY - The radius along the y-axis. + * @throws {Error} Will throw an error if any dimension is invalid. + */ + constructor(radiusX, radiusY) { + this.#validateDimension(radiusX, 'radiusX') + this.#validateDimension(radiusY, 'radiusY') + + this.#radiusX = radiusX + this.#radiusY = radiusY + } + + /** + * Validates that a dimension is a positive number. + * @private + * @param {number} value - The value to validate. + * @param {string} name - The name of the dimension (for error reporting). + * @throws {Error} Will throw an error if the value is not a positive number. + */ + #validateDimension(value, name) { + if (typeof value !== 'number' || isNaN(value) || value <= 0) { + throw new Error(`${name} must be a positive number.`) + } + } + + /** + * Calculates the area of the ellipse. + * @public + * @returns {number} The area of the ellipse. + */ + area() { + return Math.PI * this.#radiusX * this.#radiusY + } + + /** + * Calculates the circumference of the ellipse using Ramanujan's approximation. + * @public + * @returns {number} The circumference of the ellipse. + */ + circumference() { + return ( + Math.PI * + (3 * (this.#radiusX + this.#radiusY) - + Math.sqrt( + (3 * this.#radiusX + this.#radiusY) * + (this.#radiusX + 3 * this.#radiusY) + )) + ) + } + + /** + * Returns a string representation of the ellipse. + * @public + * @returns {string} A string describing the ellipse's dimensions and area/circumference. + */ + toString() { + return ( + `Ellipse: radiusX = ${this.#radiusX}, radiusY = ${this.#radiusY}, ` + + `area = ${this.area()}, circumference = ${this.circumference()}` + ) + } + + /** + * Gets the radius along the x-axis. + * @public + * @returns {number} The radius along the x-axis. + */ + get radiusX() { + return this.#radiusX + } + + /** + * Gets the radius along the y-axis. + * @public + * @returns {number} The radius along the y-axis. + */ + get radiusY() { + return this.#radiusY + } +} diff --git a/Geometry/Test/Ellipse.test.js b/Geometry/Test/Ellipse.test.js new file mode 100644 index 0000000000..9c98adc99e --- /dev/null +++ b/Geometry/Test/Ellipse.test.js @@ -0,0 +1,68 @@ +import Ellipse from '../Ellipse' + +describe('Ellipse', () => { + describe('Constructor', () => { + test('creates an ellipse with valid dimensions', () => { + const ellipse = new Ellipse(5, 10) + expect(ellipse).toBeInstanceOf(Ellipse) + expect(ellipse.radiusX).toBe(5) + expect(ellipse.radiusY).toBe(10) + }) + + test('throws an error if any dimension is invalid', () => { + expect(() => new Ellipse(-5, 10)).toThrow( + 'radiusX must be a positive number.' + ) + expect(() => new Ellipse(5, -10)).toThrow( + 'radiusY must be a positive number.' + ) + expect(() => new Ellipse(NaN, 10)).toThrow( + 'radiusX must be a positive number.' + ) + expect(() => new Ellipse(5, undefined)).toThrow( + 'radiusY must be a positive number.' + ) + }) + }) + + describe('Area Calculation', () => { + test('calculates area correctly', () => { + const ellipse = new Ellipse(5, 10) + expect(ellipse.area()).toBeCloseTo(Math.PI * 5 * 10) // Area = π * rX * rY + }) + }) + + describe('Circumference Calculation', () => { + test('calculates circumference correctly', () => { + const ellipse = new Ellipse(5, 10) + expect(ellipse.circumference()).toBeCloseTo( + Math.PI * (3 * (5 + 10) - Math.sqrt((3 * 5 + 10) * (5 + 3 * 10))) + ) // Circumference using Ramanujan's approximation + }) + }) + + describe('Getters', () => { + test('radiusX getter returns correct value', () => { + const ellipse = new Ellipse(5, 10) + expect(ellipse.radiusX).toBe(5) + }) + + test('radiusY getter returns correct value', () => { + const ellipse = new Ellipse(5, 10) + expect(ellipse.radiusY).toBe(10) + }) + }) + + describe('String Representation', () => { + test('returns correct string representation', () => { + const ellipse = new Ellipse(5, 10) + expect(ellipse.toString()).toBe( + `Ellipse: radiusX = 5, radiusY = 10, area = ${ + Math.PI * 5 * 10 + }, circumference = ${ + Math.PI * (3 * (5 + 10) - Math.sqrt((3 * 5 + 10) * (5 + 3 * 10))) + }` + ) + }) + }) +})