Skip to content

Instantly share code, notes, and snippets.

@aarona
Created June 14, 2022 05:16
Show Gist options
  • Save aarona/17e273f0e23235de1612959ad36d033d to your computer and use it in GitHub Desktop.
Save aarona/17e273f0e23235de1612959ad36d033d to your computer and use it in GitHub Desktop.
Object oriented example using Typscript
export interface Shape {
area(): number
perimeter(): number
}
export interface Describable {
describe(): string
}
type Coordinate = [number, number]
export abstract class DescribableShape implements Describable, Shape {
abstract area(): number
abstract perimeter(): number
abstract describe(): string
}
export class Rectangle implements DescribableShape {
constructor(readonly height: number, readonly length: number) {
if(height <= 0 || length <= 0) throw new Error('Invalid Rectangle')
}
area(): number {
return this.height * this.length
}
perimeter(): number {
return 2 * (this.height + this.length)
}
describe(): string {
return `A Rectangle of length ${this.length} and height ${this.height}`
}
}
export class Square extends Rectangle {
constructor(readonly side: number) {
super(side, side)
}
describe(): string {
return `A Square with sides of length ${this.side}`
}
}
export class Circle implements DescribableShape {
constructor(readonly radius: number) {
if (radius <= 0) throw new Error('Invalid Circle')
}
// Area = Pr^2
area(): number {
return Math.PI * Math.pow(this.radius, 2)
}
// Perimeter = 2Pr
perimeter(): number {
return 2 * Math.PI * this.radius
}
describe(): string {
return `A Circle of radius ${this.radius}`
}
}
export class Triangle implements DescribableShape {
constructor(private sides: [number, number, number]) {
if(!this.validSides()) throw new Error('Invalid Triangle')
}
validSides(): boolean {
const { sides } = this
const side1Valid = sides[0] < sides[1] + sides[2]
const side2Valid = sides[1] < sides[0] + sides[2]
const side3Valid = sides[2] < sides[0] + sides[1]
return side1Valid && side2Valid && side3Valid
}
area(): number {
const { sides } = this
const p = this.perimeter() / 2
const [p1, p2, p3] = [p - sides[0], p - sides[1], p - sides[2]]
return Math.sqrt(p * p1 * p2 * p3)
}
perimeter(): number {
return this.sides.reduce((result, currentValue) => result + currentValue, 0)
}
describe(): string {
const { sides } = this
return `A Triagle of side lengths ${sides[0]}, ${sides[1]} and ${sides[2]}`
}
}
export class RightAngleTriangle extends Triangle {
constructor(private base: number, private height: number) {
const hypotentuse = Math.sqrt(base**2 + height**2)
super([base, height, hypotentuse])
}
area(): number {
return 0.5 * this.base * this.height
}
}
export class RegularPolygon implements DescribableShape {
constructor(readonly numberOfSides: number, readonly lengthOfSides: number) {
if (numberOfSides < 3 || lengthOfSides <= 0) throw new Error('Invalid Polygon')
}
area(): number {
return this.numberOfSides * this.lengthOfSides**2 * (1 / Math.tan(Math.PI / this.numberOfSides)) / 4
}
perimeter(): number {
return this.lengthOfSides * this.numberOfSides
}
describe(): string {
const { lengthOfSides, numberOfSides } = this
const presetLabels = {
3: 'Triangle',
4: 'Square',
5: 'Pentagon',
6: 'Hexagon',
7: 'Heptagon',
8: 'Octogon',
9: 'Nonagon',
10: 'Decagon',
12: 'Dodecagon'
}
const key = numberOfSides as keyof typeof presetLabels
const label =
presetLabels[key] ?
`${presetLabels[key]} with` :
`Regular Polygon with ${numberOfSides}`
return `A ${label} equal sides of lenth ${lengthOfSides}`
}
}
export class Polygon implements DescribableShape {
constructor(private coordinates: Coordinate[]) {
if(coordinates.length < 3) throw new Error('invalid Polygon')
}
area(): number {
const { coordinates } = this
const numOfCoordinates = coordinates.length
let areaSum = 0
for (let idx = 0; idx < numOfCoordinates; idx++) {
const nextIdx = idx + 1 === numOfCoordinates ? 0 : idx + 1
const currentCoordinate = coordinates[idx]
const nextCoordinate = coordinates[nextIdx]
areaSum += this.calcAreaComponent(currentCoordinate, nextCoordinate)
}
return Math.abs(areaSum / 2)
}
perimeter(): number {
const { coordinates } = this
const numOfCoordinates = coordinates.length
let perimeter = 0
for (let idx = 0; idx < numOfCoordinates; idx++) {
const nextIdx = idx + 1 === numOfCoordinates ? 0 : idx + 1
const currentCoordinate = coordinates[idx]
const nextCoordinate = coordinates[nextIdx]
perimeter += this.distance(currentCoordinate, nextCoordinate)
}
return perimeter
}
describe(): string {
return `A Polygon with coordinates ${this.join()}`
}
private calcAreaComponent(a: Coordinate, b: Coordinate) {
const [x1, y1] = a
const [x2, y2] = b
return x1 * y2 - y1 * x2
}
private distance(a: Coordinate, b: Coordinate) {
const [x1, y1] = a
const [x2, y2] = b
return Math.sqrt((x1 - x2)**2 + (y1 - y2)**2)
}
private join() {
const coordinates = this.coordinates.slice()
const [x, y] = coordinates.pop() as Coordinate
const coordinatesDescription =
this.coordinates.map(coordinate =>
`[${coordinate[0]}, ${coordinate[1]}]`).join(', ')
return `${coordinatesDescription} and [${x}, ${y}]`
}
}
class ShapeCalculator {
constructor(private shapes: DescribableShape[]) {}
totalArea() {
return this.shapes.reduce((result, currentShape) => result + currentShape.area(), 0)
}
totalPerimeter() {
return this.shapes.reduce((result, currentShape) => result + currentShape.perimeter(), 0)
}
print() {
const s = this.shapes.map(shape => ({
description: shape.describe(),
area: shape.area(),
perimeter: shape.perimeter()
}))
console.table(s)
}
}
const shapes = [
new Circle(1),
new Square(2),
new RegularPolygon(100, 1),
new RegularPolygon(5, 2),
new Rectangle(2, 3),
new Triangle([1, 1, 1]),
new RightAngleTriangle(3, 4),
new Triangle([1.01, 99, 100]),
new RegularPolygon(11, 2),
new Polygon([[0, 0], [0, 1], [1, 1], [1, 0]]),
new Polygon([[0, 0], [0, 10], [1, 1], [10, 0]])
]
const radius = 10 / (Math.PI * 2)
const circles = [
new Circle(radius),
new RegularPolygon(10, 1),
new RegularPolygon(100, 0.1),
new RegularPolygon(1000, 0.01),
new RegularPolygon(100000, 0.0001),
]
const calc = new ShapeCalculator(shapes)
console.log('Total Area:', calc.totalArea());
console.log('Total Perimeter:', calc.totalPerimeter());
calc.print()
const calcCircles = new ShapeCalculator(circles)
calcCircles.print()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment