-
-
Save tyjvazum/f371762b4e808b6b5a523ae6d1928c02 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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