From d578e945b16e60846311f2c7f2b0033b420b08fb Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 12 Aug 2021 09:24:01 +0200 Subject: [PATCH 1/4] feat(template): add magic circle --- package.json | 1 + src/controller/circle.controller.ts | 20 ++++++- src/example-magic-circle.ts | 87 +++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 src/example-magic-circle.ts diff --git a/package.json b/package.json index 9bf1ae9..87a089c 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "start": "ts-node ./src/index.ts", "try": "ts-node ./src/try.ts", "example-ellipse": "ts-node ./src/example-ellipse.ts", + "example-magic-circle": "ts-node ./src/example-magic-circle.ts", "example-bezier": "ts-node ./src/example-bezier.ts", "example-rectangle": "ts-node ./src/example-rectangle.ts", "example-dark-vs-light": "ts-node ./src/example-dark-vs-light.ts", diff --git a/src/controller/circle.controller.ts b/src/controller/circle.controller.ts index 015b0cf..900aa10 100644 --- a/src/controller/circle.controller.ts +++ b/src/controller/circle.controller.ts @@ -8,6 +8,11 @@ export interface CircleConfig extends ElementConfig { parameters: { center: Coordinate r: number + angle?: { + startDegree?: number + endDegree?: number + } + counterClockwise?: boolean } } @@ -20,11 +25,22 @@ export class CircleController { circleConfig.parameters.center.x, circleConfig.parameters.center.y, circleConfig.parameters.r, - 0, - 2 * Math.PI, + circleConfig.parameters.angle && circleConfig.parameters.angle.startDegree + ? CircleController.toRadians(circleConfig.parameters.angle.startDegree) + : 0, + circleConfig.parameters.angle && circleConfig.parameters.angle.endDegree + ? CircleController.toRadians(circleConfig.parameters.angle.endDegree) + : 2 * Math.PI, + circleConfig.parameters.counterClockwise !== undefined + ? circleConfig.parameters.counterClockwise + : false, ) ElementController.postProcessing(config, circleConfig) return config } + + private static toRadians(degree: number): number { + return (degree * Math.PI) / 180 + } } diff --git a/src/example-magic-circle.ts b/src/example-magic-circle.ts new file mode 100644 index 0000000..50d8954 --- /dev/null +++ b/src/example-magic-circle.ts @@ -0,0 +1,87 @@ +/* eslint-disable id-length */ +import { MathController } from './controller/utils/math.controller' +import * as path from 'path' +import { CAMUNDA, DAFTPUNK, SUNSET } from './constants/Colors.constants' +import { BackgroundController } from './controller/background.controller' +import { CanvasController } from './controller/canvas.controller' +import { CircleController } from './controller/circle.controller' +import { OutputController } from './controller/output.controller' +import { ColorController } from './controller/utils/color.controller' +import { Element } from './enums/Element.enum' +import { Config } from './types/Config.type' +import { Coordinate } from './types/Coordinate.type' + +async function run() { + const colors = ColorController.randomAlpha( + ColorController.allHexToRgb(CAMUNDA), + ) + + let config: Config = { + width: 1000, + height: 1000, + colors, + } + config = CanvasController.init(config) + config = BackgroundController.fill(config, 'black') + + const lineWidth = 3 + const radiusDifference = lineWidth + 3 + const circleSectionDifference = 5 + + const maxAngleDifference = 60 + + const center: Coordinate = { + x: config.height / 2, + y: config.width / 2, + } + + // loop over radius + for ( + let radius = 10; + radius < config.width + 0.2 * config.width; + radius += radiusDifference + ) { + // loop over circles per radius + let currentAngleStart = MathController.random(0, 360) + let currentAngle = MathController.random(0, maxAngleDifference) + let currentAngleEnd = currentAngleStart + currentAngle + let angleSum = currentAngle + while (angleSum < 360) { + config = CircleController.circle(config, { + color: { + stroke: + config.colors[MathController.random(0, config.colors.length - 1)], + }, + element: Element.CIRCLE, + closePath: false, + lineWidth, + parameters: { + center, + r: radius, + angle: { + startDegree: currentAngleStart, + endDegree: currentAngleEnd, + }, + }, + }) + + // new angle calculation + const differenceToFull = 360 - angleSum + if (differenceToFull < maxAngleDifference + circleSectionDifference) { + currentAngle = differenceToFull - circleSectionDifference + } else { + currentAngle = MathController.random(0, maxAngleDifference) + } + currentAngleStart = currentAngleEnd + circleSectionDifference + currentAngleEnd = currentAngleStart + currentAngle + angleSum += circleSectionDifference + currentAngle + } + } + + await OutputController.save( + config, + path.resolve(__dirname, '..', 'output', 'magic-circle.png'), + ) +} + +run() From 9afdf9f029d1260e5f2762f7260192018bc6d584 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 12 Aug 2021 10:44:29 +0200 Subject: [PATCH 2/4] feat(magiccircle): update colors --- src/example-magic-circle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/example-magic-circle.ts b/src/example-magic-circle.ts index 50d8954..2fbcc14 100644 --- a/src/example-magic-circle.ts +++ b/src/example-magic-circle.ts @@ -13,7 +13,7 @@ import { Coordinate } from './types/Coordinate.type' async function run() { const colors = ColorController.randomAlpha( - ColorController.allHexToRgb(CAMUNDA), + ColorController.allHexToRgb([...CAMUNDA, ...SUNSET, ...DAFTPUNK]), ) let config: Config = { From 4840759e8ff21b9ef416516a0f6cafe963e9a043 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 12 Aug 2021 12:43:39 +0200 Subject: [PATCH 3/4] feat(cic): add circle in circle --- package.json | 1 + src/example-circle-in-circle.ts | 113 ++++++++++++++++++++++++++++++++ src/example-magic-circle.ts | 74 +++++++++++++++------ 3 files changed, 169 insertions(+), 19 deletions(-) create mode 100644 src/example-circle-in-circle.ts diff --git a/package.json b/package.json index 87a089c..9fb1506 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "try": "ts-node ./src/try.ts", "example-ellipse": "ts-node ./src/example-ellipse.ts", "example-magic-circle": "ts-node ./src/example-magic-circle.ts", + "example-circle-in-circle": "ts-node ./src/example-circle-in-circle.ts", "example-bezier": "ts-node ./src/example-bezier.ts", "example-rectangle": "ts-node ./src/example-rectangle.ts", "example-dark-vs-light": "ts-node ./src/example-dark-vs-light.ts", diff --git a/src/example-circle-in-circle.ts b/src/example-circle-in-circle.ts new file mode 100644 index 0000000..c323134 --- /dev/null +++ b/src/example-circle-in-circle.ts @@ -0,0 +1,113 @@ +/* eslint-disable id-length */ +import { MathController } from './controller/utils/math.controller' +import * as path from 'path' +import { CAMUNDA, DAFTPUNK, SUNSET } from './constants/Colors.constants' +import { BackgroundController } from './controller/background.controller' +import { CanvasController } from './controller/canvas.controller' +import { CircleConfig, CircleController } from './controller/circle.controller' +import { OutputController } from './controller/output.controller' +import { ColorController } from './controller/utils/color.controller' +import { Element } from './enums/Element.enum' +import { Config } from './types/Config.type' +import { Coordinate } from './types/Coordinate.type' + +async function run() { + const colors = ColorController.randomAlpha( + ColorController.allHexToRgb([...CAMUNDA, ...SUNSET, ...DAFTPUNK]), + ) + + let config: Config = { + width: 1000, + height: 1000, + colors, + } + config = CanvasController.init(config) + config = BackgroundController.fill(config, '#0c0c0c') + + // params + + const radiusBigCircle = 200 + const centerX = config.height / 2 + const centerY = config.width / 2 + const numberOfInnerCircles = 100 + + // big circle + + const bigCircleConfig: CircleConfig = { + color: { + stroke: 'white', + }, + element: Element.CIRCLE, + closePath: false, + lineWidth: 2, + parameters: { + center: { + x: centerX, + y: centerY, + }, + r: radiusBigCircle, + }, + } + + config = CircleController.circle(config, bigCircleConfig) + + // inner circles + for (let i = 0; i < numberOfInnerCircles; i++) { + const smallCircleConfig = generateSmallCircleConfig( + centerX, + centerY, + radiusBigCircle, + colors, + ) + config = CircleController.circle(config, smallCircleConfig) + } + + await OutputController.save( + config, + path.resolve(__dirname, '..', 'output', 'circle-in-circle.png'), + ) +} + +function generateSmallCircleConfig( + centerX: number, + centerY: number, + radiusBigCircle: number, + colors?: string[], +): CircleConfig { + const randomRadiusSmallCircle = MathController.random( + 10, + radiusBigCircle * 0.8, + ) + const randomDegree = MathController.random(0, 360) + const bigCircleX = centerX + radiusBigCircle * Math.sin(randomDegree) + const bigCircleY = centerY + radiusBigCircle * Math.cos(randomDegree) + + const smallCircleCenter: Coordinate = { + x: + bigCircleX + + (randomRadiusSmallCircle / radiusBigCircle) * + (centerX - Math.abs(bigCircleX)), + y: + bigCircleY + + (randomRadiusSmallCircle / radiusBigCircle) * + (centerY - Math.abs(bigCircleY)), + } + + const smallCircleConfig: CircleConfig = { + color: { + stroke: colors + ? colors[MathController.random(0, colors.length - 1)] + : 'white', + }, + element: Element.CIRCLE, + closePath: false, + lineWidth: 2, + parameters: { + center: smallCircleCenter, + r: randomRadiusSmallCircle, + }, + } + return smallCircleConfig +} + +run() diff --git a/src/example-magic-circle.ts b/src/example-magic-circle.ts index 2fbcc14..63f96f1 100644 --- a/src/example-magic-circle.ts +++ b/src/example-magic-circle.ts @@ -22,48 +22,65 @@ async function run() { colors, } config = CanvasController.init(config) - config = BackgroundController.fill(config, 'black') + config = BackgroundController.fill(config, '#0c0c0c') + + // params + + const likelihoodToShow = 100 const lineWidth = 3 const radiusDifference = lineWidth + 3 - const circleSectionDifference = 5 + let circleSectionDifference = 5 - const maxAngleDifference = 60 + let maxAngleDifference = 30 const center: Coordinate = { x: config.height / 2, y: config.width / 2, } + // overall index + let index = 0 + // loop over radius for ( let radius = 10; radius < config.width + 0.2 * config.width; radius += radiusDifference ) { + // update base parameters depending on index + maxAngleDifference = maxAngleDifferenceByRadius(radius) + circleSectionDifference = circleSectionDifferenceByRadius(radius) + // loop over circles per radius let currentAngleStart = MathController.random(0, 360) let currentAngle = MathController.random(0, maxAngleDifference) let currentAngleEnd = currentAngleStart + currentAngle let angleSum = currentAngle while (angleSum < 360) { - config = CircleController.circle(config, { - color: { - stroke: - config.colors[MathController.random(0, config.colors.length - 1)], - }, - element: Element.CIRCLE, - closePath: false, - lineWidth, - parameters: { - center, - r: radius, - angle: { - startDegree: currentAngleStart, - endDegree: currentAngleEnd, + const shouldDraw = + likelihoodToShow === 100 || + MathController.random(0, 100) <= likelihoodToShow + + if (shouldDraw) { + config = CircleController.circle(config, { + color: { + stroke: + config.colors[MathController.random(0, config.colors.length - 1)], + }, + element: Element.CIRCLE, + closePath: false, + lineWidth, + parameters: { + center, + r: radius, + angle: { + startDegree: currentAngleStart, + endDegree: currentAngleEnd, + }, }, - }, - }) + }) + } // new angle calculation const differenceToFull = 360 - angleSum @@ -76,6 +93,7 @@ async function run() { currentAngleEnd = currentAngleStart + currentAngle angleSum += circleSectionDifference + currentAngle } + index++ } await OutputController.save( @@ -84,4 +102,22 @@ async function run() { ) } +function maxAngleDifferenceByRadius(radius: number): number { + if (radius < 100) { + return 90 + } else if (radius < 300) { + return 60 + } else if (radius < 600) { + return 45 + } else if (radius < 900) { + return 30 + } + return 20 +} + +function circleSectionDifferenceByRadius(radius: number): number { + const perimeter = 2 * Math.PI * radius + return (5 / perimeter) * 360 +} + run() From 4af79a3dc4659788e0c4419ee81212512b5a89f9 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 13 Aug 2021 08:58:49 +0200 Subject: [PATCH 4/4] feat(utils): add several util functions --- src/controller/utils/circle.utils.ts | 24 +++++++++++++++++++++ src/controller/utils/vector.utils.ts | 20 +++++++++++++++++ src/example-circle-in-circle.ts | 32 ++++++++++++++++------------ src/example-magic-circle.ts | 13 ++++++----- 4 files changed, 68 insertions(+), 21 deletions(-) create mode 100644 src/controller/utils/circle.utils.ts create mode 100644 src/controller/utils/vector.utils.ts diff --git a/src/controller/utils/circle.utils.ts b/src/controller/utils/circle.utils.ts new file mode 100644 index 0000000..acedf4d --- /dev/null +++ b/src/controller/utils/circle.utils.ts @@ -0,0 +1,24 @@ +/* eslint-disable id-length */ +import { Coordinate } from '../../types/Coordinate.type' + +export class CircleUtils { + // calculates a point on the outline of a circle based on + // center, radius and degree + public static pointOnCircleOutlineByAngle( + center: Coordinate, + radius: number, + degree: number, + ): Coordinate { + const coordinate: Coordinate = { + x: center.x + radius * Math.sin(degree), + y: center.y + radius * Math.cos(degree), + } + return coordinate + } + + // calculates angle in degrees to get a length on cicrle outline + public static angleForLengthOnPerimeterByRadius(radius: number): number { + const perimeter = 2 * Math.PI * radius + return (5 / perimeter) * 360 + } +} diff --git a/src/controller/utils/vector.utils.ts b/src/controller/utils/vector.utils.ts new file mode 100644 index 0000000..dd84472 --- /dev/null +++ b/src/controller/utils/vector.utils.ts @@ -0,0 +1,20 @@ +/* eslint-disable id-length */ +import { Coordinate } from '../../types/Coordinate.type' +import { DistanceController } from './distance.controller' + +export class VectorUtils { + public static lineWithLengthBetweenTwoPoints( + start: Coordinate, + end: Coordinate, + length: number, + ): { start: Coordinate; end: Coordinate } { + const distanceBetweenCoordinates = DistanceController.distance(start, end) + const factor = length / distanceBetweenCoordinates + + const calculatedEnd: Coordinate = { + x: start.x + factor * (end.x - Math.abs(start.x)), + y: start.y + factor * (end.y - Math.abs(start.y)), + } + return { start, end: calculatedEnd } + } +} diff --git a/src/example-circle-in-circle.ts b/src/example-circle-in-circle.ts index c323134..6ca9abc 100644 --- a/src/example-circle-in-circle.ts +++ b/src/example-circle-in-circle.ts @@ -1,12 +1,14 @@ /* eslint-disable id-length */ -import { MathController } from './controller/utils/math.controller' import * as path from 'path' import { CAMUNDA, DAFTPUNK, SUNSET } from './constants/Colors.constants' import { BackgroundController } from './controller/background.controller' import { CanvasController } from './controller/canvas.controller' import { CircleConfig, CircleController } from './controller/circle.controller' import { OutputController } from './controller/output.controller' +import { CircleUtils } from './controller/utils/circle.utils' import { ColorController } from './controller/utils/color.controller' +import { MathController } from './controller/utils/math.controller' +import { VectorUtils } from './controller/utils/vector.utils' import { Element } from './enums/Element.enum' import { Config } from './types/Config.type' import { Coordinate } from './types/Coordinate.type' @@ -78,20 +80,22 @@ function generateSmallCircleConfig( 10, radiusBigCircle * 0.8, ) - const randomDegree = MathController.random(0, 360) - const bigCircleX = centerX + radiusBigCircle * Math.sin(randomDegree) - const bigCircleY = centerY + radiusBigCircle * Math.cos(randomDegree) - - const smallCircleCenter: Coordinate = { - x: - bigCircleX + - (randomRadiusSmallCircle / radiusBigCircle) * - (centerX - Math.abs(bigCircleX)), - y: - bigCircleY + - (randomRadiusSmallCircle / radiusBigCircle) * - (centerY - Math.abs(bigCircleY)), + const center: Coordinate = { + x: centerX, + y: centerY, } + const randomDegree = MathController.random(0, 360) + const bigCircleCoordinate = CircleUtils.pointOnCircleOutlineByAngle( + center, + radiusBigCircle, + randomDegree, + ) + const lineCoordinates = VectorUtils.lineWithLengthBetweenTwoPoints( + bigCircleCoordinate, + center, + randomRadiusSmallCircle, + ) + const smallCircleCenter: Coordinate = lineCoordinates.end const smallCircleConfig: CircleConfig = { color: { diff --git a/src/example-magic-circle.ts b/src/example-magic-circle.ts index 63f96f1..0b450f4 100644 --- a/src/example-magic-circle.ts +++ b/src/example-magic-circle.ts @@ -1,12 +1,13 @@ /* eslint-disable id-length */ -import { MathController } from './controller/utils/math.controller' import * as path from 'path' import { CAMUNDA, DAFTPUNK, SUNSET } from './constants/Colors.constants' import { BackgroundController } from './controller/background.controller' import { CanvasController } from './controller/canvas.controller' import { CircleController } from './controller/circle.controller' import { OutputController } from './controller/output.controller' +import { CircleUtils } from './controller/utils/circle.utils' import { ColorController } from './controller/utils/color.controller' +import { MathController } from './controller/utils/math.controller' import { Element } from './enums/Element.enum' import { Config } from './types/Config.type' import { Coordinate } from './types/Coordinate.type' @@ -50,7 +51,10 @@ async function run() { ) { // update base parameters depending on index maxAngleDifference = maxAngleDifferenceByRadius(radius) - circleSectionDifference = circleSectionDifferenceByRadius(radius) + // circleSectionDifference = circleSectionDifferenceByRadius(radius) + circleSectionDifference = CircleUtils.angleForLengthOnPerimeterByRadius( + radius, + ) // loop over circles per radius let currentAngleStart = MathController.random(0, 360) @@ -115,9 +119,4 @@ function maxAngleDifferenceByRadius(radius: number): number { return 20 } -function circleSectionDifferenceByRadius(radius: number): number { - const perimeter = 2 * Math.PI * radius - return (5 / perimeter) * 360 -} - run()