Add Javascript implementation
Add a simple JS port of colorhash for easier demonstration purposes. You don't need to open a command line to create a colorhash, you can just use the webpage. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -1,42 +1,42 @@
|
||||
<svg width="256" height="160" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="32" height="32" fill="hsl(0.00,100.00%,83.33%)" />
|
||||
<rect x="32" y="0" width="32" height="32" fill="hsl(0.00,100.00%,96.67%)" />
|
||||
<rect x="64" y="0" width="32" height="32" fill="hsl(0.00,100.00%,56.67%)" />
|
||||
<rect x="96" y="0" width="32" height="32" fill="hsl(0.00,100.00%,76.67%)" />
|
||||
<rect x="128" y="0" width="32" height="32" fill="hsl(0.00,100.00%,76.67%)" />
|
||||
<rect x="160" y="0" width="32" height="32" fill="hsl(0.00,100.00%,86.67%)" />
|
||||
<rect x="192" y="0" width="32" height="32" fill="hsl(0.00,100.00%,50.00%)" />
|
||||
<rect x="224" y="0" width="32" height="32" fill="hsl(0.00,100.00%,70.00%)" />
|
||||
<rect x="0" y="32" width="32" height="32" fill="hsl(0.00,100.00%,93.33%)" />
|
||||
<rect x="32" y="32" width="32" height="32" fill="hsl(0.00,100.00%,100.00%)" />
|
||||
<rect x="64" y="32" width="32" height="32" fill="hsl(0.00,100.00%,63.33%)" />
|
||||
<rect x="96" y="32" width="32" height="32" fill="hsl(0.00,100.00%,83.33%)" />
|
||||
<rect x="128" y="32" width="32" height="32" fill="hsl(0.00,100.00%,63.33%)" />
|
||||
<rect x="160" y="32" width="32" height="32" fill="hsl(0.00,100.00%,70.00%)" />
|
||||
<rect x="192" y="32" width="32" height="32" fill="hsl(0.00,100.00%,50.00%)" />
|
||||
<rect x="224" y="32" width="32" height="32" fill="hsl(0.00,100.00%,90.00%)" />
|
||||
<rect x="0" y="64" width="32" height="32" fill="hsl(0.00,100.00%,60.00%)" />
|
||||
<rect x="32" y="64" width="32" height="32" fill="hsl(0.00,100.00%,73.33%)" />
|
||||
<rect x="64" y="64" width="32" height="32" fill="hsl(0.00,100.00%,90.00%)" />
|
||||
<rect x="96" y="64" width="32" height="32" fill="hsl(0.00,100.00%,76.67%)" />
|
||||
<rect x="128" y="64" width="32" height="32" fill="hsl(0.00,100.00%,60.00%)" />
|
||||
<rect x="160" y="64" width="32" height="32" fill="hsl(0.00,100.00%,70.00%)" />
|
||||
<rect x="192" y="64" width="32" height="32" fill="hsl(0.00,100.00%,96.67%)" />
|
||||
<rect x="224" y="64" width="32" height="32" fill="hsl(0.00,100.00%,56.67%)" />
|
||||
<rect x="0" y="96" width="32" height="32" fill="hsl(0.00,100.00%,73.33%)" />
|
||||
<rect x="32" y="96" width="32" height="32" fill="hsl(0.00,100.00%,50.00%)" />
|
||||
<rect x="64" y="96" width="32" height="32" fill="hsl(0.00,100.00%,100.00%)" />
|
||||
<rect x="96" y="96" width="32" height="32" fill="hsl(0.00,100.00%,80.00%)" />
|
||||
<rect x="128" y="96" width="32" height="32" fill="hsl(0.00,100.00%,96.67%)" />
|
||||
<rect x="160" y="96" width="32" height="32" fill="hsl(0.00,100.00%,93.33%)" />
|
||||
<rect x="192" y="96" width="32" height="32" fill="hsl(0.00,100.00%,83.33%)" />
|
||||
<rect x="224" y="96" width="32" height="32" fill="hsl(0.00,100.00%,83.33%)" />
|
||||
<rect x="0" y="128" width="32" height="32" fill="hsl(0.00,100.00%,86.67%)" />
|
||||
<rect x="32" y="128" width="32" height="32" fill="hsl(0.00,100.00%,100.00%)" />
|
||||
<rect x="64" y="128" width="32" height="32" fill="hsl(0.00,100.00%,100.00%)" />
|
||||
<rect x="96" y="128" width="32" height="32" fill="hsl(0.00,100.00%,86.67%)" />
|
||||
<rect x="128" y="128" width="32" height="32" fill="hsl(0.00,100.00%,73.33%)" />
|
||||
<rect x="160" y="128" width="32" height="32" fill="hsl(0.00,100.00%,93.33%)" />
|
||||
<rect x="192" y="128" width="32" height="32" fill="hsl(0.00,100.00%,70.00%)" />
|
||||
<rect x="224" y="128" width="32" height="32" fill="hsl(0.00,100.00%,93.33%)" />
|
||||
<rect x="0" y="0" width="32" height="32" fill="hsl(180.00,100.00%,20.00%)" />
|
||||
<rect x="32" y="0" width="32" height="32" fill="hsl(180.00,100.00%,0.00%)" />
|
||||
<rect x="64" y="0" width="32" height="32" fill="hsl(180.00,100.00%,30.00%)" />
|
||||
<rect x="96" y="0" width="32" height="32" fill="hsl(180.00,100.00%,3.33%)" />
|
||||
<rect x="128" y="0" width="32" height="32" fill="hsl(180.00,100.00%,3.33%)" />
|
||||
<rect x="160" y="0" width="32" height="32" fill="hsl(180.00,100.00%,23.33%)" />
|
||||
<rect x="192" y="0" width="32" height="32" fill="hsl(180.00,100.00%,0.00%)" />
|
||||
<rect x="224" y="0" width="32" height="32" fill="hsl(180.00,100.00%,46.67%)" />
|
||||
<rect x="0" y="32" width="32" height="32" fill="hsl(180.00,100.00%,43.33%)" />
|
||||
<rect x="32" y="32" width="32" height="32" fill="hsl(180.00,100.00%,13.33%)" />
|
||||
<rect x="64" y="32" width="32" height="32" fill="hsl(180.00,100.00%,46.67%)" />
|
||||
<rect x="96" y="32" width="32" height="32" fill="hsl(180.00,100.00%,10.00%)" />
|
||||
<rect x="128" y="32" width="32" height="32" fill="hsl(180.00,100.00%,40.00%)" />
|
||||
<rect x="160" y="32" width="32" height="32" fill="hsl(180.00,100.00%,43.33%)" />
|
||||
<rect x="192" y="32" width="32" height="32" fill="hsl(180.00,100.00%,46.67%)" />
|
||||
<rect x="224" y="32" width="32" height="32" fill="hsl(180.00,100.00%,10.00%)" />
|
||||
<rect x="0" y="64" width="32" height="32" fill="hsl(180.00,100.00%,36.67%)" />
|
||||
<rect x="32" y="64" width="32" height="32" fill="hsl(180.00,100.00%,6.67%)" />
|
||||
<rect x="64" y="64" width="32" height="32" fill="hsl(180.00,100.00%,40.00%)" />
|
||||
<rect x="96" y="64" width="32" height="32" fill="hsl(180.00,100.00%,30.00%)" />
|
||||
<rect x="128" y="64" width="32" height="32" fill="hsl(180.00,100.00%,26.67%)" />
|
||||
<rect x="160" y="64" width="32" height="32" fill="hsl(180.00,100.00%,10.00%)" />
|
||||
<rect x="192" y="64" width="32" height="32" fill="hsl(180.00,100.00%,36.67%)" />
|
||||
<rect x="224" y="64" width="32" height="32" fill="hsl(180.00,100.00%,6.67%)" />
|
||||
<rect x="0" y="96" width="32" height="32" fill="hsl(180.00,100.00%,46.67%)" />
|
||||
<rect x="32" y="96" width="32" height="32" fill="hsl(180.00,100.00%,13.33%)" />
|
||||
<rect x="64" y="96" width="32" height="32" fill="hsl(180.00,100.00%,20.00%)" />
|
||||
<rect x="96" y="96" width="32" height="32" fill="hsl(180.00,100.00%,26.67%)" />
|
||||
<rect x="128" y="96" width="32" height="32" fill="hsl(180.00,100.00%,26.67%)" />
|
||||
<rect x="160" y="96" width="32" height="32" fill="hsl(180.00,100.00%,3.33%)" />
|
||||
<rect x="192" y="96" width="32" height="32" fill="hsl(180.00,100.00%,0.00%)" />
|
||||
<rect x="224" y="96" width="32" height="32" fill="hsl(180.00,100.00%,20.00%)" />
|
||||
<rect x="0" y="128" width="32" height="32" fill="hsl(180.00,100.00%,43.33%)" />
|
||||
<rect x="32" y="128" width="32" height="32" fill="hsl(180.00,100.00%,6.67%)" />
|
||||
<rect x="64" y="128" width="32" height="32" fill="hsl(180.00,100.00%,26.67%)" />
|
||||
<rect x="96" y="128" width="32" height="32" fill="hsl(180.00,100.00%,30.00%)" />
|
||||
<rect x="128" y="128" width="32" height="32" fill="hsl(180.00,100.00%,33.33%)" />
|
||||
<rect x="160" y="128" width="32" height="32" fill="hsl(180.00,100.00%,3.33%)" />
|
||||
<rect x="192" y="128" width="32" height="32" fill="hsl(180.00,100.00%,36.67%)" />
|
||||
<rect x="224" y="128" width="32" height="32" fill="hsl(180.00,100.00%,10.00%)" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.3 KiB |
420
js/colorhash.js
Normal file
420
js/colorhash.js
Normal file
@@ -0,0 +1,420 @@
|
||||
/**
|
||||
* Simplified colorhash web frontend.
|
||||
*/
|
||||
|
||||
// TODO - need a way to share palettes between Python and JS
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Colors
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Color {
|
||||
constructor() {
|
||||
if(new.target === Color) {
|
||||
throw new Error("Cannot instantiate abstract class Color");
|
||||
}
|
||||
}
|
||||
|
||||
toString() { return self.toHTMLColor(); }
|
||||
|
||||
toHTMLColor() { throw new Error("Cannot call abstract method toHTMLColor"); }
|
||||
|
||||
toRGB() { throw new Error("Cannot call abstract method toRGB"); }
|
||||
|
||||
toHSL() { throw new Error("Cannot call abstract method toHSL"); }
|
||||
}
|
||||
|
||||
class RGBColor extends Color {
|
||||
constructor(r, g, b) {
|
||||
super();
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
toHTMLColor() {
|
||||
return `rgb(${this.r}, ${this.g}, ${this.b})`;
|
||||
}
|
||||
|
||||
toRGB() {
|
||||
return this;
|
||||
}
|
||||
|
||||
toHSL() {
|
||||
// NOTE - this isn't perfect, but it should be OK for our uses.
|
||||
// We don't really use the toHSL at all anyway, this is really just here for completeness.
|
||||
// example of floating point biting us in the ass:
|
||||
// new HSLColor(120, 50, 25).toRGB().toHSL()
|
||||
// { h: 120, s: 50, l: 25.098039215686274 }
|
||||
let r = this.r / 255;
|
||||
let g = this.g / 255;
|
||||
let b = this.b / 255;
|
||||
|
||||
let max = Math.max(r, g, b);
|
||||
let min = Math.min(r, g, b);
|
||||
|
||||
let h, s;
|
||||
let l = (max + min) / 2;
|
||||
|
||||
if (max === min) {
|
||||
h = s = 0; // achromatic
|
||||
} else {
|
||||
let d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
switch (max) {
|
||||
// not sure if braces are necessary here, this is mostly just a
|
||||
// habit from C/C++ where the `switch` scope is shared
|
||||
case r: {
|
||||
h = (g - b) / d + (g < b ? 6 : 0);
|
||||
}; break;
|
||||
case g: {
|
||||
h = (b - r) / d + 2;
|
||||
}; break;
|
||||
case b: {
|
||||
h = (r - g) / d + 4;
|
||||
}; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
|
||||
return new HSLColor(h * 360, s * 100, l * 100);
|
||||
}
|
||||
}
|
||||
|
||||
class HSLColor extends Color {
|
||||
constructor(h, s, l) {
|
||||
super();
|
||||
this.h = h;
|
||||
this.s = s;
|
||||
this.l = l;
|
||||
}
|
||||
|
||||
toHTMLColor() {
|
||||
return `hsl(${this.h}, ${this.s}%, ${this.l}%)`;
|
||||
}
|
||||
|
||||
toRGB() {
|
||||
// nice little hack we can use to convert to RGB
|
||||
const div = document.createElement("div");
|
||||
div.style.backgroundColor = this.toHTMLColor();
|
||||
const [_, r, g, b] = div
|
||||
.style
|
||||
.backgroundColor
|
||||
.match(/rgb\((\d+), (\d+), (\d+)\)/)
|
||||
.map(Number);
|
||||
return new RGBColor(r, g, b)
|
||||
}
|
||||
|
||||
toHSL() {
|
||||
return this;
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Utilities
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// hahahahahahahaha javascript doesn't even have a zip() function
|
||||
// HAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA "JUST INSTALL A DEPENDENCY"
|
||||
// HAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA
|
||||
const zip = (a, b) => a.map((k, i) => [k, b[i]]);
|
||||
|
||||
function quantize(min, max, steps) {
|
||||
const dist = max - min;
|
||||
return Array.from(
|
||||
{length: steps},
|
||||
(_, i) => min + (i * dist / (steps - 1))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a hash string to Uint8Array.
|
||||
*/
|
||||
function hash2array(hash) {
|
||||
return Array.from(
|
||||
new Uint8Array(
|
||||
hash.match(/../g)
|
||||
.map(e => parseInt(e, 16))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to detect the type of hash or algorithm that has been supplied.
|
||||
* Examples:
|
||||
*
|
||||
* 'sha1' => 'sha1'
|
||||
* 'ae288b06df4a460c37c836e270f9edaabffb7d6d' => 'sha1'
|
||||
* 'e575e0af2fbe9d8cc3a2c13542aa1375d2f75875e55529d0889f9b46' => 'sha224'
|
||||
* 'md5' => 'md5'
|
||||
* 'crc32' => error, not supported
|
||||
*
|
||||
* etc.
|
||||
*/
|
||||
function detectAlgorithm(hashOrAlgo) {
|
||||
const dimensions = {
|
||||
md5: 32,
|
||||
32: "md5",
|
||||
sha1: 40,
|
||||
40: "sha1",
|
||||
sha224: 56,
|
||||
56: "sha224",
|
||||
sha256: 64,
|
||||
64: "sha256",
|
||||
sha384: 96,
|
||||
96: "sha384",
|
||||
sha512: 128,
|
||||
128: "sha512",
|
||||
};
|
||||
|
||||
hashOrAlgo = hashOrAlgo.toLowerCase();
|
||||
|
||||
if(hashOrAlgo.match(/^([0-9a-fA-F]{2})+$/)) {
|
||||
if(dimensions.hasOwnProperty(hashOrAlgo.length)) {
|
||||
return dimensions[hashOrAlgo.length];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if(dimensions.hasOwnProperty(hashOrAlgo)) {
|
||||
return dimensions[hashOrAlgo];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Palettes
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Create a list of HSL
|
||||
*/
|
||||
function hslPalette(start, end) {
|
||||
if(!(start instanceof HSLColor)) {
|
||||
throw new TypeError("expected param 'start' to be instance of HSLColor");
|
||||
}
|
||||
if(!(end instanceof HSLColor)) {
|
||||
throw new TypeError("expected param 'end' to be instance of HSLColor");
|
||||
}
|
||||
const hues = quantize(start.h, end.h, 16);
|
||||
const sats = quantize(start.s, end.s, 16);
|
||||
const lights = quantize(start.l, end.l, 16);
|
||||
|
||||
return zip(zip(hues, sats), lights).map(
|
||||
([[h, s], l]) => new HSLColor(h, s, l)
|
||||
);
|
||||
}
|
||||
|
||||
const GRADIENT_PALETTES = [
|
||||
["red-light", hslPalette(new HSLColor(0, 100, 50), new HSLColor(0, 100, 100))],
|
||||
["red-dark", hslPalette(new HSLColor(0, 100, 0), new HSLColor(0, 100, 50))],
|
||||
//
|
||||
["orange-light", hslPalette(new HSLColor(30, 100, 50), new HSLColor(30, 100, 100))],
|
||||
["orange-dark", hslPalette(new HSLColor(30, 100, 0), new HSLColor(30, 100, 50))],
|
||||
//
|
||||
//["yellow-light", hslPalette(new HSLColor(60, 100, 50), new HSLColor(60, 100, 100))],
|
||||
["yellow-dark", hslPalette(new HSLColor(60, 100, 0), new HSLColor(60, 100, 50))],
|
||||
//
|
||||
//["lime-light", hslPalette(new HSLColor(90, 100, 50), new HSLColor(90, 100, 100))],
|
||||
//["lime-dark", hslPalette(new HSLColor(90, 100, 0), new HSLColor(90, 100, 50))],
|
||||
//
|
||||
["green-light", hslPalette(new HSLColor(120, 100, 50), new HSLColor(120, 100, 100))],
|
||||
["green-dark", hslPalette(new HSLColor(120, 100, 0), new HSLColor(120, 100, 50))],
|
||||
//
|
||||
//["seafoam-light", hslPalette(new HSLColor(150, 100, 50), new HSLColor(150, 100, 100))],
|
||||
//["seafoam-dark", hslPalette(new HSLColor(150, 100, 0), new HSLColor(150, 100, 50))],
|
||||
//
|
||||
["cyan-light", hslPalette(new HSLColor(180, 100, 50), new HSLColor(180, 100, 100))],
|
||||
["cyan-dark", hslPalette(new HSLColor(180, 100, 0), new HSLColor(180, 100, 50))],
|
||||
//
|
||||
//["teal-light", hslPalette(new HSLColor(210, 100, 50), new HSLColor(210, 100, 100))],
|
||||
//["teal-dark", hslPalette(new HSLColor(210, 100, 0), new HSLColor(210, 100, 50))],
|
||||
//
|
||||
["blue-light", hslPalette(new HSLColor(240, 100, 50), new HSLColor(240, 100, 100))],
|
||||
["blue-dark", hslPalette(new HSLColor(240, 100, 0), new HSLColor(240, 100, 50))],
|
||||
//
|
||||
["purple-light", hslPalette(new HSLColor(270, 100, 50), new HSLColor(270, 100, 100))],
|
||||
["purple-dark", hslPalette(new HSLColor(270, 100, 0), new HSLColor(270, 100, 50))],
|
||||
//
|
||||
["magenta-light", hslPalette(new HSLColor(300, 100, 50), new HSLColor(300, 100, 100))],
|
||||
["magenta-dark", hslPalette(new HSLColor(300, 100, 0), new HSLColor(300, 100, 50))],
|
||||
//
|
||||
["pink-light", hslPalette(new HSLColor(330, 100, 50), new HSLColor(330, 100, 100))],
|
||||
["pink-dark", hslPalette(new HSLColor(330, 100, 0), new HSLColor(330, 100, 50))],
|
||||
//
|
||||
["gray-light", hslPalette(new HSLColor(0, 0, 50), new HSLColor(0, 0, 100))],
|
||||
["gray-dark", hslPalette(new HSLColor(0, 0, 0), new HSLColor(0, 0, 50))],
|
||||
];
|
||||
|
||||
const MULTICOLOR_PALETTES = [
|
||||
["rainbow", hslPalette(new HSLColor(0, 100, 50), new HSLColor(360, 0, 50))],
|
||||
["rainbow-reverse", hslPalette(new HSLColor(360, 100, 50), new HSLColor(0, 0, 50))],
|
||||
];
|
||||
|
||||
DEFAULT_PALETTES = [].concat(
|
||||
GRADIENT_PALETTES,
|
||||
MULTICOLOR_PALETTES,
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Matricizer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Matricizer {
|
||||
constructor() {
|
||||
if(new.target === Matricizer) {
|
||||
throw new Error("Cannot instantiate abstract class Matricizer");
|
||||
}
|
||||
}
|
||||
|
||||
matricize(hash) {
|
||||
throw new Error("Cannot call abstract method matricize");
|
||||
}
|
||||
|
||||
choosePalette(hash, palettes = null) {
|
||||
if(!palettes) {
|
||||
palettes = DEFAULT_PALETTES;
|
||||
}
|
||||
const total = hash2array(hash).reduce((acc, curr) => acc + curr, 0);
|
||||
|
||||
return palettes[total % palettes.length][1]
|
||||
}
|
||||
|
||||
static chooseDimensions(hashOrAlgo) {
|
||||
throw new Error("Cannot call abstract method chooseDimensions");
|
||||
}
|
||||
}
|
||||
|
||||
class NibbleMatricizer extends Matricizer {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
matricize(hash) {
|
||||
const dimensions = {
|
||||
md5: [8, 4],
|
||||
sha1: [8, 5],
|
||||
sha224: [8, 7],
|
||||
sha256: [8, 8],
|
||||
sha384: [12, 8],
|
||||
sha512: [16, 8],
|
||||
};
|
||||
|
||||
const hashAlgo = detectAlgorithm(hash);
|
||||
if(hashAlgo === null) {
|
||||
throw new Error(`unable to determine hash algorithm`);
|
||||
}
|
||||
|
||||
const [w, h] = dimensions[hashAlgo];
|
||||
|
||||
const nibbles = hash2array(hash)
|
||||
.map(b => [(b & 0xf0) >> 4, b & 0x0f])
|
||||
.flat();
|
||||
|
||||
if(nibbles.length != w * h) {
|
||||
throw new Error(`invalid hash length: ${hash.length} (${nibbles.length} nibbles)`);
|
||||
}
|
||||
|
||||
const rows = []
|
||||
let cols = []
|
||||
for(const b of nibbles) {
|
||||
cols.push(b);
|
||||
if(cols.length === w) {
|
||||
rows.push(cols);
|
||||
cols = []
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
choosePalette(data, palettes = null) {
|
||||
return super.choosePalette(data, palettes || GRADIENT_PALETTES);
|
||||
}
|
||||
}
|
||||
|
||||
class RandomartMatricizer extends Matricizer {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
matricize(hash) {
|
||||
const dimensions = {
|
||||
md5: [7, 6],
|
||||
sha1: [7, 6],
|
||||
sha224: [8, 7],
|
||||
sha256: [8, 7],
|
||||
sha384: [11, 10],
|
||||
sha512: [11, 10],
|
||||
}
|
||||
|
||||
const hashAlgo = detectAlgorithm(hash);
|
||||
if(hashAlgo === null) {
|
||||
throw new Error(`unable to determine hash algorithm`);
|
||||
}
|
||||
|
||||
const [w, h] = dimensions[hashAlgo];
|
||||
const bytes = hash2array(hash);
|
||||
|
||||
// create an empty 2d array
|
||||
const rows = Array.from(
|
||||
{ length: h },
|
||||
() => Array.from({ length: w }, () => 0)
|
||||
);
|
||||
|
||||
let c = Math.floor(w / 2);
|
||||
let r = Math.floor(h / 2);
|
||||
|
||||
// do the randomart algorithm
|
||||
for(let value of bytes) {
|
||||
for(let i = 0; i < 4; i++) {
|
||||
if(value & 1) {
|
||||
c += 1;
|
||||
} else {
|
||||
c -= 1;
|
||||
}
|
||||
if(value & 2) {
|
||||
r += 1;
|
||||
} else {
|
||||
r -= 1;
|
||||
}
|
||||
c = Math.min(Math.max(c, 0), w - 1);
|
||||
r = Math.min(Math.max(r, 0), h - 1);
|
||||
if(rows[r][c] < 0xf) {
|
||||
rows[r][c] += 1;
|
||||
}
|
||||
value >>= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
choosePalette(data, palettes = null) {
|
||||
return super.choosePalette(data, palettes || MULTICOLOR_PALETTES);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SVG output
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function genSVG(matrix, palette, squareSize = 32) {
|
||||
const colors = matrix.map(
|
||||
row => row.map(v => palette[v])
|
||||
);
|
||||
|
||||
const w = colors[0].length;
|
||||
const h = colors.length;
|
||||
let svg = `<svg width="${w * squareSize}" height="${h * squareSize}" xmlns="http://www.w3.org/2000/svg">\n`;
|
||||
|
||||
for(let r = 0; r < h; r++) {
|
||||
for(let c = 0; c < w; c++) {
|
||||
const x = c * squareSize;
|
||||
const y = r * squareSize;
|
||||
const color = colors[r][c];
|
||||
svg += `<rect x="${x}" y="${y}" width="${squareSize}" height="${squareSize}" fill="${color.toHTMLColor()}" />\n`;
|
||||
}
|
||||
}
|
||||
svg += "</svg>";
|
||||
return svg;
|
||||
}
|
||||
105
js/index.html
Normal file
105
js/index.html
Normal file
@@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Colorhash</title>
|
||||
<style>
|
||||
#error {
|
||||
color: #ff0000;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<p>
|
||||
Create a colorhash image by typing in a hash here
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="text" style="width:400px;" autocomplete="off" id="inputhash"/><br/>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="radio" id="nibble" name="art" checked="checked" />
|
||||
<label for="nibble">Nibbles</label>
|
||||
<input type="radio" id="randomart" name="art" />
|
||||
<label for="randomart">Randomart</label>
|
||||
</td>
|
||||
<td>
|
||||
<button id="createbutton" onclick="createArt()">Create</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
</div>
|
||||
<div id="error">
|
||||
</div>
|
||||
<p>
|
||||
<div id="output">
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<script src="colorhash.js" defer></script>
|
||||
<script>
|
||||
qs = document.querySelector.bind(document);
|
||||
|
||||
function clearError() {
|
||||
qs("#error").innerHTML = '';
|
||||
}
|
||||
|
||||
function setError(error) {
|
||||
qs("#error").innerHTML = `<p>${error}</p>`;
|
||||
}
|
||||
|
||||
function clearOutput() {
|
||||
qs("#output").innerHTML = '';
|
||||
}
|
||||
|
||||
function setOutput(output) {
|
||||
qs("#output").innerHTML = output;
|
||||
}
|
||||
|
||||
function createDownloadLink(svgData) {
|
||||
return "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svgData);
|
||||
}
|
||||
|
||||
function createArt() {
|
||||
clearError();
|
||||
clearOutput();
|
||||
|
||||
const inputHash = qs("#inputhash").value;
|
||||
let matricizer;
|
||||
if(qs("#nibble").checked) {
|
||||
matricizer = new NibbleMatricizer();
|
||||
} else if(qs("#randomart").checked) {
|
||||
matricizer = new RandomartMatricizer();
|
||||
}
|
||||
|
||||
// get matrix
|
||||
let matrix;
|
||||
try {
|
||||
matrix = matricizer.matricize(inputHash);
|
||||
} catch(e) {
|
||||
setError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// get colors
|
||||
const palette = matricizer.choosePalette(inputHash);
|
||||
|
||||
// create SVG
|
||||
const svg = "<p>" + genSVG(matrix, palette) + "</p>";
|
||||
|
||||
const detectedHash = `<p>Detected hash: ${detectAlgorithm(inputHash)}</p>`;
|
||||
const downloadLink = `<p><a href="${createDownloadLink(svg).toString()}">"Save link as" to download</a></p>`
|
||||
setOutput(detectedHash + downloadLink + svg);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user