Skip to content

Instantly share code, notes, and snippets.

@greggman
Last active July 23, 2025 23:10
Show Gist options
  • Save greggman/8e4fee18b6acae61ebfff249692ab842 to your computer and use it in GitHub Desktop.
Save greggman/8e4fee18b6acae61ebfff249692ab842 to your computer and use it in GitHub Desktop.
Mipmap Diagram
svg { width: 100%; display: block; }
/*bug-in-github-api-content-can-not-be-empty*/
const range = (num, fn) => new Array(num).fill(0).map((_, i) => fn(i));
const roundUp = (v, a) => Math.ceil(v / a) * a;
const hsl = (h, s, l) => `hsl(${h * 360 | 0}, ${s * 100}%, ${l * 100 | 0}%)`;
function hLine({x1, x2, y, stroke}) {
return [
['line', {x1, x2, y1: y, y2: y, stroke}],
['line', {x1, x2: x1 + 1, y1: y, y2: y + 0.5, stroke}],
['line', {x1, x2: x1 + 1, y1: y, y2: y - 0.5, stroke}],
['line', {x1: x2, x2: x2 - 1, y1: y, y2: y + 0.5, stroke}],
['line', {x1: x2, x2: x2 - 1, y1: y, y2: y - 0.5, stroke}],
];
}
function makeMap(size) {
const numRows = Math.ceil(Math.log2(size));
const rowSize = 10;
const width = 100;
const height = (numRows) * rowSize;
const kBlockSize = 16;
const rows = range(numRows, rowNdx => {
const numTexels = Math.max(1, size >> rowNdx);
const blockSize = kBlockSize >> rowNdx;
const numInRow = roundUp(numTexels, blockSize);
const texelSize = width / numTexels;
const prevNumTexels = Math.max(1, size >> (rowNdx - 1));
const prevTexelSize = width / prevNumTexels;
const rowHue = rowNdx / numRows;
return ['g', {transform: `translate(0 ${rowNdx * rowSize})` }, ...range(numInRow, tx => {
const x1 = (tx + 0.5) * texelSize;
const u = (tx + 0.5) / numTexels;
const tu = u * prevNumTexels - 0.5;
const sx = (Math.floor(tu) + 0.5) * prevTexelSize;
const ex = (Math.ceil(tu) + 0.5) * prevTexelSize;
const texelHue = tx < numTexels ? hsl(rowHue + (tx / blockSize | 0) * 0.78, 1, 0.8) : 'rgb(0 0 0 / 0)';
return [
['rect', { x: tx * texelSize, y: 0, width: texelSize, height: rowSize * 0.66, stroke: '#000', fill: texelHue }],
...(rowNdx > 0 && tx < numTexels ? [
['line', { x1, x2: x1, y1: rowSize * 0.25, y2: rowSize * -0.5, stroke: '#000' }],
...hLine({ x1: sx, x2: ex, y: rowSize * -0.5, stroke: '#000' }),
] : [])
];
}).flat()];
});
document.body.append(
makeElem(['svg', { viewBox: `0 0 ${width * 2} ${height}` },
['g', { 'stroke-width': '0.1' }, ...rows],
])
);
}
for (let w = 23; w > 2; --w) {
log(`${w}x1`);
makeMap(w);
//break;
}
function log(...args) {
const elem = document.createElement('pre');
elem.textContent = args.join(' ');
document.body.appendChild(elem);
}
/**
* Implements JsonML *like* element creation (http://www.jsonml.org/)
*
* The major difference is this takes event handlers for `on` functions
* and supports nested attributes? Also allows elements.
*/
function makeElem(elemSpec, creatorFn) {
const tag = elemSpec[0];
if (tag instanceof Node) {
return tag;
}
if (tag === 'svg') {
const kSVG = "http://www.w3.org/2000/svg";
creatorFn = tag => document.createElementNS(kSVG, tag);
}
const elem = creatorFn ? creatorFn(tag) : document.createElement(tag);
let firstChildNdx = 1;
if (typeof elemSpec[1] !== Node && typeof elemSpec[1] !== 'string' && !Array.isArray(elemSpec[1])) {
firstChildNdx = 2;
for (const [key, value] of Object.entries(elemSpec[1])) {
if (typeof value === 'function' && key.startsWith('on')) {
const eventName = key.substring(2).toLowerCase();
elem.addEventListener(eventName, value, {passive: false});
} else if (typeof value === 'object') {
for (const [k, v] of Object.entries(value)) {
elem[key][k] = v;
}
} else {
elem.setAttribute(key, value);
}
}
}
for (let ndx = firstChildNdx; ndx < elemSpec.length; ++ndx) {
const v = elemSpec[ndx];
if (typeof v === 'string') {
elem.appendChild(document.createTextNode(v));
} else if (v instanceof Node) {
elem.appendChild(v);
} else {
elem.appendChild(makeElem(v, creatorFn));
}
}
return elem;
}
{"name":"Mipmap Diagram","settings":{},"filenames":["index.html","index.css","index.js"]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment