Whitepaper

Spell checker not included.

Log in to post a comment.

// LL 2021

Canvas.setpenopacity(1);

const tt = new ClippingTurtle();

const text_size = 2; // min=1 max=5 step=0.25
const columns = 3; // min=1 max=4 step=1
const seed=0; // min=0 max=100 step=1

var rng;

const fake_text = new FakeText();
function walk(i) {
    if (i==0) {
        rng = new RNG(seed);
        
        const paper_width = 130;
        const paper_height = paper_width / 8.5 * 11;
        const margin = paper_width * 0.05 / columns;
        const title_size = text_size * 5;
        const subtitle_size = text_size * 2.5;
        const margin_top = margin * 2 + title_size + subtitle_size;
    
        // Draw pages
        tt.jump(-paper_width/2, -paper_height/2);
        drawPaper(tt, paper_width, paper_height, 5);
    
        // Draw title
        for (var t=0; t<3; t++)
        {
            const title_length = ((paper_width - margin * 2) / title_size / 1.5) | 0;
            const title_text = generateRandomText(1, title_length);
            const title_width = title_length * title_size;
            tt.jump(-title_width/2, -paper_height/2 + margin);
            fake_text.print(tt, title_text, title_size);
        }

        // Draw subtitle
        {
            const subtitle_length = ((paper_width - margin * 2) / subtitle_size / 2) | 0;
            const subtitle_text = generateRandomText(1, subtitle_length);
            const subtitle_width = subtitle_length * subtitle_size;
            tt.jump(-subtitle_width/2, -paper_height/2 + margin + title_size);
            fake_text.print(tt, subtitle_text, subtitle_size);
        }

        // Draw columns
        for (var c=0; c<columns; c++) {
            const left = -paper_width/2 + margin + c * (paper_width/columns - margin/columns);
            const top = -paper_height/2 + margin_top;
            const width = paper_width/columns - margin - margin / columns;
            const height = paper_height - margin - margin_top;
            
            drawColumn(tt, left, top, width, height, text_size);
        }
    }

    return false;
}

function drawColumn(turtle, x, y, w, h, text_size) {
    {
        const line_count = (w / text_size) | 0;
        const top_line = (random() * (h / text_size - line_count)) | 0;
        drawFigure(turtle, x, y + top_line * text_size, w, line_count * text_size);
        turtle.setWindow(x, y + top_line * text_size, x + w * 1.01, y + (top_line + line_count) * text_size);
        turtle.drawInsideWindow = false;
    }
    {
        const line_count = (h / text_size) | 0;
        const line_width = (w / text_size) | 0;
        const col_text = generateRandomText(line_count, line_width);
        turtle.jump(x, y);
        fake_text.print(turtle, col_text, text_size);
    }
    turtle.setWindow([]);
}

var available_figures = [];
function drawFigure(turtle, x, y, w, h) {
    const figure_count = 5;
    if (available_figures.length == 0) 
        available_figures = [drawDNA, drawLissajous, drawChart, drawFlower, drawMoire, drawDefault];

    turtle.setWindow(x + w * 0.05, y + h * 0.05, x + w * 0.95, y + h * 0.95);
    turtle.drawInsideWindow = true;

    const figure_index = (random() * available_figures.length) | 0;
    available_figures[figure_index](turtle, x, y, w, h);
    available_figures.splice(figure_index, 1);
}

function drawDefault(turtle, x, y, w, h) {
    const cx = x + w/2;
    const cy = y + h/2;
    const r = 0.45 * Math.min(w, h);
    
    const step = Math.PI * 2 / (10 + random() * 10);
    const add = step + 10 * random();
    for (var a = 0; a < Math.PI * 2; a += step) {
        turtle.jump(cx + r * Math.cos(a), cy + r * Math.sin(a));
        turtle.goto(cx + r * Math.cos(a+add), cy + r * Math.sin(a+add));
    }
}

function drawMoire(turtle, x, y, w, h) {
    for (var t=0; t<=1; t++) {
        const cx = x + w * random();
        const cy = y + h * random();
        const interval = 1.5;
        turtle.down();
        for (var r = interval; r < Math.max(w, h) * 2; r += interval) {
            turtle.jump(cx, cy - r);
            turtle.circle(r);
        }
    }
}

function drawLissajous(turtle, x, y, w, h) {
    const cx = x + w/2;
    const cy = y + h/2;
    const r = 0.45 * Math.min(w, h);
    
    const step = Math.PI * 2 / ((10 + random() * 10)|0);
    const mul = random() * 30.1 + 1;
    turtle.jump(cx + r, cy);
    for (var a = 0; a < Math.PI * 2 * 10; a += step) {
        turtle.goto(cx + r * Math.cos(a), cy + r * Math.sin(a*mul));
    }
}

