Fortunately the Xiaolin Wu algorithm works perfectly, see below!
// This pattern creates a rectangular framebuffer
// and does its own integer coordinate mapping
// and draws a few lines using Xiaolin Wu's antialiased line algorithm.
var blur = 0
export function sliderMotion_Blur(v) {
blur = v
}
// Duplicate PixelBlaze 'Mapper' functionality without normalizing
var width = 8
var height = 32
var coordmap = array(pixelCount)
for (index=0; index<pixelCount; index++) {
y = floor(index / width)
x = index % width
x = y % 2 == 1 ? width - 1 - x : x // I have a zigzag 8x32 LED matrix
coords = array(2)
coords[0] = x
coords[1] = y
coordmap[index] = coords
}
// RGB framebuffer
// ... Storage
var Pr = array(width); for (i=0; i<width; i++) Pr[i] = array(height)
var Pg = array(width); for (i=0; i<width; i++) Pg[i] = array(height)
var Pb = array(width); for (i=0; i<width; i++) Pb[i] = array(height)
// ... Increment a pixel, with 'alpha' multiplication here to keep WuLine sane
function PixelAdd(x,y,a,r,g,b) {
x = floor(x)
y = floor(y)
if (x >= 0 && x < width && y >= 0 && y < height) {
Pr[x][y] = clamp(Pr[x][y] + r*a, 0, 1)
Pg[x][y] = clamp(Pg[x][y] + g*a, 0, 1)
Pb[x][y] = clamp(Pb[x][y] + b*a, 0, 1)
}
}
// Xiaolin Wu's antialiased line algorithm
// Adapted from https://gist.github.com/polyamide/3f33cb4dc69e22fbf8b66cee39b78d60
// to replace utility functions with PixelBlaze built-ins
function WuLine(x0, y0, x1, y1, r, g, b) {
if (x0 == x1 && y0 == y1) return
steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
tmp = y0; y0 = x0; x0 = tmp;
tmp = y1; y1 = x1; x1 = tmp;
}
if (x0 > x1) {
tmp = x0; x0 = x1; x1 = tmp;
tmp = y0; y0 = y1; y1 = tmp;
}
dx = x1 - x0;
dy = y1 - y0;
gradient = dy / dx;
xEnd = round(x0);
yEnd = y0 + gradient * (xEnd - x0);
xGap = 1 - frac(x0 + 0.5);
xPx1 = xEnd;
yPx1 = trunc(yEnd);
if (steep) {
PixelAdd(yPx1, xPx1, 1 - frac(yEnd) * xGap, r, g, b )
PixelAdd(yPx1 + 1, xPx1, frac(yEnd) * xGap, r, g, b )
} else {
PixelAdd(xPx1, yPx1, 1 - frac(yEnd) * xGap, r, g, b )
PixelAdd(xPx1, yPx1 + 1, frac(yEnd) * xGap, r, g, b )
}
intery = yEnd + gradient;
xEnd = round(x1);
yEnd = y1 + gradient * (xEnd - x1);
xGap = frac(x1 + 0.5);
xPx2 = xEnd;
yPx2 = trunc(yEnd);
if (steep) {
PixelAdd(yPx2, xPx2, 1 - frac(yEnd) * xGap, r, g, b )
PixelAdd(yPx2 + 1, xPx2, frac(yEnd) * xGap, r, g, b )
} else {
PixelAdd(xPx2, yPx2, 1 - frac(yEnd) * xGap, r, g, b )
PixelAdd(xPx2, yPx2 + 1, frac(yEnd) * xGap, r, g, b )
}
if (steep) {
for (x = xPx1 + 1; x <= xPx2 - 1; x++) {
PixelAdd(trunc(intery), x, 1 - frac(intery), r, g, b )
PixelAdd(trunc(intery) + 1, x, frac(intery), r, g, b )
intery = intery + gradient;
}
} else {
for (x = xPx1 + 1; x <= xPx2 - 1; x++) {
PixelAdd(x, trunc(intery), 1 - frac(intery), r, g, b )
PixelAdd(x, trunc(intery) + 1, frac(intery), r, g, b )
intery = intery + gradient
}
}
}
// Clear (or fade if blur enabled) framebuffer
// and render a few bouncy lines
export function beforeRender(delta) {
for (i=0; i<width; i++) {
for (j=0; j<height; j++) {
Pr[i][j] = blur ? Pr[i][j]/2 : 0
Pg[i][j] = blur ? Pg[i][j]/2 : 0
Pb[i][j] = blur ? Pb[i][j]/2 : 0
}
}
t1 = time(.03)
t2 = time(.05)
t3 = time(.07)
t4 = time(.11)
t5 = time(.13)
wm1 = width-1
hm1 = height-1
WuLine(
wm1 * wave(t1),
hm1 * triangle(t2),
wm1 * wave(t3),
hm1 * wave(t4),
triangle(t5), wave(t3)/2, square(t1,0.5)
)
WuLine(
wm1 * (1-wave(t3)),
hm1 * wave(t4),
wm1 * wave(t2),
hm1 * triangle(t5),
0, wave(t1), 1-wave(t1)
)
WuLine(
wm1 * wave(t4),
hm1 * triangle(t1),
wm1 * wave(t5),
hm1 * wave(t2),
0.75,0.5,-2 // this line subtracts blue
)
}
// Draw from the framebuffer
export function render(index) {
coords = coordmap[index]
x = coords[0]
y = coords[1]
r = Pr[x][y]
g = Pg[x][y]
b = Pb[x][y]
rgb(r*r,g*g,b*b)
}