xxxxxxxxxx
96
/*
* Draw a text (wireframe) using a LOGO / TURTLE type language with commands encoded into a bitfield
* the three characters + space are all encoded into a single 32-bit value as a bitfield
* the commands are embedded as 3-bit, commands move and draw in four directions + bottom left diagonal
* there is also one command which move right and assign a color
* it is used to skip to next char by using background color.
*
* The trick is to start from somewhere which has the maximum efficiency in term of number of commands (or type of commands)
* so here i start from somewhere which only require the 'move right but don't draw' instruction to draw
* the whole text, there is still some commands left to do more stuff if required
*
* 8 possible commands in total can be encoded that way and 11 commands can be fetched for every 32-bit values
*
* This code can be converted to assembly language for a very efficient way codesize wise to draw a text / logo
* in assembly the function table is turned into some code next to a label which assign registers (could also be swapping)
* and then jump again to draw code, the initial jump (fetch instruction & call) could be done as 2/3 instructions (logic OP, direct short jump)
*/
let lineLength = 64
let defaultBrightness = 255
// a series of commands as 32-bit values to draw whatever
let stack = [
3883744834
]
let index = 0
function computeStartPosition() {
// compute start drawing position given as a precomputed index (a single instruction on x86)
let x = width / 2 - lineLength * 5 / 2 + lineLength
let y = height / 2 + lineLength / 2
// fix the pixels to the grid
x = Math.floor(x)
y = Math.floor(y)
index = (x + y * width) * 4
//
}
function setup() {
createCanvas(512, 512);
background(0);
computeStartPosition()
drawOnce()
}
let dummyf = () => { return { x: 0, y: 0 } }
// the jump table where each entry would basically be a label + simple register allocation for x,y with ASM code for example
let f = [
() => { return { x: -1, y: 0 } }, // move left
() => { return { x: 0, y: 1 } }, // move down
() => { return { x: 0, y: -1 } }, // move up
() => { return { x: 1, y: 0, c: 0 } }, // move right with color 0 (if background is 0 this is basically move but don't draw)
() => { return { x: -1, y: 1 } }, // move in diagonal bottom left
dummyf,
dummyf,
() => { return { x: 1, y: 0 } } // move right
]
function drawOnce() {
loadPixels()
while (v = stack.pop()) {
for (let l = 0; l < 11; l += 1) {
let g = v & 7 // get the virtual 'instruction'
let r = f[g]() // basically a jump table: jmp g
let dx = r.x
let dy = r.y
// either a color or the default color (white) for each new line
// this can be used as a 'move but do not draw' instruction if color is the same as the back color
let c = (r.c != 0) ? defaultBrightness : 0
// draw segment based on the instructions result
for (let i = 0; i < lineLength; i += 1) {
pixels[index + 0] = c
pixels[index + 1] = c
pixels[index + 2] = c
// move (note: mul is not necessary if jump table code is modified)
// this could also be done as a single OP (needs change to the jump table code)
index += dx * 4
index += dy * 512 * 4
}
v >>= 3 // move to the next instruction
}
}
updatePixels()
}
function draw() {
}