function drawFlower(turtle, x, y, w, h) {
    const cx = x + w/2;
    const cy = y + h/2;
    const r = 0.25 * Math.min(w, h);
    const r2 = r * 0.8;
    
    const step = Math.PI * 2 / 100;
    const mul = random() * 10 + 1;
    turtle.up();
    for (var a = 0; a < Math.PI * 2 * 2.1; a += step) {
        turtle.goto(cx + r * Math.cos(a) + r2 * Math.cos(a * mul), cy + r * Math.sin(a) + r2 * Math.sin(a * mul));
        turtle.down();
    }
}

function rotX(x, y, a) { return Math.cos(a) * x - Math.sin(a) * y; }
function rotY(x, y, a) { return Math.sin(a) * x + Math.cos(a) * y; }

function getY(oy, cr, dir, offset, a) {
    return oy + dir * Math.sin(offset + a * Math.PI*2) * cr;
}

function drawDNA(turtle, x, y, w, h) {
    turtle.down();
    const offset = random() * Math.PI * 2;
    const ox = x + w/2;
    const oy = y + h/2;
    const cr = w / (5 + 10 * random());
    const r = 0.0025 * w;
    const aa = random() * Math.PI * 2;
    const step = 1/(50 + 30 * random());
    const freq = 3;
    const w2 = w * 1.3;
    const factor = 1 + random() * 2;
    [-1, 1].forEach(dir => {
        var previous = false;
        var index = -2;
        for (var a=0; a<1+step/2; a+=step) {
            const x2 = ox - w2 / 2 + w2 * a;
            const y2 = getY(oy, cr, dir, offset, a * factor);
            const rx = rotX(x2 - ox, y2 - oy, aa) + ox;
            const ry = rotY(x2 - ox, y2 - oy, aa) + oy;
            const current = [rx, ry];
            if (previous) {
                turtle.jump(previous);
                turtle.goto(current);
            }
            previous = current;
            if ((index%freq)==0) {
                turtle.jump(rx, ry-r*5);
                turtle.circle(r*5);
                if (dir == 1) {
                    const y3 = getY(oy, cr, -dir, offset, a * factor);
                    const rx2 = rotX(x2 - ox, y3 - oy, aa) + ox;
                    const ry2 = rotY(x2 - ox, y3 - oy, aa) + oy;
                    turtle.jump(rx2, ry2);
                    turtle.goto(rx, ry);
                }
            } else {
                turtle.jump(rx, ry-r*2);
                turtle.circle(r*2);
            }
            index++;
        }
    });

}

function drawChart(turtle, x, y, w, h) {
    
    const cx = x + w * 0.1;
    const cw = w * 0.8;
    const cy = y + h * 0.9;
    const ch = -h * 0.8;
    
    turtle.down();
    turtle.jump(cx, cy+ch);
    turtle.goto(cx, cy-ch * 0.1);
    turtle.jump(cx-cw * 0.1, cy);
    turtle.goto(cx+cw, cy);
    
    for (var fy=.3; fy<.91; fy+=.3) {
        const step = 1/40;
        turtle.up();
        for (var fx=0, d=true; fx<1.01; fx+=step, d=!d) {
            turtle.goto(cx + fx * cw, cy + fy * ch);
            d ? turtle.down() : turtle.up();
        }
    }
 
     {   
        const step = 1/10;
        const tw = cw * 0.025;
        turtle.down();
        for (var t=step; t<1; t+=step) {
            turtle.jump(cx + t * cw, cy + tw);
            turtle.goto(cx + t * cw, cy - tw);
            turtle.jump(cx - tw, cy + t * ch);
            turtle.goto(cx + tw, cy + t * ch);
        }
    }

    turtle.up();
    const phase = random();
    const step = 1/100;
    for (var fx=0; fx<1.01; fx+=step) {
        const fy = .5 + (Math.cos((fx + phase) * Math.PI * 2) / 2 + Math.cos((fx + phase) * Math.PI * 2 * 2 + phase) / 4 + Math.cos(fx * Math.PI * 2 * 4 + phase) / 8) /2;
        turtle.goto(cx + fx * cw, cy + fy * ch);
        turtle.down();
    }
}

function drawPaper(turtle, width, height, pages) {
    const x = turtle.position()[0], y = turtle.position()[1];
    const step = height / 200;
    turtle.down();
    [[1,0],[1,1],[0,1],[0,0]].forEach(p => turtle.goto(x + width * p[0], y + height * p[1]));
    for (var p=1; p<pages; p++) {
        turtle.up();
        [[1,1,0,0],[1,0,0,0],[1,0,1,0],[0,0,1,0],[0,0,1,1]].forEach(q => {
            turtle.goto(x + width * q[0] + step * (p - q[1]), y + height * q[2] + step * (p - q[3]));
            turtle.down();
        });
    }
}

//function random() { return Math.random(); }
function random() { return rng.nextFloat(); }

function generateRandomText(line_count, line_width) {
    var str = "";
    for (var line=0; line<line_count; line++) {
        if (line > 0) str += '\n';
        var need_space = 0;
        for (var c=0; c<line_width; c++) {
            if (c == line_width-1) need_space = 0;
            if (need_space > 1 && random()<0.2) {
                str += " ";
                need_space = 0;
            } else {
                str += String.fromCharCode(33 + (random()*94)|0);
                need_space++;
            }
        }
    }
    return str;
}

