Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"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-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",
Expand Down
20 changes: 18 additions & 2 deletions src/controller/circle.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ export interface CircleConfig extends ElementConfig {
parameters: {
center: Coordinate
r: number
angle?: {
startDegree?: number
endDegree?: number
}
counterClockwise?: boolean
}
}

Expand All @@ -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
}
}
24 changes: 24 additions & 0 deletions src/controller/utils/circle.utils.ts
Original file line number Diff line number Diff line change
@@ -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
}
}
20 changes: 20 additions & 0 deletions src/controller/utils/vector.utils.ts
Original file line number Diff line number Diff line change
@@ -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 }
}
}
117 changes: 117 additions & 0 deletions src/example-circle-in-circle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/* eslint-disable id-length */
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'

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 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: {
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()
122 changes: 122 additions & 0 deletions src/example-magic-circle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/* eslint-disable id-length */
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'

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 likelihoodToShow = 100

const lineWidth = 3
const radiusDifference = lineWidth + 3
let circleSectionDifference = 5

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)
circleSectionDifference = CircleUtils.angleForLengthOnPerimeterByRadius(
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) {
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
if (differenceToFull < maxAngleDifference + circleSectionDifference) {
currentAngle = differenceToFull - circleSectionDifference
} else {
currentAngle = MathController.random(0, maxAngleDifference)
}
currentAngleStart = currentAngleEnd + circleSectionDifference
currentAngleEnd = currentAngleStart + currentAngle
angleSum += circleSectionDifference + currentAngle
}
index++
}

await OutputController.save(
config,
path.resolve(__dirname, '..', 'output', 'magic-circle.png'),
)
}

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
}

run()