Fuzzy lighting

Circular light source + multiple rays per point

Log in to post a comment.

const light_Y = 25; // min=0, max=35, step=1
const light_R = 20; // min=1, max=25, step=1
const N_points = 1000000; 
const stroke_len = 1;
const N_rays = 16; // min=1, max=64, step=1
const fade = 1; // min=0, max=1, step=1 (False, True)
const max_D = 170; // min=100, max=250, step= 1
const show_light = 1; // min=0, max=1, step=1 (True circle source, Candle)

let R = light_R;
let R2 = R * R;

Canvas.setpenopacity(-0.5);

const t = new Turtle();

const light = [0, -light_Y];

const walls = [[[-75, -25], [-75, 25]], [[-80, -25], [-75, -25]],
               [[75, -25], [75, 25]], [[75, -25], [80, -25]],
               [[-25, -75], [25, -75]],
               [[-25, 50], [25, 50]],
               [[-75, 75], [-50, 75]], [[-50, 75], [-50, 100]],
               [[75, 75], [50, 75]], [[50, 75], [50, 100]]];

for (let i = 0; i < walls.length; i++) {
    if (walls[i][0][0] == walls[i][1][0]) {
        [walls[i][0][1], walls[i][1][1]] = [Math.min(walls[i][0][1], walls[i][1][1]), Math.max(walls[i][0][1], walls[i][1][1])]
    }
    else {
        [walls[i][0][0], walls[i][1][0]] = [Math.min(walls[i][0][0], walls[i][1][0]), Math.max(walls[i][0][0], walls[i][1][0])]
    }
} 

if (show_light == 0) { // draw true light circle
    for (let r = 0.1; r <= R; r += 0.05) {
        t.jump(light[0] - r, light[1]);
        t.seth(-90);
        t.circle(r);
    }
}
else { // draw candle
    let fire_r = Math.max(2, light_R / 5);
    
    for (let r = 0.1; r <= fire_r; r += 0.05) {
        t.jump(light[0] - r, light[1]);
        t.seth(-90);
        t.circle(r);
    }
    
    let fire_h = 2.5 * fire_r;
    
    for (let dy = 0; dy <= fire_h; dy += 0.05) {
        let dx = fire_r * (1 - dy / fire_h)**0.6;
        let y = light[1] - dy - 1;
        let dy1 = light[1] - y;
        
        if (dy1 <= fire_r) {
            let dr = Math.sqrt(fire_r**2 - dy1**2);
            
            t.jump(-dx, y);
            t.goto(-dr, y);
    
            t.jump(+dr, y);
            t.goto(+dx, y);
        }
        else {
            t.jump(-dx, y);
            t.goto(+dx, y);
        }
    }
    
    let candle_r = 4;
    let candle_y = light[1] + fire_r + 1
    
    for (let y = candle_y; y < 50; y += 0.05) {
        t.jump(-candle_r, y);
        t.goto(+candle_r, y);
    }
    
    for (let x = -0.5; x <= 0.5; x += 0.05) {
        t.jump(x, candle_y);
        t.goto(x, light[1] + fire_r);
    }
}

function intersects(p1, p2, w1, w2) {
    if (w1[0] == w2[0]) {
        if (((p1[0] < w1[0]) && (p2[0] < w1[0])) || ((p1[0] > w1[0]) && (p2[0] > w1[0]))) {
            return false;
        }
        else {
            let y = p1[1] + (p2[1] - p1[1]) / (p2[0]- p1[0]) * (w1[0] - p1[0]);
            
            return (w1[1] < y) && (y < w2[1]);
        }
    }
    else {
        if (((p1[1] < w1[1]) && (p2[1] < w1[1])) || ((p1[1] > w1[1]) && (p2[1] > w1[1]))) {
            return false;
        }
        else {
            let x = p1[0] + (p2[0] - p1[0]) / (p2[1]- p1[1]) * (w1[1] - p1[1]);
            
            return (w1[0] < x) && (x < w2[0]);
        }
    }
}

function walk(frame) {
    let x = -100 + 200 * Math.random();
    let y = -100 + 200 * Math.random();
    let p = [x, y];

    let dx = light[0] - x, dy = light[1] - y;
    let d2 = dx * dx + dy * dy;
    
    let n = 0;
    
    for (let i = 0; i < N_rays; i++) {
        let phi = 2 * Math.PI * Math.random();
        let light_p = [light[0] + R * Math.cos(phi), light[1] + R * Math.sin(phi)];
        
        let ok = true;
        for (let w of walls) {
            if (intersects(light_p, p, w[0], w[1])) {
                ok = false;
                break;
            }
        }
        
        if (ok) {
            n++;
        }
    }
    
    if (n > 0) {
        let d = Math.sqrt(d2);
        let s = stroke_len / d * (n / N_rays);
        
        if (fade == 1) {
            s *= Math.max(0.0, (1 - d / max_D))**2;
        }
        
        [dx, dy] = [dx * s, dy * s];
        
        t.jump(x, y);
        t.goto(x + dx, y + dy);
    }

    return frame < N_points;
}