function FakeText() {
    var font = null;
    function generate_font() {
        if (font != null) return;
        font = {};
        for (var c=33; c<127; c++) {
            font[c] = [];
            const count = 2 + random() * 8;
            const h = count / 20 + random() * 0.5;
            for (var i=0; i<count; i++) {
                const line = [ [ random(), 1 - random() * h], [ random(), 1 - random() * h] ];
                font[c].push(line);
            }
        }
    }
    function print_chr(turtle, x, y, chr, char_width, char_height) {
        turtle.down();
        const list = font[chr.charCodeAt(0)];
        if (list !== undefined) {
            list.forEach(line => {
                turtle.jump(x + line[0][0] * char_width * 1.1, y + line[0][1] * char_height);
                turtle.goto(x + line[1][0] * char_width * 1.1, y + line[1][1] * char_height);
            });
        }
    }
    function print_str(turtle, str, size=10) {
        generate_font();
        str = str.toLowerCase();
        const char_height = size;
        const char_width = size;
        var x = turtle.position()[0];
        const left_x = x;
        var y = turtle.position()[1];
        for (var i=0; i<str.length; i++) {
            if (str[i] == '\n') {
                x = left_x;
                y += char_height;
            } else {
                print_chr(turtle, x, y, str[i], char_width*1.2, char_height);
                x += char_width;
            }
        }
        turtle.jump(x, y);
    }
    return {
        print: (turtle, str, size) => print_str(turtle, str, size),
    };
}

///////////////////////////////////////////////////////////////////////////
// Clipping Turtle utility code (minified). Created by Lionel Lemarie 2021
// https://turtletoy.net/turtle/2b46e93a22
///////////////////////////////////////////////////////////////////////////
function ClippingTurtle(){function i(i,s,t){return i-1e-4<=s&&s<=t+1e-4||t-1e-4<=s&&s<=i+1e-4}return new class extends Turtle{constructor(){super(),this.window=[],this.drawInsideWindow=!0}setWindow(i,s,t,n){this.window=[i,s,t,n]}drawWindow(){if(4!=this.window.length)return;const i=this.position(),s=!this.isdown();this.down(),super.jump(this.window[0],this.window[1]),super.goto(this.window[2],this.window[1]),super.goto(this.window[2],this.window[3]),super.goto(this.window[0],this.window[3]),super.goto(this.window[0],this.window[1]),super.jump(i),s&&this.up()}pointInside(i){return i[0]>=this.window[0]&&i[1]>=this.window[1]&&i[0]<=this.window[2]&&i[1]<=this.window[3]}lineInside(i){return this.pointInside(i[0])&&this.pointInside(i[1])}lineOutside(i){return i[0][0]<this.window[0]&&i[1][0]<this.window[0]||i[0][1]<this.window[1]&&i[1][1]<this.window[1]||i[0][0]>this.window[2]&&i[1][0]>this.window[2]||i[0][1]>this.window[3]&&i[1][1]>this.window[3]}clipLine(s,t,n,o,w){const e=(h=this.window[t],d=this.window[n],r=this.window[o],p=this.window[w],u=s[0][0],a=s[0][1],c=s[1][0],l=s[1][1],g=((h*p-d*r)*(u-c)-(h-r)*(u*l-a*c))/((h-r)*(a-l)-(d-p)*(u-c)),I=((h*p-d*r)*(a-l)-(d-p)*(u*l-a*c))/((h-r)*(a-l)-(d-p)*(u-c)),!(isNaN(g)||isNaN(I)||!i(h,g,r)||!i(d,I,p)||!i(u,g,c)||!i(a,I,l))&&[g,I]);var h,d,r,p,u,a,c,l,g,I;return e&&(s[this.pointInside(s[0])^this.drawInsideWindow?0:1]=[...e]),s}goto(i,s){const t=Array.isArray(i)?[...i]:[i,s];if(this.isdown()&&4==this.window.length){var n=[this.position(),[...t]];this.lineInside(n)?this.drawInsideWindow?super.goto(t):super.jump(t):this.lineOutside(n)?this.drawInsideWindow?super.jump(t):super.goto(t):(n=this.clipLine(n,0,1,2,1),n=this.clipLine(n,2,1,2,3),n=this.clipLine(n,0,3,2,3),n=this.clipLine(n,0,1,0,3),super.jump(n[0]),super.goto(n[1]),super.jump(t))}else super.goto(t)}circle(i){const s=this.position(),t=2*Math.PI/100;for(var n=-Math.PI/2;n<=1.501*Math.PI;n+=t)this.goto(s[0]+i*Math.cos(n),s[1]+i+i*Math.sin(n))}}}

// Minified Random Number Generator from https://turtletoy.net/turtle/ab7a7e539e
function RNG(t){return new class{constructor(t){this.m=2147483648,this.a=1103515245,this.c=12345,this.state=t||Math.floor(Math.random()*(this.m-1))}nextFloat(){return this.state=(this.a*this.state+this.c)%this.m,this.state/(this.m-1)}}(t)}