const chroma = require( "chroma-js" );
const readline = require( "readline" );
const DEGREES_PER_BIN = 16;
const COLOUR_RADIUS = 15;
const COLOUR_WIDTH = 35;
const COLOUR_HEIGHT = 35;
const COLOUR_SEP_X = 5;
const COLOUR_SEP_Y = 5;
const THRESHOLD_SIMILARITY = 7;
const THRESHOLD_CHROMA = 10;
const main = function() {
read_hex_codes()
.then( bin_by_hue )
.then( sort_by_lightness )
.then( remove_colours )
.then( write_hex_codes );
}
const read_hex_codes = function() {
return new Promise( function( resolve, reject ) {
var colours = [];
const r = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
r.on( 'line', function( line ) {
colours.push( line );
}).on( 'close', function() {
resolve( colours );
});
});
}
const bin_by_hue = function( colours ) {
return new Promise( function( resolve, reject ) {
var bins = [];
colours.forEach( function( colour ) {
const cs = chroma( colour ).lch();
const c = Math.round( cs[ 1 ] );
const h = chroma( colour ).hsv()[ 0 ];
var bin = 0;
if( c > THRESHOLD_CHROMA ) {
bin = ~~(h / DEGREES_PER_BIN) + 1;
}
if( typeof bins[ bin ] == 'undefined' ) {
bins[ bin ] = [];
}
bins[ bin ].push( colour );
});
resolve( bins );
});
}
const sort_by_lightness = function( bins ) {
return new Promise( function( resolve, reject ) {
var result = [];
bins.forEach( function( bin ) {
var sorted_bin = [];
while( bin.length > 0 ) {
var min = 1;
var darkest;
bin.forEach( function( colour ) {
var lum = chroma( colour ).luminance();
if( lum < min ) {
min = lum;
darkest = colour;
}
});
var index = bin.indexOf( darkest );
bin.splice( index, 1 );
sorted_bin.push( darkest );
}
result.push( sorted_bin )
});
resolve( result );
});
}
const remove_colours = function( bins ) {
return new Promise( function( resolve, reject ) {
var resolved = [];
bins.forEach( function( bin ) {
var modified = true;
while( modified ) {
modified = false;
bin = bin.filter( function( colour, index ) {
var retain = true;
var hue = chroma( colour ).hsl();
if( hue[1] > 0.6 && hue[2] > 0.7 ) {
retain = false;
}
else if( index + 1 < bin.length ) {
const distance = chroma.distance( colour, bin[ index + 1 ] );
retain = distance > THRESHOLD_SIMILARITY;
}
if( retain === false ) {
modified = true;
}
return retain;
});
}
resolved.push( bin );
});
resolve( resolved );
});
}
const write_hex_codes = function( bins ) {
var x = COLOUR_SEP_X + COLOUR_RADIUS;
bins
.filter( n => Object.keys( n ).length !== 0 )
.forEach( function( bin ) {
let y = COLOUR_RADIUS;
bin.forEach( function( colour ) {
y -= (COLOUR_HEIGHT + COLOUR_SEP_Y);
console.log(
'<circle cx="' + x +
'" cy="' + y +
'" r="' + COLOUR_RADIUS +
'" style="fill:' + colour + '"/>'
);
});
x += (COLOUR_WIDTH + COLOUR_SEP_X);
});
}
main();