Skip to content

Instantly share code, notes, and snippets.

@tyjvazum
Created December 24, 2023 01:04
Show Gist options
  • Save tyjvazum/f371762b4e808b6b5a523ae6d1928c02 to your computer and use it in GitHub Desktop.
Save tyjvazum/f371762b4e808b6b5a523ae6d1928c02 to your computer and use it in GitHub Desktop.
const colors1 = [
"FF1744",
"F50057",
"D500F9",
"651FFF",
"3D5AFE",
"2979FF",
"00B0FF",
"00E5FF",
"1DE9B6",
"00E676",
"76FF03",
"C6FF00",
"FFEA00",
"FFC400",
"FF9100",
"FF3D00"
];
const colors2 = [
"FFCDD2",
"F8BBD0",
"E1BEE7",
"D1C4E9",
"C5CAE9",
"BBDEFB",
"B3E5FC",
"B2EBF2",
"B2DFDB",
"C8E6C9",
"DCEDC8",
"F0F4C3",
"FFF9C4",
"FFECB3",
"FFE0B2",
"FFCCBC"
];
const colors3 = [
"B71C1C",
"880E4F",
"4A148C",
"311B92",
"1A237E",
"0D47A1",
"01579B",
"006064",
"004D40",
"1B5E20",
"33691E",
"827717",
"F57F17",
"FF6F00",
"E65100",
"BF360C"
];
// https://stackoverflow.com/questions/5560248/programmatically-lighten-or-darken-a-hex-color-or-rgb-and-blend-colors
const pSBC = (p, c0, c1, l) => {
let r,g,b,P,f,t,h,i=parseInt,m=Math.round,a=typeof(c1)=="string";
if(typeof(p)!="number"||p<-1||p>1||typeof(c0)!="string"||(c0[0]!='r'&&c0[0]!='#')||(c1&&!a))return null;
if(!this.pSBCr)this.pSBCr=(d)=>{
let n=d.length,x={};
if(n>9){
[r,g,b,a]=d=d.split(","),n=d.length;
if(n<3||n>4)return null;
x.r=i(r[3]=="a"?r.slice(5):r.slice(4)),x.g=i(g),x.b=i(b),x.a=a?parseFloat(a):-1
}else{
if(n==8||n==6||n<4)return null;
if(n<6)d="#"+d[1]+d[1]+d[2]+d[2]+d[3]+d[3]+(n>4?d[4]+d[4]:"");
d=i(d.slice(1),16);
if(n==9||n==5)x.r=d>>24&255,x.g=d>>16&255,x.b=d>>8&255,x.a=m((d&255)/0.255)/1000;
else x.r=d>>16,x.g=d>>8&255,x.b=d&255,x.a=-1
}return x};
h=c0.length>9,h=a?c1.length>9?true:c1=="c"?!h:false:h,f=this.pSBCr(c0),P=p<0,t=c1&&c1!="c"?this.pSBCr(c1):P?{r:0,g:0,b:0,a:-1}:{r:255,g:255,b:255,a:-1},p=P?p*-1:p,P=1-p;
if(!f||!t)return null;
if(l)r=m(P*f.r+p*t.r),g=m(P*f.g+p*t.g),b=m(P*f.b+p*t.b);
else r=m((P*f.r**2+p*t.r**2)**0.5),g=m((P*f.g**2+p*t.g**2)**0.5),b=m((P*f.b**2+p*t.b**2)**0.5);
a=f.a,t=t.a,f=a>=0||t>=0,a=f?a<0?t:t<0?a:a*P+t*p:0;
if(h)return"rgb"+(f?"a(":"(")+r+","+g+","+b+(f?","+m(a*1000)/1000:"")+")";
else return"#"+(4294967296+r*16777216+g*65536+b*256+(f?m(a*255):0)).toString(16).slice(1,f?undefined:-2)
}
function createImageData(input) {
const b = [];
const result = [];
input.forEach(val => b.push(("00000000" + val.toString(2)).slice(-8)));
if (b.length === 32 || b.length === 8 || b.length == 128) {
let previous = undefined;
b.forEach((value) => {
if (previous != undefined) {
result.push(previous + value + value.split("").reverse().join("") + previous.split("").reverse().join(""));
previous = undefined;
} else {
previous = value;
}
});
Array.from(result).reverse().forEach((element) => {
result.push(element);
});
} else if (b.length === 64) {
let previous = undefined;
b.forEach((value) => {
if (previous != undefined) {
result.push(previous + value + value.split("").reverse().join("") + previous.split("").reverse().join(""));
previous = undefined;
} else {
previous = value;
}
});
} else {
b.forEach(val => result.push(val + val.split("").reverse().join("")));
}
const arr = result.join("").split("");
return arr;
}
function renderIcon(opts, canvas, isolate = false, id, outline) {
const imageData = createImageData(opts.input);
const width = Math.sqrt(imageData.length);
const key1 = parseInt(imageData.slice(0, 4).join(""), 2);
const key2 = parseInt(imageData.slice(4, 8).join(""), 2);
const key3 = parseInt(imageData.slice(8, 12).join(""), 2);
const key4 = parseInt(imageData.slice(12, 16).join(""), 2);
let eyes = false;
const color1 = colors1[key1];
const color2 = colors2[key2];
const color3 = colors3[key3];
const color4 = colors1[key4];
canvas.width = canvas.height = opts.size * opts.scale;
let cc = canvas.getContext('2d');
let grid = process(imageData, width);
grid = build(grid);
grid = replaceSinglesPlus(grid);
const cellSize = opts.scale;
createCanvas(canvas, grid, cellSize, color1, color2, color3);
function process(inputArray, width) {
const outputArray = [];
function createRow(rowIndex) {
const row = inputArray.slice(rowIndex * width, (rowIndex + 1) * width);
const outputRow = [];
function processElement(currentElement, colIndex) {
if (currentElement == 0) {
let isAtEdge = (colIndex == 0 || colIndex == row.length - 1 || rowIndex == 0 || rowIndex == inputArray.length / width - 1);
let isConnectedToEdge = false;
let isSurroundedByOnes = (
(colIndex > 0 && row[colIndex - 1] == 1) &&
(colIndex < row.length - 1 && row[colIndex + 1] == 1) &&
(rowIndex > 0 && inputArray[(rowIndex - 1) * width + colIndex] == 1) &&
(rowIndex < inputArray.length / width - 1 && inputArray[(rowIndex + 1) * width + colIndex] == 1)
);
if (!isSurroundedByOnes) {
isConnectedToEdge = checkForConnectionsToEdge(rowIndex, colIndex, row);
}
if (isAtEdge || isConnectedToEdge) {
outputRow.push("A");
} else {
outputRow.push("C");
}
} else {
outputRow.push("B");
}
}
for (let colIndex = 0; colIndex < row.length; colIndex++) {
processElement(row[colIndex], colIndex);
}
return outputRow;
}
function checkForConnectionsToEdge(rowIndex, colIndex, row) {
const stack = [[rowIndex, colIndex]];
const visited = new Set();
while (stack.length > 0) {
const [r, c] = stack.pop();
visited.add(`${r},${c}`);
if (c == 0 || c == row.length - 1 || r == 0 || r == inputArray.length / width - 1) {
return true;
}
const neighbors = [
[r - 1, c],
[r + 1, c],
[r, c - 1],
[r, c + 1],
];
for (const [nr, nc] of neighbors) {
if (
nr >= 0 &&
nr < inputArray.length / width &&
nc >= 0 &&
nc < row.length &&
inputArray[nr * width + nc] == 0 &&
!visited.has(`${nr},${nc}`)
) {
stack.push([nr, nc]);
}
}
}
return false;
}
for (let rowIndex = 0; rowIndex < inputArray.length / width; rowIndex++) {
outputArray.push(createRow(rowIndex));
}
return outputArray;
}
function build(inputArray) {
const height = inputArray.length;
const width = inputArray[0].length;
function isSurroundedByAsOrBoundary(row, col) {
for (let i = Math.max(0, row - 1); i <= Math.min(height - 1, row + 1); i++) {
for (let j = Math.max(0, col - 1); j <= Math.min(width - 1, col + 1); j++) {
if (i === row && j === col) {
continue;
}
if (inputArray[i][j] !== "A" && !(i === 0 || j === 0 || i === height - 1 || j === width - 1)) {
return false;
}
}
}
return true;
}
function isContiguousBsSurroundedByAsOrBoundary(contiguousBs) {
if (contiguousBs.length > opts.noiseReduction) {
return false;
}
const boundary = new Set();
const set = new Set(contiguousBs.map(([row, col]) => `${row},${col}`));
for (let [row, col] of contiguousBs) {
if (row === 0 || col === 0 || row === height - 1 || col === width - 1) {
boundary.add(row + "," + col);
}
if (row > 0 && inputArray[row - 1][col] !== "B" && !set.has((row - 1) + "," + col)) {
boundary.add((row - 1) + "," + col);
}
if (col > 0 && inputArray[row][col - 1] !== "B" && !set.has(row + "," + (col - 1))) {
boundary.add(row + "," + (col - 1));
}
if (row < height - 1 && inputArray[row + 1][col] !== "B" && !set.has((row + 1) + "," + col)) {
boundary.add((row + 1) + "," + col);
}
if (col < width - 1 && inputArray[row][col + 1] !== "B" && !set.has(row + "," + (col + 1))) {
boundary.add(row + "," + (col + 1));
}
}
for (let [row, col] of contiguousBs) {
let isSurrounded = true;
if (row > 0 && inputArray[row - 1][col] !== "A" && !set.has((row - 1) + "," + col)) {
isSurrounded = false;
}
if (col > 0 && inputArray[row][col - 1] !== "A" && !set.has(row + "," + (col - 1))) {
isSurrounded = false;
}
if (row < height - 1 && inputArray[row + 1][col] !== "A" && !set.has((row + 1) + "," + col)) {
isSurrounded = false;
}
if (col < width - 1 && inputArray[row][col + 1] !== "A" && !set.has(row + "," + (col + 1))) {
isSurrounded = false;
}
if (!isSurrounded) {
return false;
}
}
return true;
}
const bGroups = [];
const visited = new Set();
for (let i = 0; i < height; i++) {
for (let j = 0; j < width; j++) {
if (inputArray[i][j] === "B" && !visited.has(i + "," + j)) {
const contiguousBs = [];
const stack = [[i, j]];
while (stack.length > 0) {
const [row, col] = stack.pop();
if (inputArray[row][col] === "B" && !visited.has(row + "," + col)) {
contiguousBs.push([row, col]);
visited.add(row + "," + col);
for (let k = Math.max(0, row - 1); k <= Math.min(height - 1, row + 1); k++) {
for (let l = Math.max(0, col - 1); l <= Math.min(width - 1, col + 1); l++) {
if (inputArray[k][l] === "B" && !visited.has(k + "," + l)) {
stack.push([k, l]);
}
}
}
}
}
if (isContiguousBsSurroundedByAsOrBoundary(contiguousBs)) {
bGroups.push(...contiguousBs);
}
}
}
}
for (let [row, col] of bGroups) {
inputArray[row][col] = "D";
}
return inputArray;
}
function replaceSinglesPlus(inputArray) {
const width = inputArray[0].length;
const height = inputArray.length;
const bGroups = [];
const visited = new Set();
if (opts.noiseReduction == 32) {
opts.noiseReduction = 2;
}
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
if (inputArray[row][col] === "B" && !visited.has(`${row},${col}`)) {
const bGroup = findBGroup(row, col);
if (bGroup.length <= opts.noiseReduction && validateBGroup(bGroup)) {
bGroups.push(bGroup);
}
}
}
}
for (const bGroup of bGroups) {
for (const [row, col] of bGroup) {
inputArray[row][col] = "E";
}
}
return inputArray;
function findBGroup(row, col, group = []) {
const key = `${row},${col}`;
if (visited.has(key)) {
return group;
}
visited.add(key);
if (inputArray[row][col] === "B") {
group.push([row, col]);
}
if (row > 0 && !visited.has(`${row-1},${col}`) && inputArray[row-1][col] === "B") {
findBGroup(row-1, col, group);
}
if (col > 0 && !visited.has(`${row},${col-1}`) && inputArray[row][col-1] === "B") {
findBGroup(row, col-1, group);
}
if (row < height-1 && !visited.has(`${row+1},${col}`) && inputArray[row+1][col] === "B") {
findBGroup(row+1, col, group);
}
if (col < width-1 && !visited.has(`${row},${col+1}`) && inputArray[row][col+1] === "B") {
findBGroup(row, col+1, group);
}
return group;
}
function validateBGroup(group) {
const groupSet = new Set(group.map(([row, col]) => `${row},${col}`));
for (const [row, col] of group) {
if (row > 0 && !groupSet.has(`${row-1},${col}`) && inputArray[row-1][col] !== "A" && !isEdge(row-1, col)) {
return false;
}
if (col > 0 && !groupSet.has(`${row},${col-1}`) && inputArray[row][col-1] !== "A" && !isEdge(row, col-1)) {
return false;
}
if (row < height-1 && !groupSet.has(`${row+1},${col}`) && inputArray[row+1][col] !== "A" && !isEdge(row+1, col)) {
return false;
}
if (col < width-1 && !groupSet.has(`${row},${col+1}`) && inputArray[row][col+1] !== "A" && !isEdge(row, col+1)) {
return false;
}
}
return true;
}
function isEdge(row, col) {
return row === 0 || col === 0 || row === height-1 || col === width-1;
}
}
function createCanvas(canvas, grid, cellSize) {
const context = canvas.getContext("2d");
for (let i = 0; i < grid.length; i++) {
const row = grid[i];
for (let j = 0; j < row.length; j++) {
const value = row[j];
context.fillStyle = value === "A" ? "#"+color2 : value === "B" ? "#"+color1 : value === "C" ? "#"+color3 : value === "D" ? pSBC(-0.1, "#"+color2) : opts.input.length === 64 ? pSBC(0.1, "#"+color3) : pSBC(0.1, "#"+color3);
context.fillRect(j * cellSize, i * cellSize, cellSize, cellSize);
}
}
return canvas;
}
if (outline) {
const flattened = grid.flat();
flattened.forEach((p, i, a) => {
const row = Math.floor(i / width);
const col = i % width;
if (p === "B" || p === "E") {
if (p === "B") {
cc.fillStyle = pSBC(-0.5, "#"+color1);
} else {
if (opts.input.length == 64) {
cc.fillStyle = pSBC(-0.5, "#"+color3);
} else {
cc.fillStyle = pSBC(-0.5, "#"+color3);
}
}
if (col == 0) {
cc.fillRect(0, row * opts.scale, 1, opts.scale);
}
if (col == width - 1) {
cc.fillRect(((col + 1) * opts.scale) - 1, row * opts.scale, 1, opts.scale);
}
if (row == 0) {
cc.fillRect(col * opts.scale, 0, opts.scale, 1);
}
if (row == width - 1) {
cc.fillRect(col * opts.scale, ((row + 1) * opts.scale) - 1, opts.scale, 1);
}
if (col > 0 && flattened[i - 1] === "A") {
cc.fillRect(((col - 1) * opts.scale) + (opts.scale - 1), row * opts.scale, 1, opts.scale);
}
if (col >= 0 && flattened[i + 1] === "A") {
cc.fillRect((col + 1) * opts.scale, row * opts.scale, 1, opts.scale);
}
if (row > 0 && flattened[i - width] === "A") {
cc.fillRect(col * opts.scale, ((row - 1) * opts.scale) + (opts.scale - 1), opts.scale, 1);
}
if (row >= 0 && flattened[i + width] === "A") {
cc.fillRect(col * opts.scale, (row + 1) * opts.scale, opts.scale, 1);
}
}
if (p === "D") {
cc.fillStyle = pSBC(-0.15, "#"+color2);
if (col == 0) {
cc.fillRect(0, row * opts.scale, 1, opts.scale);
}
if (col == width - 1) {
cc.fillRect(((col + 1) * opts.scale) - 1, row * opts.scale, 1, opts.scale);
}
if (row == 0) {
cc.fillRect(col * opts.scale, 0, opts.scale, 1);
}
if (row == width - 1) {
cc.fillRect(col * opts.scale, ((row + 1) * opts.scale) - 1, opts.scale, 1);
}
if (col > 0 && flattened[i - 1] === "A") {
cc.fillRect(((col - 1) * opts.scale) + (opts.scale - 1), row * opts.scale, 1, opts.scale);
}
if (col >= 0 && flattened[i + 1] === "A") {
cc.fillRect((col + 1) * opts.scale, row * opts.scale, 1, opts.scale);
}
if (row > 0 && flattened[i - width] === "A") {
cc.fillRect(col * opts.scale, ((row - 1) * opts.scale) + (opts.scale - 1), opts.scale, 1);
}
if (row >= 0 && flattened[i + width] === "A") {
cc.fillRect(col * opts.scale, (row + 1) * opts.scale, opts.scale, 1);
}
}
if (p === "C") {
cc.fillStyle = pSBC(-0.15, "#"+color1);
if (col == 0) {
cc.fillRect(0, row * opts.scale, 1, opts.scale);
}
if (col == width - 1) {
cc.fillRect(((col + 1) * opts.scale) - 1, row * opts.scale, 1, opts.scale);
}
if (row == 0) {
cc.fillRect(col * opts.scale, 0, opts.scale, 1);
}
if (row == width - 1) {
cc.fillRect(col * opts.scale, ((row + 1) * opts.scale) - 1, opts.scale, 1);
}
if (col > 0 && flattened[i - 1] === "B") {
cc.fillRect(((col - 1) * opts.scale) + (opts.scale - 1), row * opts.scale, 1, opts.scale);
}
if (col >= 0 && flattened[i + 1] === "B") {
cc.fillRect((col + 1) * opts.scale, row * opts.scale, 1, opts.scale);
}
if (row > 0 && flattened[i - width] === "B") {
cc.fillRect(col * opts.scale, ((row - 1) * opts.scale) + (opts.scale - 1), opts.scale, 1);
}
if (row >= 0 && flattened[i + width] === "B") {
cc.fillRect(col * opts.scale, (row + 1) * opts.scale, opts.scale, 1);
}
}
});
const tempCanvas = document.createElement('canvas');
let offset = 0;
if (opts.input.length === 8 || opts.input.length === 16) {
offset = 16;
} else {
offset = 8;
}
tempCanvas.width = tempCanvas.height = (opts.size * opts.scale) + offset;
dd = tempCanvas.getContext("2d");
dd.drawImage(canvas, opts.scale, opts.scale);
canvas = tempCanvas;
cc = dd;
}
const wrapper = document.createElement('div');
wrapper.style.display = "inline";
wrapper.style.margin = "4px";
if (opts.input.length === 32 || opts.input.length === 64 || opts.input.length === 128) {
wrapper.style.padding = "7px";
}
if (opts.input.length === 8 || opts.input.length === 32) {
canvas.style.border = "4px solid " + pSBC(-0.15, "#"+color2);
}
wrapper.style.background = "#"+color2;;
wrapper.style.width = (canvas.width*1.75)+"px";
wrapper.style.height = (canvas.height*1.75)+"px";
wrapper.style.textAlign = "center";
wrapper.style.display = "flex";
wrapper.style.verticalAlign = "middle";
wrapper.style.alignItems = "center";
wrapper.style.float = "left";
canvas.style.margin = "auto";
wrapper.append(canvas);
return wrapper;
}
function createIcon(opts, id) {
const canvas = document.createElement('canvas');
const wrap = document.createElement('div');
const icon = renderIcon(opts, canvas, true, id, true);
wrap.append(icon);
return wrap;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment