feat: Test running overhaul, switch to Prettier & reformat everything (#1407)

* chore: Switch to Node 20 + Vitest

* chore: migrate to vitest mock functions

* chore: code style (switch to prettier)

* test: re-enable long-running test

Seems the switch to Node 20 and Vitest has vastly improved the code's and / or the test's runtime!

see #1193

* chore: code style

* chore: fix failing tests

* Updated Documentation in README.md

* Update contribution guidelines to state usage of Prettier

* fix: set prettier printWidth back to 80

* chore: apply updated code style automatically

* fix: set prettier line endings to lf again

* chore: apply updated code style automatically

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com>
This commit is contained in:
Roland Hummel
2023-10-03 23:08:19 +02:00
committed by GitHub
parent 0ca18c2b2c
commit 86d333ee94
392 changed files with 5849 additions and 16622 deletions

View File

@ -1,4 +1,4 @@
function euclideanGCDRecursive (first, second) {
function euclideanGCDRecursive(first, second) {
/*
Calculates GCD of two numbers using Euclidean Recursive Algorithm
:param first: First number
@ -8,11 +8,11 @@ function euclideanGCDRecursive (first, second) {
if (second === 0) {
return first
} else {
return euclideanGCDRecursive(second, (first % second))
return euclideanGCDRecursive(second, first % second)
}
}
function euclideanGCDIterative (first, second) {
function euclideanGCDIterative(first, second) {
/*
Calculates GCD of two numbers using Euclidean Iterative Algorithm
:param first: First number

View File

@ -1,103 +1,132 @@
/**
* Flood fill.
*
* Flood fill, also called seed fill, is an algorithm that determines and alters the area connected to a given node in a
* multi-dimensional array with some matching attribute. It is used in the "bucket" fill tool of paint programs to fill
* connected, similarly-colored areas with a different color.
*
* (description adapted from https://en.wikipedia.org/wiki/Flood_fill)
* @see https://www.techiedelight.com/flood-fill-algorithm/
*/
const neighbors = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]]
/**
* Implements the flood fill algorithm through a breadth-first approach using a queue.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
export function breadthFirstSearch (rgbData, location, targetColor, replacementColor) {
if (location[0] < 0 ||
location[0] >= rgbData.length ||
location[1] < 0 ||
location[1] >= rgbData[0].length) {
throw new Error('location should point to a pixel within the rgbData')
}
const queue = []
queue.push(location)
while (queue.length > 0) {
breadthFirstFill(rgbData, location, targetColor, replacementColor, queue)
}
}
/**
* Implements the flood fill algorithm through a depth-first approach using recursion.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
export function depthFirstSearch (rgbData, location, targetColor, replacementColor) {
if (location[0] < 0 ||
location[0] >= rgbData.length ||
location[1] < 0 ||
location[1] >= rgbData[0].length) {
throw new Error('location should point to a pixel within the rgbData')
}
depthFirstFill(rgbData, location, targetColor, replacementColor)
}
/**
* Utility-function to implement the breadth-first loop.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
* @param queue The locations that still need to be visited.
*/
function breadthFirstFill (rgbData, location, targetColor, replacementColor, queue) {
const currentLocation = queue[0]
queue.shift()
if (rgbData[currentLocation[0]][currentLocation[1]] === targetColor) {
rgbData[currentLocation[0]][currentLocation[1]] = replacementColor
for (let i = 0; i < neighbors.length; i++) {
const x = currentLocation[0] + neighbors[i][0]
const y = currentLocation[1] + neighbors[i][1]
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
queue.push([x, y])
}
}
}
}
/**
* Utility-function to implement the depth-first loop.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
function depthFirstFill (rgbData, location, targetColor, replacementColor) {
if (rgbData[location[0]][location[1]] === targetColor) {
rgbData[location[0]][location[1]] = replacementColor
for (let i = 0; i < neighbors.length; i++) {
const x = location[0] + neighbors[i][0]
const y = location[1] + neighbors[i][1]
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
depthFirstFill(rgbData, [x, y], targetColor, replacementColor)
}
}
}
}
/**
* Flood fill.
*
* Flood fill, also called seed fill, is an algorithm that determines and alters the area connected to a given node in a
* multi-dimensional array with some matching attribute. It is used in the "bucket" fill tool of paint programs to fill
* connected, similarly-colored areas with a different color.
*
* (description adapted from https://en.wikipedia.org/wiki/Flood_fill)
* @see https://www.techiedelight.com/flood-fill-algorithm/
*/
const neighbors = [
[-1, -1],
[-1, 0],
[-1, 1],
[0, -1],
[0, 1],
[1, -1],
[1, 0],
[1, 1]
]
/**
* Implements the flood fill algorithm through a breadth-first approach using a queue.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
export function breadthFirstSearch(
rgbData,
location,
targetColor,
replacementColor
) {
if (
location[0] < 0 ||
location[0] >= rgbData.length ||
location[1] < 0 ||
location[1] >= rgbData[0].length
) {
throw new Error('location should point to a pixel within the rgbData')
}
const queue = []
queue.push(location)
while (queue.length > 0) {
breadthFirstFill(rgbData, location, targetColor, replacementColor, queue)
}
}
/**
* Implements the flood fill algorithm through a depth-first approach using recursion.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
export function depthFirstSearch(
rgbData,
location,
targetColor,
replacementColor
) {
if (
location[0] < 0 ||
location[0] >= rgbData.length ||
location[1] < 0 ||
location[1] >= rgbData[0].length
) {
throw new Error('location should point to a pixel within the rgbData')
}
depthFirstFill(rgbData, location, targetColor, replacementColor)
}
/**
* Utility-function to implement the breadth-first loop.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
* @param queue The locations that still need to be visited.
*/
function breadthFirstFill(
rgbData,
location,
targetColor,
replacementColor,
queue
) {
const currentLocation = queue[0]
queue.shift()
if (rgbData[currentLocation[0]][currentLocation[1]] === targetColor) {
rgbData[currentLocation[0]][currentLocation[1]] = replacementColor
for (let i = 0; i < neighbors.length; i++) {
const x = currentLocation[0] + neighbors[i][0]
const y = currentLocation[1] + neighbors[i][1]
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
queue.push([x, y])
}
}
}
}
/**
* Utility-function to implement the depth-first loop.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
function depthFirstFill(rgbData, location, targetColor, replacementColor) {
if (rgbData[location[0]][location[1]] === targetColor) {
rgbData[location[0]][location[1]] = replacementColor
for (let i = 0; i < neighbors.length; i++) {
const x = location[0] + neighbors[i][0]
const y = location[1] + neighbors[i][1]
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
depthFirstFill(rgbData, [x, y], targetColor, replacementColor)
}
}
}
}

View File

@ -15,7 +15,7 @@
/** Class to handle the vector calculations. */
export class Vector2 {
constructor (x, y) {
constructor(x, y) {
this.x = x
this.y = y
}
@ -26,7 +26,7 @@ export class Vector2 {
* @param vector The vector to be added.
* @returns The sum-vector.
*/
add (vector) {
add(vector) {
const x = this.x + vector.x
const y = this.y + vector.y
return new Vector2(x, y)
@ -38,7 +38,7 @@ export class Vector2 {
* @param vector The vector to be subtracted.
* @returns The difference-vector.
*/
subtract (vector) {
subtract(vector) {
const x = this.x - vector.x
const y = this.y - vector.y
return new Vector2(x, y)
@ -50,7 +50,7 @@ export class Vector2 {
* @param scalar The factor by which to multiply the vector.
* @returns The scaled vector.
*/
multiply (scalar) {
multiply(scalar) {
const x = this.x * scalar
const y = this.y * scalar
return new Vector2(x, y)
@ -62,8 +62,8 @@ export class Vector2 {
* @param angleInDegrees The angle by which to rotate the vector.
* @returns The rotated vector.
*/
rotate (angleInDegrees) {
const radians = angleInDegrees * Math.PI / 180
rotate(angleInDegrees) {
const radians = (angleInDegrees * Math.PI) / 180
const ca = Math.cos(radians)
const sa = Math.sin(radians)
const x = ca * this.x - sa * this.y
@ -81,7 +81,7 @@ export class Vector2 {
* @param steps The number of iterations.
* @returns The transformed vectors after the iteration-steps.
*/
export function iterate (initialVectors, steps) {
export function iterate(initialVectors, steps) {
let vectors = initialVectors
for (let i = 0; i < steps; i++) {
vectors = iterationStep(vectors)
@ -99,7 +99,7 @@ export function iterate (initialVectors, steps) {
* @param vectors The vectors composing the shape to which the algorithm is applied.
* @returns The transformed vectors after the iteration-step.
*/
function iterationStep (vectors) {
function iterationStep(vectors) {
const newVectors = []
for (let i = 0; i < vectors.length - 1; i++) {
const startVector = vectors[i]
@ -107,7 +107,9 @@ function iterationStep (vectors) {
newVectors.push(startVector)
const differenceVector = endVector.subtract(startVector).multiply(1 / 3)
newVectors.push(startVector.add(differenceVector))
newVectors.push(startVector.add(differenceVector).add(differenceVector.rotate(60)))
newVectors.push(
startVector.add(differenceVector).add(differenceVector.rotate(60))
)
newVectors.push(startVector.add(differenceVector.multiply(2)))
}

View File

@ -7,7 +7,7 @@ import { Vector2, iterate } from './KochSnowflake'
* @param steps The number of iterations.
* @returns The canvas of the rendered Koch snowflake.
*/
function getKochSnowflake (canvasWidth = 600, steps = 5) {
function getKochSnowflake(canvasWidth = 600, steps = 5) {
if (canvasWidth <= 0) {
throw new Error('canvasWidth should be greater than zero')
}
@ -15,7 +15,10 @@ function getKochSnowflake (canvasWidth = 600, steps = 5) {
const offsetX = canvasWidth / 10.0
const offsetY = canvasWidth / 3.7
const vector1 = new Vector2(offsetX, offsetY)
const vector2 = new Vector2(canvasWidth / 2, Math.sin(Math.PI / 3) * canvasWidth * 0.8 + offsetY)
const vector2 = new Vector2(
canvasWidth / 2,
Math.sin(Math.PI / 3) * canvasWidth * 0.8 + offsetY
)
const vector3 = new Vector2(canvasWidth - offsetX, offsetY)
const initialVectors = []
initialVectors.push(vector1)
@ -34,7 +37,7 @@ function getKochSnowflake (canvasWidth = 600, steps = 5) {
* @param canvasHeight The height of the canvas.
* @returns The canvas of the rendered edges.
*/
function drawToCanvas (vectors, canvasWidth, canvasHeight) {
function drawToCanvas(vectors, canvasWidth, canvasHeight) {
const canvas = document.createElement('canvas')
canvas.width = canvasWidth
canvas.height = canvasHeight

View File

@ -1,7 +1,7 @@
// wiki - https://en.wikipedia.org/wiki/Tower_of_Hanoi
// Recursive Javascript function to solve tower of hanoi
export function TowerOfHanoi (n, from, to, aux, output = []) {
export function TowerOfHanoi(n, from, to, aux, output = []) {
if (n === 1) {
output.push(`Move disk 1 from rod ${from} to rod ${to}`)
return output

View File

@ -30,7 +30,12 @@ describe('FloodFill', () => {
* @param testLocation The location of the color to be checked.
* @return The color at testLocation.
*/
function testBreadthFirst (fillLocation, targetColor, replacementColor, testLocation) {
function testBreadthFirst(
fillLocation,
targetColor,
replacementColor,
testLocation
) {
const rgbData = generateTestRgbData()
breadthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
return rgbData[testLocation[0]][testLocation[1]]
@ -45,7 +50,13 @@ function testBreadthFirst (fillLocation, targetColor, replacementColor, testLoca
* @param testLocation The location of the color to be checked.
* @return The color at testLocation.
*/
function testDepthFirst (fillLocation, targetColor, replacementColor, testLocation) {// eslint-disable-line
function testDepthFirst(
fillLocation,
targetColor,
replacementColor,
testLocation
) {
// eslint-disable-line
const rgbData = generateTestRgbData()
depthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
return rgbData[testLocation[0]][testLocation[1]]
@ -56,7 +67,7 @@ function testDepthFirst (fillLocation, targetColor, replacementColor, testLocati
*
* @return example rgbData-matrix.
*/
function generateTestRgbData () {
function generateTestRgbData() {
const layout = [
[violet, violet, green, green, black, green, green],
[violet, green, green, black, green, green, green],

View File

@ -2,19 +2,29 @@ import { iterate, Vector2 } from '../KochSnowflake'
describe('KochSnowflake', () => {
it('should produce the correctly-transformed vectors', () => {
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[0])
.toEqual({ x: 0, y: 0 })
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[0]).toEqual({
x: 0,
y: 0
})
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[1])
.toEqual({ x: 1 / 3, y: 0 })
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[1]).toEqual({
x: 1 / 3,
y: 0
})
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[2])
.toEqual({ x: 1 / 2, y: Math.sin(Math.PI / 3) / 3 })
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[2]).toEqual({
x: 1 / 2,
y: Math.sin(Math.PI / 3) / 3
})
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[3])
.toEqual({ x: 2 / 3, y: 0 })
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[3]).toEqual({
x: 2 / 3,
y: 0
})
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[4])
.toEqual({ x: 1, y: 0 })
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[4]).toEqual({
x: 1,
y: 0
})
})
})