Whoo got unit tests that are failing...
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
import HSL from "./hsl"
|
||||
|
||||
abstract class Scheme {
|
||||
protected primary: HSL
|
||||
protected constructor(initial: number) {
|
||||
this.primary = HSL.fromHex(initial)
|
||||
}
|
||||
|
||||
colors: number[]
|
||||
}
|
||||
|
||||
export class Analogous extends Scheme {
|
||||
constructor(initial: number) {
|
||||
super(initial)
|
||||
|
||||
const left1 = new HSL(this.primary.hue - 60, this.primary.saturation, this.primary.lightness)
|
||||
const left2 = new HSL(this.primary.hue - 30, this.primary.saturation, this.primary.lightness)
|
||||
const right1 = new HSL(this.primary.hue + 30, this.primary.saturation, this.primary.lightness)
|
||||
const right2 = new HSL(this.primary.hue + 60, this.primary.saturation, this.primary.lightness)
|
||||
|
||||
this.colors = [
|
||||
left1.toHex(),
|
||||
left2.toHex(),
|
||||
initial,
|
||||
right1.toHex(),
|
||||
right2.toHex()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export class Monochromatic extends Scheme {
|
||||
constructor(initial: number) {
|
||||
super(initial)
|
||||
|
||||
const delta = this.primary.lightness / 6
|
||||
this.colors = [
|
||||
initial,
|
||||
new HSL(this.primary.hue, this.primary.saturation, this.primary.lightness - delta).toHex(),
|
||||
new HSL(this.primary.hue, this.primary.saturation, this.primary.lightness - delta * 2).toHex(),
|
||||
new HSL(this.primary.hue, this.primary.saturation, this.primary.lightness - delta * 3).toHex(),
|
||||
new HSL(this.primary.hue, this.primary.saturation, this.primary.lightness - delta * 4).toHex()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export class SplitComplementary extends Scheme {
|
||||
constructor(initial: number) {
|
||||
super(initial)
|
||||
|
||||
const complement = new HSL((this.primary.hue + 180) % 360, this.primary.saturation, this.primary.lightness)
|
||||
this.colors = [
|
||||
initial,
|
||||
new HSL(complement.hue - 30, complement.saturation, complement.lightness).toHex(),
|
||||
new HSL(complement.hue + 30, complement.saturation, complement.lightness).toHex()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export class Triadic extends Scheme {
|
||||
constructor(initial: number) {
|
||||
super(initial)
|
||||
|
||||
this.colors = [
|
||||
initial,
|
||||
new HSL(this.primary.hue - 120, this.primary.saturation, this.primary.lightness).toHex(),
|
||||
new HSL(this.primary.hue + 120, this.primary.saturation, this.primary.lightness).toHex()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export class Tetradic extends Scheme {
|
||||
constructor(initial: number) {
|
||||
super(initial)
|
||||
|
||||
const second = new HSL(this.primary.hue + 45, this.primary.saturation, this.primary.lightness)
|
||||
const complement = new HSL(this.primary.hue + 180, this.primary.saturation, this.primary.lightness)
|
||||
const secondComplement = new HSL(second.hue + 180, this.primary.saturation, this.primary.lightness)
|
||||
|
||||
this.colors = [
|
||||
initial,
|
||||
second.toHex(),
|
||||
complement.toHex(),
|
||||
secondComplement.toHex()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export class Square extends Scheme {
|
||||
constructor(initial: number) {
|
||||
super(initial)
|
||||
|
||||
const second = new HSL(this.primary.hue + 90, this.primary.saturation, this.primary.lightness)
|
||||
const third = new HSL(this.primary.hue + 180, this.primary.saturation, this.primary.lightness)
|
||||
const fourth = new HSL(second.hue + 270, this.primary.saturation, this.primary.lightness)
|
||||
|
||||
this.colors = [
|
||||
initial,
|
||||
second.toHex(),
|
||||
third.toHex(),
|
||||
fourth.toHex()
|
||||
]
|
||||
}
|
||||
}
|
||||
+10
-6
@@ -2,15 +2,15 @@ import SandPainter from "./sand-painter"
|
||||
import State from "./state"
|
||||
|
||||
export default class Crack {
|
||||
private x: number = 0
|
||||
private y: number = 0
|
||||
private angle: number = 0
|
||||
x: number = 0
|
||||
y: number = 0
|
||||
angle: number = 0
|
||||
readonly state: State
|
||||
private painter: SandPainter
|
||||
private state: State
|
||||
|
||||
constructor(state: State, x: number, y: number, angle: number) {
|
||||
this.painter = new SandPainter(state.getRandomColor())
|
||||
this.state = state
|
||||
this.painter = new SandPainter(this)
|
||||
this.start(x, y, angle)
|
||||
}
|
||||
|
||||
@@ -25,9 +25,13 @@ export default class Crack {
|
||||
this.x += .42 * Math.cos(this.angle * Math.PI / 180)
|
||||
this.y += .42 * Math.sin(this.angle * Math.PI / 180)
|
||||
|
||||
const fuzzX = this.x + (Math.random() * .66 - .33)
|
||||
const fuzzY = this.y + (Math.random() * .66 - .33)
|
||||
|
||||
const context = this.state.canvas.getContext("2d")
|
||||
context.fillStyle = "#000"
|
||||
context.fillRect(this.x, this.y, 1, 1)
|
||||
context.fillRect(fuzzX, fuzzY, 1, 1)
|
||||
this.painter.render()
|
||||
|
||||
const x = Math.floor(this.x)
|
||||
const y = Math.floor(this.y)
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
import RGB from "./rgb"
|
||||
|
||||
export default class HSL {
|
||||
hue: number
|
||||
saturation: number
|
||||
lightness: number
|
||||
|
||||
constructor(hue: number, saturation: number, lightness: number) {
|
||||
this.hue = (hue + 360) % 360
|
||||
this.saturation = saturation
|
||||
this.lightness = lightness
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/RGB_color_model
|
||||
public static fromRgb(rgb: RGB) {
|
||||
const redValue = rgb.red / 255,
|
||||
greenValue = rgb.green / 255,
|
||||
blueValue = rgb.blue / 255,
|
||||
lightness = (redValue + greenValue + blueValue) / 3,
|
||||
saturation = 1 - (3 / (redValue + greenValue + blueValue)) * Math.min(redValue, greenValue, blueValue)
|
||||
|
||||
const dividend = (redValue - greenValue) + (redValue - blueValue)
|
||||
const divisor = 2 * Math.sqrt((redValue - greenValue) ^ 2 + (redValue - blueValue) * (greenValue - blueValue))
|
||||
let hue = (1 / Math.cos(dividend / divisor))
|
||||
if (greenValue > blueValue) hue = 360 - hue
|
||||
|
||||
return new HSL(hue, saturation, lightness)
|
||||
}
|
||||
|
||||
public static fromHex(color: number): HSL {
|
||||
const red = color >> 16
|
||||
const green = (color - (red << 16)) >> 8
|
||||
const blue = color - (red << 16) - (green << 8)
|
||||
return HSL.fromRgb(new RGB(red, green, blue))
|
||||
}
|
||||
|
||||
public toHex(): number {
|
||||
return RGB.fromHsl(this).toHex()
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,6 @@
|
||||
export class HSL {
|
||||
hue: number
|
||||
saturation: number
|
||||
lightness: number
|
||||
import HSL from "./hsl"
|
||||
|
||||
constructor(hue: number, saturation: number, lightness: number) {
|
||||
this.hue = hue
|
||||
this.saturation = saturation
|
||||
this.lightness = lightness
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/RGB_color_model
|
||||
public static fromRgb(rgb: RGB) {
|
||||
const redValue = rgb.red / 255,
|
||||
greenValue = rgb.green / 255,
|
||||
blueValue = rgb.blue / 255,
|
||||
lightness = (redValue + greenValue + blueValue) / 3,
|
||||
saturation = 1 - (3 / (redValue + greenValue + blueValue)) * Math.min(redValue, greenValue, blueValue)
|
||||
|
||||
const dividend = (redValue - greenValue) + (redValue - blueValue)
|
||||
const divisor = 2 * Math.sqrt((redValue - greenValue) ^ 2 + (redValue - blueValue) * (greenValue - blueValue))
|
||||
let hue = (1 / Math.cos(dividend / divisor))
|
||||
if (greenValue > blueValue) hue = 360 - hue
|
||||
|
||||
return new HSL(hue, saturation, lightness)
|
||||
}
|
||||
}
|
||||
|
||||
export class RGB {
|
||||
export default class RGB {
|
||||
red: number
|
||||
green: number
|
||||
blue: number
|
||||
@@ -37,6 +11,10 @@ export class RGB {
|
||||
this.blue = blue
|
||||
}
|
||||
|
||||
toHex(): number {
|
||||
return this.red << 16 + this.green << 8 + this.blue
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/HSL_and_HSV
|
||||
public static fromHsl(hsl: HSL): RGB {
|
||||
let red = 0,
|
||||
@@ -71,13 +49,4 @@ export class RGB {
|
||||
|
||||
return new RGB((red + match) * 255, (green + match) * 255, (blue + match) * 255)
|
||||
}
|
||||
}
|
||||
|
||||
export enum Schemes {
|
||||
Analogous,
|
||||
Monochromatic,
|
||||
SplitComplementary,
|
||||
Triadic,
|
||||
Tetradic,
|
||||
Square
|
||||
}
|
||||
+16
-6
@@ -1,16 +1,20 @@
|
||||
import State from "./state"
|
||||
import Crack from "./crack"
|
||||
|
||||
export default class SandPainter {
|
||||
private crack: Crack
|
||||
private color: number
|
||||
private gain: number
|
||||
|
||||
constructor(color: number) {
|
||||
this.color = color
|
||||
constructor(crack: Crack) {
|
||||
this.crack = crack
|
||||
this.color = this.crack.state.getRandomColor()
|
||||
this.gain = Math.random() / 10
|
||||
}
|
||||
|
||||
render(x: number, y: number, angle: number, state: State) {
|
||||
render() {
|
||||
const state = this.crack.state, x = this.crack.x, y = this.crack.y, angle = this.crack.angle
|
||||
let open = true, endX = x, endY = y
|
||||
|
||||
while (open) {
|
||||
endX += .81 * Math.sin(angle * Math.PI / 180)
|
||||
endY -= .81 * Math.cos(angle * Math.PI / 180)
|
||||
@@ -24,14 +28,20 @@ export default class SandPainter {
|
||||
}
|
||||
}
|
||||
|
||||
this.gain += (Math.random() / 10 - .05)
|
||||
if (this.gain < 0) this.gain = 0
|
||||
if (this.gain > 1) this.gain = 1
|
||||
|
||||
const grains = 64
|
||||
const hex = this.color.toString(16)
|
||||
const w = this.gain / (grains - 1) // some kind of multiplier
|
||||
const context = state.canvas.getContext("2d")
|
||||
for (let i = 0; i < grains; i++) {
|
||||
const alpha = (.1 - i / (grains * 10)) * 255
|
||||
const alpha = Math.floor((.1 - i / (grains * 10)) * 255)
|
||||
const paintX = x + (endX - x) * Math.sin(Math.sin(i * w))
|
||||
const paintY = y + (endY - y) * Math.sin(Math.sin(i * w))
|
||||
context.fillStyle = `#${hex.padStart(6, "0")}${alpha.toString(16).padStart(2, "0")}`
|
||||
|
||||
context.fillRect(paintX, paintY, 1, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -2,7 +2,7 @@ import Crack from "./crack"
|
||||
|
||||
export default class State {
|
||||
readonly canvas: HTMLCanvasElement
|
||||
readonly colors: number[]
|
||||
colors: number[]
|
||||
readonly maxCracks: number
|
||||
readonly grid: number[][]
|
||||
seeds: {x: number, y: number}[]
|
||||
@@ -19,7 +19,8 @@ export default class State {
|
||||
this.init()
|
||||
}
|
||||
|
||||
init() {
|
||||
init(colors?: number[]) {
|
||||
if (colors) this.colors = colors
|
||||
this.cracks.length = 0
|
||||
this.grid.length = 0
|
||||
this.seeds.length = 0
|
||||
|
||||
+20
-1
@@ -1,5 +1,6 @@
|
||||
// TS attempt of http://www.complexification.net/gallery/machines/substrate/index.php by laniaung.me
|
||||
import State from "./state"
|
||||
import {Analogous, Monochromatic, SplitComplementary, Square, Tetradic, Triadic} from "./color-schemes"
|
||||
|
||||
export default class Substrate {
|
||||
private readonly state: State
|
||||
@@ -23,7 +24,25 @@ export default class Substrate {
|
||||
requestAnimationFrame(draw)
|
||||
}
|
||||
|
||||
setInterval(() => me.state.init(), 120 * 1000)
|
||||
setInterval(() => me.state.init(me.getRandomColorScheme()), 30 * 1000)
|
||||
draw()
|
||||
}
|
||||
|
||||
getRandomColorScheme(): number[] {
|
||||
const color = Math.floor(Math.random() * Math.pow(256, 3))
|
||||
switch (Math.floor(Math.random() * 6)) {
|
||||
case 1:
|
||||
return new Monochromatic(color).colors
|
||||
case 2:
|
||||
return new SplitComplementary(color).colors
|
||||
case 3:
|
||||
return new Triadic(color).colors
|
||||
case 4:
|
||||
return new Tetradic(color).colors
|
||||
case 5:
|
||||
return new Square(color).colors
|
||||
default:
|
||||
return new Analogous(color).colors
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user