Add the color scheme generator
This commit is contained in:
parent
c2bbbfb103
commit
ebeb1c5d9e
22
main.rkt
22
main.rkt
|
@ -10,18 +10,25 @@
|
|||
"src/webcontainer/webcontainer.rkt"
|
||||
"src/webcontainer/website.rkt"
|
||||
"src/pages/sitemap.rkt"
|
||||
"src/pages/home.rkt")
|
||||
"src/pages/home.rkt"
|
||||
"src/pages/other-generators.rkt")
|
||||
; Website
|
||||
(define *website*
|
||||
(website
|
||||
"" weblet pages:home
|
||||
("AboutMe" weblet pages:about-me)
|
||||
("Fonts" weblet pages:fonts)))
|
||||
("Fonts" weblet pages:fonts)
|
||||
("Generators" weblet pages:other-generators
|
||||
("ColorScheme" weblet pages:color-scheme-generator))
|
||||
))
|
||||
; Sitemap
|
||||
(sitemap
|
||||
("Home" "/" #f
|
||||
("About Me" "/AboutMe" #f)
|
||||
("Fonts I made" "/Fonts" #f)))
|
||||
("Fonts I made" "/Fonts" #f))
|
||||
("Miscellaneous Generators" "/Generators" #f
|
||||
("Color Scheme" "/Generators/ColorScheme" #f))
|
||||
)
|
||||
; Webcontainer
|
||||
(define *webcontainer* (make-webcontainer))
|
||||
(webcontainer-add-website! *webcontainer* *website*)
|
||||
|
@ -29,12 +36,3 @@
|
|||
(display "Starting server...")(newline)
|
||||
(webcontainer-start *webcontainer*))
|
||||
|
||||
;(website
|
||||
; "" weblet home-page
|
||||
; ("ToyCatCreator" redirection "http://beleth.pink")
|
||||
; ("About" weblet about-me-page)
|
||||
; ("Fonts" weblet fonts-page)
|
||||
; ("FlagGenerator" weblet flag-generator-page
|
||||
; ("RawFlag" weblet flag-generator-raw-page)
|
||||
; ("About" weblet about-flag-generator-page))
|
||||
; )
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
#lang racket/base
|
||||
|
||||
; Pages with minor generators, often written in javascript
|
||||
(require
|
||||
"templates.rkt"
|
||||
racket/string)
|
||||
|
||||
(provide
|
||||
pages:other-generators
|
||||
pages:color-scheme-generator)
|
||||
|
||||
; About page on the other generators
|
||||
(define pages:other-generators
|
||||
(pages:template
|
||||
#:title "Miscellaneous Generators"
|
||||
#:author "Feufochmar"
|
||||
#:date "2019-11-08"
|
||||
#:content
|
||||
'(article
|
||||
(p "One of the thing I like to code as a hobby are procedural generators. "
|
||||
"This section regroups little other generators I wrote, but that are not important enough to have their own section. "
|
||||
"Some where made during the " (a ((href "http://www.procjam.com/")) "PROCJAM") " sessions, a recurrent coding jam about procedural generation. "
|
||||
"So they may also be present on my " (a ((href "https://feufochmar.itch.io/")) " itch.io page") ". ")
|
||||
)))
|
||||
|
||||
; Color Scheme Generator
|
||||
(define (color-scheme-block name type)
|
||||
`(div ((class "palette-block"))
|
||||
(p ((id ,(string-append "wheel." type)) (class "palette-wheel")) "")
|
||||
(p ((class "palette-name")) ,name)
|
||||
,@(build-list 5
|
||||
(lambda (i)
|
||||
(let ((j (+ 1 i)))
|
||||
`(p ((id ,(string-append "color-" (number->string j) "-" type))
|
||||
(class "palette-color"))
|
||||
,(string-append "Color." (number->string j) "." name)))))))
|
||||
|
||||
(define pages:color-scheme-generator
|
||||
(pages:template
|
||||
#:title "Color Scheme Generator"
|
||||
#:author "Feufochmar"
|
||||
#:date "2019-11-08"
|
||||
#:stylesheets '("/css/feuforeve.css" "/css/palette-generator.css")
|
||||
#:scripts '("/scripts/palette-generator.js")
|
||||
#:on-load "generateScheme();"
|
||||
#:content
|
||||
`(article
|
||||
(section
|
||||
(p (button ((onclick "generateScheme();")) "New color scheme")))
|
||||
(section
|
||||
,(color-scheme-block "HSL" "hsl")
|
||||
,(color-scheme-block "HSV" "hsv")
|
||||
,(color-scheme-block "LCHab" "lchab")
|
||||
,(color-scheme-block "LCHuv" "lchuv") )
|
||||
(section
|
||||
(p (h3 "About this generator")
|
||||
"This generator creates a small color palette using a cylindrical representation. "
|
||||
"This representation is mapped to four different color spaces to generate four color schemes. " (br)
|
||||
"The color wheel displayed shows for each colorspace the colors with the hues from 0° to 360°, "
|
||||
"a lightness or value of 50%, and a saturation or chroma of 100%. " (br)
|
||||
"The colors are displayed with their RGB values. "
|
||||
))
|
||||
)))
|
|
@ -26,9 +26,9 @@
|
|||
(define (get-avatar author)
|
||||
(case author
|
||||
(("Feufochmar")
|
||||
"images/feufochmar.png")
|
||||
"/images/feufochmar.png")
|
||||
(("feuforeve.fr")
|
||||
("images/feuforeve.png"))
|
||||
("/images/feuforeve.png"))
|
||||
(else
|
||||
#f)))
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
.palette-block {
|
||||
display: inline-block;
|
||||
width: 180;
|
||||
margin: 1ex;
|
||||
border: solid;
|
||||
border-color: hsla(230, 10%, 15%, 1.0);
|
||||
border-width: thin;
|
||||
}
|
||||
|
||||
.palette-wheel {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.palette-name {
|
||||
text-align: center;
|
||||
padding: 1ex;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.palette-color {
|
||||
padding: 1ex;
|
||||
margin: 0px;
|
||||
}
|
|
@ -0,0 +1,402 @@
|
|||
// Color
|
||||
function Color() {
|
||||
this.hue = 0;
|
||||
this.saturation = 0;
|
||||
this.luminosity = 0;
|
||||
}
|
||||
|
||||
// Palette
|
||||
function ColorScheme() {
|
||||
this.color1 = new Color();
|
||||
this.color2 = new Color();
|
||||
this.color3 = new Color();
|
||||
this.color4 = new Color();
|
||||
this.color5 = new Color();
|
||||
};
|
||||
|
||||
//
|
||||
function displayRgb(r, g, b) {
|
||||
var hexVal = 0x1000000 + r * 0x010000 + g * 0x000100 + b * 0x000001;
|
||||
return '#' + hexVal.toString(16).substring(1);
|
||||
}
|
||||
|
||||
// Pick an element from an array at random
|
||||
function pickFrom(arr) {
|
||||
var idx = Math.floor(arr.length * Math.random());
|
||||
return arr[idx];
|
||||
}
|
||||
|
||||
function pickBoolean() {
|
||||
return Math.random() < 0.5;
|
||||
}
|
||||
|
||||
function clamp(val, mn, mx) {
|
||||
return Math.min(mx, Math.max(mn, val));
|
||||
}
|
||||
|
||||
function hToRgb(v1, v2, h) {
|
||||
var vH = h;
|
||||
if (h < 0) {
|
||||
vH = h + 1;
|
||||
}
|
||||
if (h > 1) {
|
||||
vH = h - 1;
|
||||
}
|
||||
var res;
|
||||
if ((6 * vH) < 1) {
|
||||
res = v1 + ((v2 - v1) * 6 * vH);
|
||||
} else if ((2 * vH) < 1) {
|
||||
res = v2;
|
||||
} else if ((3 * vH) < 2) {
|
||||
res = v1 + ((v2 - v1) * 6 * ((2 / 3) - vH));
|
||||
} else {
|
||||
res = v1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function hslToRgb(color) {
|
||||
var h = color.hue; // 0..360
|
||||
var s = color.saturation; // 0..100
|
||||
var l = color.luminosity; // 0..100
|
||||
//
|
||||
var r = 0;
|
||||
var g = 0;
|
||||
var b = 0;
|
||||
//
|
||||
if (s == 0) {
|
||||
r = l / 100;
|
||||
g = l / 100;
|
||||
b = l / 100;
|
||||
} else {
|
||||
var hr = h / 360;
|
||||
var lr = l / 100;
|
||||
var sr = s / 100;
|
||||
var v2;
|
||||
if (lr < 0.5) {
|
||||
v2 = lr * (1 + sr);
|
||||
} else {
|
||||
v2 = (lr + sr) - (lr * sr);
|
||||
}
|
||||
var v1 = 2 * lr - v2;
|
||||
|
||||
r = hToRgb(v1, v2, hr + (1 / 3));
|
||||
g = hToRgb(v1, v2, hr);
|
||||
b = hToRgb(v1, v2, hr - (1 / 3));
|
||||
}
|
||||
|
||||
//
|
||||
r = Math.floor(clamp(255 * r, 0, 255));
|
||||
g = Math.floor(clamp(255 * g, 0, 255));
|
||||
b = Math.floor(clamp(255 * b, 0, 255));
|
||||
return displayRgb(r, g, b);
|
||||
}
|
||||
|
||||
function hsvToRgb(color) {
|
||||
var h = color.hue; // 0..360
|
||||
var s = color.saturation; // 0..100
|
||||
var v = color.luminosity; // 0..100
|
||||
//
|
||||
if (s == 0) {
|
||||
r = v / 100;
|
||||
g = v / 100;
|
||||
b = v / 100;
|
||||
} else {
|
||||
var hr = h / 360;
|
||||
var vr = v / 100;
|
||||
var sr = s / 100;
|
||||
//
|
||||
var vH = hr * 6;
|
||||
if (vH == 6) {
|
||||
vH = 0;
|
||||
}
|
||||
var vI = Math.floor(vH);
|
||||
var v1 = vr * (1 - sr);
|
||||
var v2 = vr * (1 - sr * (vH - vI));
|
||||
var v3 = vr * (1 - sr * (1 - vH + vI));
|
||||
//
|
||||
if (vI == 0) {
|
||||
r = vr;
|
||||
g = v3;
|
||||
b = v1;
|
||||
} else if (vI == 1) {
|
||||
r = v2;
|
||||
g = vr;
|
||||
b = v1;
|
||||
} else if (vI == 2) {
|
||||
r = v1;
|
||||
g = vr;
|
||||
b = v3;
|
||||
} else if (vI == 3) {
|
||||
r = v1;
|
||||
g = v2;
|
||||
b = vr;
|
||||
} else if (vI == 4) {
|
||||
r = v3;
|
||||
g = v1;
|
||||
b = vr;
|
||||
} else {
|
||||
r = vr;
|
||||
g = v1;
|
||||
b = v2;
|
||||
}
|
||||
}
|
||||
//
|
||||
r = Math.floor(clamp(255 * r, 0, 255));
|
||||
g = Math.floor(clamp(255 * g, 0, 255));
|
||||
b = Math.floor(clamp(255 * b, 0, 255));
|
||||
return displayRgb(r, g, b);
|
||||
}
|
||||
|
||||
function xyzToRgb(x, y, z) {
|
||||
var vX = x / 100; // Note: x from 0 to 95.047 (Observer = 2°, Illuminant = D65)
|
||||
var vY = y / 100; // Note: y from 0 to 100
|
||||
var vZ = z / 100; // Note: z from 0 to 108.883
|
||||
//
|
||||
var vR = (3.2406 * vX) + (-1.5372 * vY) + (-0.4986 * vZ);
|
||||
var vG = (-0.9689 * vX) + (1.8758 * vY) + (0.0415 * vZ);
|
||||
var vB = (0.0557 * vX) + (-0.2040 * vY) + (1.0570 * vZ);
|
||||
//
|
||||
var transform = function(v) {
|
||||
var res;
|
||||
if (v > 0.0031308) {
|
||||
res = (1.055 * Math.pow(v, 1 / 2.4)) - 0.055;
|
||||
} else {
|
||||
res = 12.92 * v;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
//
|
||||
var r = Math.floor(clamp(255 * transform(vR), 0, 255));
|
||||
var g = Math.floor(clamp(255 * transform(vG), 0, 255));
|
||||
var b = Math.floor(clamp(255 * transform(vB), 0, 255));
|
||||
return displayRgb(r, g, b);
|
||||
}
|
||||
|
||||
function LabToRgb(l, a, b) {
|
||||
var vY = (l + 16) / 116;
|
||||
var vX = vY + (a / 500);
|
||||
var vZ = vY - (b / 200);
|
||||
//
|
||||
var transform = function(v) {
|
||||
var res;
|
||||
var v3 = v * v * v;
|
||||
if (v3 > 0.008856) {
|
||||
res = v3;
|
||||
} else {
|
||||
res = (v - (16 / 116)) / 7.787;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
// Observer= 2°, Illuminant= D65
|
||||
var x = 95.047 * transform(vX);
|
||||
var y = 100.000 * transform(vY);
|
||||
var z = 108.883 * transform(vZ);
|
||||
return xyzToRgb(x, y, z);
|
||||
}
|
||||
|
||||
function LCHabToRgb(color) {
|
||||
var l = color.luminosity; // 0..100
|
||||
var c = color.saturation; // 0..100
|
||||
var h = color.hue; // 0..360
|
||||
//
|
||||
var a = c * Math.cos(h * Math.PI / 180);
|
||||
var b = c * Math.sin(h * Math.PI / 180);
|
||||
return LabToRgb(l, a, b);
|
||||
}
|
||||
|
||||
function LuvToRgb(l, u, v) {
|
||||
var vY;
|
||||
if (l > 8) {
|
||||
vY = Math.pow((l + 16) / 116, 3);
|
||||
} else {
|
||||
vY = l * Math.pow(3 / 29, 3);
|
||||
}
|
||||
|
||||
// Observer= 2°, Illuminant= D65
|
||||
var rX = 95.047;
|
||||
var rY = 100.000;
|
||||
var rZ = 108.883;
|
||||
|
||||
var rU = (4 * rX) / (rX + (15 * rY) + (3 * rZ));
|
||||
var rV = (9 * rY) / (rX + (15 * rY) + (3 * rZ));
|
||||
|
||||
var vU = rU + (u / (13 * l));
|
||||
var vV = rV + (v / (13 * l));
|
||||
|
||||
var y = 100 * vY;
|
||||
var x = y * ((9 * vU) / (4 * vV));
|
||||
var z = y * ((12 - (3 * vU) - (20 * vV)) / (4 * vV));
|
||||
|
||||
return xyzToRgb(x, y, z);
|
||||
}
|
||||
|
||||
function LCHuvToRgb(color) {
|
||||
var l = color.luminosity; // 0..100
|
||||
var c = color.saturation; // 0..100
|
||||
var h = color.hue; // 0..360
|
||||
//
|
||||
var u = c * Math.cos(h * Math.PI / 180);
|
||||
var v = c * Math.sin(h * Math.PI / 180);
|
||||
return LuvToRgb(l, u, v);
|
||||
}
|
||||
|
||||
// Choose the first color
|
||||
function pickInitialColor() {
|
||||
var result = new Color();
|
||||
result.hue = 360 * Math.random();
|
||||
result.saturation = 60 + (30 * Math.random());
|
||||
result.luminosity = 60 + (30 * Math.random());
|
||||
return result;
|
||||
}
|
||||
|
||||
function lerp(a, b, t) {
|
||||
return a + t * (b - a);
|
||||
}
|
||||
|
||||
function onWheel(val) {
|
||||
if (val < 0) {
|
||||
return onWheel(val + 360);
|
||||
} else if (val >= 360) {
|
||||
return onWheel(val - 360);
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
// Gradient between complementaries colors
|
||||
function algorithmGradient(initialColor) {
|
||||
var scheme = new ColorScheme();
|
||||
scheme.color1 = initialColor;
|
||||
scheme.color5.hue = onWheel(scheme.color1.hue + 180);
|
||||
|
||||
scheme.color5.saturation = 100 - scheme.color1.saturation;
|
||||
scheme.color5.luminosity = 100 - scheme.color1.luminosity;
|
||||
var directHue = pickBoolean();
|
||||
var minHue = scheme.color1.hue;
|
||||
var maxHue = scheme.color5.hue;
|
||||
var minSat = scheme.color1.saturation;
|
||||
var maxSat = scheme.color5.saturation;
|
||||
var minLum = scheme.color1.luminosity;
|
||||
var maxLum = scheme.color5.luminosity;
|
||||
var computeColor = function(color, step) {
|
||||
if (directHue) {
|
||||
color.hue = onWheel(lerp(minHue, maxHue, step));
|
||||
} else {
|
||||
color.hue = onWheel(lerp(maxHue, minHue, 1 - step));
|
||||
}
|
||||
color.saturation = lerp(minSat, maxSat, step);
|
||||
color.luminosity = lerp(minLum, maxLum, step);
|
||||
}
|
||||
computeColor(scheme.color2, 0.25);
|
||||
computeColor(scheme.color3, 0.5);
|
||||
computeColor(scheme.color4, 0.75);
|
||||
|
||||
return scheme;
|
||||
}
|
||||
|
||||
function pickValueAnalog(x) {
|
||||
var a = 50 + x / 2;
|
||||
var b = 3 * x / 2 - 50;
|
||||
var mn = Math.max(50, Math.min(a, b));
|
||||
var mx = Math.min(90, Math.max(a, b));
|
||||
return lerp(mn, mx, Math.random());
|
||||
}
|
||||
|
||||
function pickValueComplementary(x) {
|
||||
var a = x / 4;
|
||||
var b = 3 * x / 4;
|
||||
var mn = Math.max(a, 25);
|
||||
var mx = Math.min(b, 50);
|
||||
return lerp(mn, mx, Math.random());
|
||||
}
|
||||
|
||||
// initial, 2 analogs, 2 complementaries
|
||||
function algorithmFiveHues(initialColor) {
|
||||
var scheme = new ColorScheme();
|
||||
scheme.color2 = initialColor;
|
||||
var analogDistance = 25 + Math.floor(25 * Math.random());
|
||||
scheme.color1.hue = onWheel(scheme.color2.hue + analogDistance);
|
||||
scheme.color3.hue = onWheel(scheme.color2.hue - analogDistance);
|
||||
scheme.color4.hue = onWheel(scheme.color2.hue + (analogDistance / 2) + 180);
|
||||
scheme.color5.hue = onWheel(scheme.color2.hue - (analogDistance / 2) + 180);
|
||||
/**/
|
||||
scheme.color1.saturation = pickValueAnalog(scheme.color2.saturation);
|
||||
scheme.color3.saturation = pickValueAnalog(scheme.color2.saturation);
|
||||
scheme.color1.luminosity = pickValueAnalog(scheme.color2.luminosity);
|
||||
scheme.color3.luminosity = pickValueAnalog(scheme.color2.luminosity);
|
||||
if (pickBoolean()) {
|
||||
scheme.color4.saturation = pickValueComplementary(scheme.color2.saturation);
|
||||
scheme.color4.luminosity = pickValueComplementary(scheme.color2.luminosity);
|
||||
} else {
|
||||
scheme.color4.saturation = pickValueAnalog(scheme.color2.saturation);
|
||||
scheme.color4.luminosity = pickValueAnalog(scheme.color2.luminosity);
|
||||
}
|
||||
if (pickBoolean()) {
|
||||
scheme.color5.saturation = pickValueComplementary(scheme.color2.saturation);
|
||||
scheme.color5.luminosity = pickValueComplementary(scheme.color2.luminosity);
|
||||
} else {
|
||||
scheme.color5.saturation = pickValueAnalog(scheme.color2.saturation);
|
||||
scheme.color5.luminosity = pickValueAnalog(scheme.color2.luminosity);
|
||||
}
|
||||
|
||||
return scheme;
|
||||
}
|
||||
|
||||
// Create a new color scheme
|
||||
function newScheme() {
|
||||
// Pick a color
|
||||
var firstColor = pickInitialColor();
|
||||
// choose a scheme generator algorithm
|
||||
var algorithm = pickFrom([algorithmGradient, algorithmFiveHues, algorithmFiveHues, algorithmFiveHues, algorithmFiveHues]);
|
||||
//
|
||||
return algorithm(firstColor);
|
||||
}
|
||||
|
||||
// Entry point
|
||||
function generateScheme() {
|
||||
var scheme = newScheme();
|
||||
updateBlock('hsl', scheme, hslToRgb);
|
||||
updateBlock('hsv', scheme, hsvToRgb);
|
||||
updateBlock('lchab', scheme, LCHabToRgb);
|
||||
updateBlock('lchuv', scheme, LCHuvToRgb);
|
||||
}
|
||||
|
||||
// Update a block
|
||||
function updateBlock(blockId, scheme, colorFun) {
|
||||
updateColor('color-1-' + blockId, scheme.color1, colorFun);
|
||||
updateColor('color-2-' + blockId, scheme.color2, colorFun);
|
||||
updateColor('color-3-' + blockId, scheme.color3, colorFun);
|
||||
updateColor('color-4-' + blockId, scheme.color4, colorFun);
|
||||
updateColor('color-5-' + blockId, scheme.color5, colorFun);
|
||||
displayWheel('wheel.' + blockId, colorFun, 50);
|
||||
}
|
||||
|
||||
// Update the background (and foreground) color of element
|
||||
function updateColor(id, color, func) {
|
||||
var elem = document.getElementById(id);
|
||||
var value = func(color);
|
||||
elem.style.background = value;
|
||||
elem.innerHTML = value;
|
||||
if (color.luminosity < 45) {
|
||||
elem.style.color = 'white';
|
||||
} else {
|
||||
elem.style.color = 'black';
|
||||
}
|
||||
}
|
||||
|
||||
// Display the wheel at sat = 100
|
||||
function displayWheel(id, colorFun, lum) {
|
||||
var elem = document.getElementById(id);
|
||||
var contents = '';
|
||||
var col = new Color();
|
||||
col.saturation = 100;
|
||||
col.luminosity = lum;
|
||||
for (i = 0; i < 359; i += 2) {
|
||||
col.hue = i;
|
||||
contents += '<span style="display:inline-block;width:1px;height:20px;background:';
|
||||
contents += colorFun(col);
|
||||
contents += ';"></span>';
|
||||
}
|
||||
elem.innerHTML = contents;
|
||||
}
|
Loading…
Reference in New Issue