Entangled v2

Trying to see what this technique could be used for.

Log in to post a comment.

// LL 2021

Canvas.setpenopacity(1);
const turtle = new Turtle();

const strings = 3; // min=1 max=10 step=1
const length = 1000; // min=1 max=10000 step=1
const radius = 90; // min=1 max=100 step=1
const width = 3; // min=0.1 max=10 step=0.1
const seed = 0; // min=0 max=100 step=1
const style = 3; // min=0 max=3 step=1 (Preview,Blank,Stripes,Hatching)

const camera_angle = 0.14; // min=0 max=1 step=0.01

function walk(i, t) {
    if (i == 0) {
        camera_angle_t = camera_angle + t;
        const length_t = length * t;
        rng = undefined;
        polygons = new Polygons();

        list = [];
        for (var k=0; k<strings; k++) {
            var ro = radius * (1 - k / strings);//(random() * 0.7 + 0.3);
            var f =  0.05;
            var pos = normalize3([random() - .5, random() - .5, random() - .5]);
            var ppos = normalize3(add3(pos, [random() - .5, random() - .5, random() - .5]));
            var p0, p1, p2, p3;

            for (var j=0; j<length_t+1; j++) {
                const a = Math.sin(j / length_t * Math.PI * 2) / 300;
                const rf = 0.2;
                const ra = 10;
                const r = (((0.5 * Math.sin(j / length_t * Math.PI * 2 * ra)) + 0.5) * rf + 1 - rf) * ro;
                
                if (j == 0) {
                    p1 = p2 = mulf(project(pos[0], pos[1], pos[2]), r);
                }
                
                var dir = normalize3(sub3(pos, ppos));
                dir = rotV(dir, pos, a);
                ppos = [...pos];
                pos = normalize3(add3(pos, mulf(dir, f)));

                const xyz = mulf(project(pos[0], pos[1], pos[2]), r);
                const pxyz = mulf(project(ppos[0], ppos[1], ppos[2]), r);
                const pv = mulf(normalize3([xyz[1] - pxyz[1], pxyz[0] - xyz[0], 0]), width);

                p0 = p1; p3 = p2;
                p1 = add3(xyz, pv);
                p2 = sub3(xyz, pv);
                
                if (j == 0) {
                    const mx = (p0[0] + p3[0]) / 2;
                    const my = (p0[1] + p3[1]) / 2;
                    p0 = [mx, my]; p3 = [mx, my];
                }
                if (length_t - j < 1) {
                    const mx = (p1[0] + p2[0]) / 2;
                    const my = (p1[1] + p2[1]) / 2;
                    p1 = [mx, my]; p2 = [mx, my];
                }

                const obj = { xyz:xyz, ppos:ppos, pos:pos, points: [[...p0], [...p1], [...p2], [...p3]] };
                list.push(obj);
            }
        }
        
        list.sort(function(a, b) { return a.xyz[2] - b.xyz[2]; });
    }
    
    if (list.length < 1) return false;

    const obj = list.shift();
    
    const hmin = 0.1, hmax = 0.9;
    var h = 0;
    if (style == 3) {
        var dist = 1 - (obj.xyz[2] + radius) / radius / 2;
        dist *= 1 - Math.sqrt(obj.xyz[0] * obj.xyz[0] + obj.xyz[1] * obj.xyz[1]) / radius / 3;
        h = hmin + dist * (hmax-hmin);
    }
    drawPoints(obj.points, h);

    return true;
}

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 drawPoints(dpoints, hatching=0, angle=Math.PI/4) {
    if (style == 0) {
        turtle.jump(dpoints[0]); turtle.goto(dpoints[1]);
        turtle.jump(dpoints[2]); turtle.goto(dpoints[3]);
    } else {
        const p1 = polygons.create();
        p1.addPoints(...dpoints);
        if (style == 2) p1.addOutline();
        p1.dp.push(p1.cp[0], p1.cp[1]);
        p1.dp.push(p1.cp[2], p1.cp[3]);
        if (hatching) p1.addHatching(-angle, hatching);
        polygons.draw(turtle, p1, true);
    }
}

function project(x, y, z) {
    const rx = rotX(x, z, camera_angle_t * Math.PI * 2);
    const ry = y;
    const rz = rotY(x, z, camera_angle_t * Math.PI * 2);
    return [rx, ry, rz];
}

function rotV(v, k, a) {
    const c = Math.cos(a);
    const s = Math.sin(a);
    return add3( add3( mulf(v, c), mulf(cross3(k, v), s) ), mulf(k, dot3(k, v) * (1-c)) );
}

function normalize3(a) { const len = len3(a); return scale3(a,len3<0.0001?1:1/len); }
function scale3(a,b) { return [a[0]*b,a[1]*b,a[2]*b]; }
function len3(a) { return Math.sqrt(dot3(a,a)); }
function add3(a,b) { return [a[0]+b[0],a[1]+b[1],a[2]+b[2]]; }
function sub3(a,b) { return [a[0]-b[0],a[1]-b[1],a[2]-b[2]]; }
function dot3(a,b) { return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]; }
function cross3(a,b) { return [a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]]; }
function mulf(v, f) { return [v[0]*f,v[1]*f,v[2]*f]; }

// Random with seed
var rng;
function random() { if (rng === undefined) rng = new RNG(seed); return rng.nextFloat(); }
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)}

////////////////////////////////////////////////////////////////
// Polygon Clipping utility code - Created by Reinder Nijhoff 2019
// https://turtletoy.net/turtle/a5befa1f8d
////////////////////////////////////////////////////////////////
function Polygons(){const t=[],s=class{constructor(){this.cp=[],this.dp=[],this.aabb=[]}addPoints(...t){let s=1e5,e=-1e5,h=1e5,i=-1e5;(this.cp=[...this.cp,...t]).forEach(t=>{s=Math.min(s,t[0]),e=Math.max(e,t[0]),h=Math.min(h,t[1]),i=Math.max(i,t[1])}),this.aabb=[(s+e)/2,(h+i)/2,(e-s)/2,(i-h)/2]}addSegments(...t){t.forEach(t=>this.dp.push(t))}addOutline(){for(let t=0,s=this.cp.length;t<s;t++)this.dp.push(this.cp[t],this.cp[(t+1)%s])}draw(t){for(let s=0,e=this.dp.length;s<e;s+=2)t.jump(this.dp[s]),t.goto(this.dp[s+1])}addHatching(t,e){const h=new s;h.cp.push([-1e5,-1e5],[1e5,-1e5],[1e5,1e5],[-1e5,1e5]);const i=Math.sin(t)*e,n=Math.cos(t)*e,a=200*Math.sin(t),p=200*Math.cos(t);for(let t=.5;t<150/e;t++)h.dp.push([i*t+p,n*t-a],[i*t-p,n*t+a]),h.dp.push([-i*t+p,-n*t-a],[-i*t-p,-n*t+a]);h.boolean(this,!1),this.dp=[...this.dp,...h.dp]}inside(t){let s=0;for(let e=0,h=this.cp.length;e<h;e++)this.segment_intersect(t,[.1,-1e3],this.cp[e],this.cp[(e+1)%h])&&s++;return 1&s}boolean(t,s=!0){if(Math.abs(this.aabb[0]-t.aabb[0])-(t.aabb[2]+this.aabb[2])>=0&&Math.abs(this.aabb[1]-t.aabb[1])-(t.aabb[3]+this.aabb[3])>=0)return this.dp.length>0;const e=[];for(let h=0,i=this.dp.length;h<i;h+=2){const i=this.dp[h],n=this.dp[h+1],a=[];for(let s=0,e=t.cp.length;s<e;s++){const h=this.segment_intersect(i,n,t.cp[s],t.cp[(s+1)%e]);!1!==h&&a.push(h)}if(0===a.length)s===!t.inside(i)&&e.push(i,n);else{a.push(i,n);const h=n[0]-i[0],p=n[1]-i[1];a.sort((t,s)=>(t[0]-i[0])*h+(t[1]-i[1])*p-(s[0]-i[0])*h-(s[1]-i[1])*p);for(let h=0;h<a.length-1;h++)(a[h][0]-a[h+1][0])**2+(a[h][1]-a[h+1][1])**2>=.001&&s===!t.inside([(a[h][0]+a[h+1][0])/2,(a[h][1]+a[h+1][1])/2])&&e.push(a[h],a[h+1])}}return(this.dp=e).length>0}segment_intersect(t,s,e,h){const i=(h[1]-e[1])*(s[0]-t[0])-(h[0]-e[0])*(s[1]-t[1]);if(0===i)return!1;const n=((h[0]-e[0])*(t[1]-e[1])-(h[1]-e[1])*(t[0]-e[0]))/i,a=((s[0]-t[0])*(t[1]-e[1])-(s[1]-t[1])*(t[0]-e[0]))/i;return n>=0&&n<=1&&a>=0&&a<=1&&[t[0]+n*(s[0]-t[0]),t[1]+n*(s[1]-t[1])]}};return{list:()=>t,create:()=>new s,draw:(s,e,h=!0)=>{for(let s=0;s<t.length&&e.boolean(t[s]);s++);e.draw(s),h&&t.push(e)}}}