Skip to content

Instantly share code, notes, and snippets.

@KillyMXI
Last active December 30, 2022 12:38
Show Gist options
  • Save KillyMXI/936450451f76e29e5c1f04604c453c1a to your computer and use it in GitHub Desktop.
Save KillyMXI/936450451f76e29e5c1f04604c453c1a to your computer and use it in GitHub Desktop.
// #region DIGRAMS ===============================================================================
function computeDigramColors(
/** @type { Uint8Array } */ uint8Array,
paletteKind = 'p5_lerp',
blendingMiddlePoint = 0.5
) {
const digramStats = Array.from({ length: 65536 }, () => ({
count: 0, // digram occurrences
offsetAcc: 0.0 // accumulator to compute average position
}));
const pairsNumber = uint8Array.length - 1;
let maxDigramCount = 0;
// loop over input once
for (let i = 0; i < pairsNumber; i++) {
const digram = uint8Array[i] * 256 + uint8Array[i+1];
const stat = digramStats[digram];
stat.count++;
stat.offsetAcc += i;
if (stat.count > maxDigramCount) {
maxDigramCount = stat.count;
}
}
const colors = [];
const palette = palettes[paletteKind];
// loop over 256*256 digram variants once
for (let i = 0; i < 65536; i++) {
const stat = digramStats[i];
const avgOffset = stat.offsetAcc / (pairsNumber || 1) / (stat.count || 1);
const pixelColor = (paletteKind === 'p5_lerp')
? lerpColor(startColor, endColor, avgOffset)
: color(sliceByLength(palette, 4 * Math.trunc(avgOffset * 255), 3));
const amplitude = Math.log10(stat.count) / Math.log10(maxDigramCount);
// approximate HSL lightness by blending with black or white color depending on the amplitude
const blendedColor = (amplitude < blendingMiddlePoint)
? lerpColor(blackColor, pixelColor, amplitude / blendingMiddlePoint)
: lerpColor(pixelColor, whiteColor, (amplitude - blendingMiddlePoint) / (1 - blendingMiddlePoint))
colors.push(red(blendedColor), green(blendedColor), blue(blendedColor), 0x00);
}
return colors;
}
// #endregion
// #region LAYERED DISTRIBUTION ==================================================================
function computeLayeredDistributionColors(
/** @type { Uint8Array } */ uint8Array,
paletteKind = 'p5_lerp',
blendingMiddlePoint = 0.5
) {
const layerStats = Array.from({ length: 256 }, () => ({
maxCount: 0, // max byte occurrence number in the layer
byteCounts: Array.from({ length: 256 }, () => 0) // each byte occurrence numbers in the layer
}));
const layerSize = Math.ceil(uint8Array.length / 256);
// loop over input once
for (let i = 0; i < uint8Array.length; i++) {
const byte = uint8Array[i];
const layerIndex = Math.floor(i / layerSize);
const stat = layerStats[layerIndex];
const newCount = ++stat.byteCounts[byte];
if (newCount > stat.maxCount) {
stat.maxCount = newCount;
}
}
let maxCount = 0; // max byte occurrence number across all layers
// loop over 256 layers once
for (let layerIndex = 0; layerIndex < 256; layerIndex++) {
const stat = layerStats[layerIndex];
if (stat.maxCount > maxCount) {
maxCount = stat.maxCount;
}
}
const colors = [];
const palette = palettes[paletteKind];
// loop over 256*256 layer/byte pairs once
for (let layerIndex = 0; layerIndex < 256; layerIndex++) {
const stat = layerStats[layerIndex];
const pixelColor = (paletteKind === 'p5_lerp')
? lerpColor(startColor, endColor, layerIndex / 255)
: color(sliceByLength(palette, 4 * layerIndex, 3));
for (let byte = 0; byte < 256; byte++) {
const byteCount = stat.byteCounts[byte];
const amplitude = Math.log10(byteCount) / Math.log10(maxCount);
// approximate HSL lightness by blending with black or white color depending on the amplitude
const blendedColor = (amplitude < blendingMiddlePoint)
? lerpColor(blackColor, pixelColor, amplitude / blendingMiddlePoint)
: lerpColor(pixelColor, whiteColor, (amplitude - blendingMiddlePoint) / (1 - blendingMiddlePoint))
colors.push(red(blendedColor), green(blendedColor), blue(blendedColor), 0x00);
}
}
return colors;
}
// #endregion
// #region UTILS =================================================================================
function sliceByLength (array, start, length) {
return array.slice(start, start + length);
}
// #endregion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment