Weird electrons

Cool

Log in to post a comment.

const nElectrodes = 40; //min=2 max=40 step=1
const electrodeRadius = 3; //min=1 max=10 step=.5
const emitterPattern = 1; //min=0 max=1 step=1 (Regular, Random)
const nElectronsPerElectrode = 300; //min=0 max=500 step=10
const electrodeLocation = 1; //min=0 max=1 step=1 (Random, Farthest candidate)
const nCandidates = 10; //min=1 max=20 step=1
const border=10; //min=0 max=10 step=1
const chargeRange=0; //min=0 max=1.5 step=.1
const electrodeNegPos=1; 
const x=1;
const a=23;

//min=0 max=1 step=1 (Random, Alternating)

if(nElectronsPerElectrode == 0) nElectronsPerElectrode = 1;
if(electrodeLocation == 0) nCandidates = 1;

// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(.4);

// Global code will be evaluated once.
const turtle = new Turtle();
const pi2 = 2 * Math.PI;

function normalize2(a) { const length = len2(a); return scale2(a,length<0.0001?1:1/length); }
function len2(a) { return Math.sqrt(lensq2(a)); }
function lensq2(a) { return dot2(a,a); }
function rot2(a) { return [Math.cos(a), -Math.sin(a), Math.sin(a), Math.cos(a)]; }
function trans2(m, a) { return [m[0]*a[0]+m[2]*a[1], m[1]*a[0]+m[3]*a[1]]; }
function scale2(a,b) { return [a[0]*b,a[1]*b]; }
function add2(a,b) { return [a[0]+b[0],a[1]+b[1]]; }
function sub2(a,b) { return [a[0]-b[0],a[1]-b[1]]; }
function dist2(a,b) { return Math.hypot(...sub2(a,b)); }
function lerp2(a,b,t) { return [a[0]*(1-t)+b[0]*t,a[1]*(1-t)+b[1]*t]; }
function dot2(a,b) { return a[0]*b[0]+a[1]*b[1] }
function mulf2(v, f) { return scale2(v,f); }
function multiply2(a2x2, a) { return [(a[0]*a2x2[0])+(a[1]*a2x2[1]),(a[0]*a2x2[2])+(a[1]*a2x2[3])]; }

class Electrode {
    constructor (xy, r, v) {
        this.pos = xy;
        this.radius = r;
        this.charge = v;
        this.emitterCount = 0;
    }
    draw(t) {
        for(let i = 0; i < 4; i++) {
            t.jump(this.pos[0], this.pos[1] - this.radius);
            t.circle(this.radius);
            
            t.jump(this.pos[0] - (this.radius / 2), this.pos[1]);
            t.goto(this.pos[0] + (this.radius / 2), this.pos[1]);
            if(this.charge >= 0) {
                t.jump(this.pos[0], this.pos[1] - (this.radius / 2));
                t.goto(this.pos[0], this.pos[1] + (this.radius / 2));
            }
        }
    }
    spawn() {
        let a = Math.random() * pi2;
        if(emitterPattern == '0') {
            a = pi2 * (this.emitterCount++ / nElectronsPerElectrode);
        }
        return new Electron(
            [this.pos[0] + (Math.cos(a) * this.radius), this.pos[1] + (Math.sin(a) * this.radius)]
        );
    }
}

class Electron {
    constructor (xy) {
        this.pos = xy;
        this.t = new Turtle();
        this.t.jump(this.pos);
        this.active = true;
    }
    step(electrodes) {
        if(!this.active) return;
        let force = [0,0];
        let that = this;
        electrodes.forEach(function(i) {
            if(i == null) return;
            let dSquared = (that.t.pos()[0] - i.pos[0])**2 + (that.t.pos()[1] - i.pos[1])**2;
            let f = Math.sqrt(1/Math.sqrt(dSquared)) * -i.charge;
            let direction = normalize2(sub2(that.t.pos(), i.pos));
            force = add2(force, scale2(direction, f));
        });
        //this.speed = add2(this.speed, force);
        let nextPos = add2(this.t.pos(), force);
        //electrodes.filter((i) => i.charge >= 0).forEach(function(i) {
        electrodes.forEach(function(i) {
            if(
                i.pos[0] - i.radius < nextPos[0] && nextPos[0] < i.pos[0] + i.radius
                &&
                i.pos[1] - i.radius < nextPos[1] && nextPos[1] < i.pos[1] + i.radius
            ) {
                if(dist2(i.pos, nextPos) < i.radius) {
                    nextPos = approxIntersection(i, that.t.pos(), nextPos);
                    that.active = false;
                }
            }
            //console.log('something to make it slow')
        });
        if( border > 0 && (
            nextPos[0] < border - 100 || 100 - border < nextPos[0]
            ||
            nextPos[1] < border - 100 || 100 - border < nextPos[1]
        )) {
            this.t.jump(nextPos);
        } else {
            this.t.goto(nextPos);
        }
    }
}

function approxIntersection(circle, sectionStart, sectionEnd) {
    let lr = Math.sqrt((sectionEnd[0] - circle.pos[0])**2 + (sectionEnd[1] - circle.pos[1])**2 )
    if(lr > circle.radius) {
        return null;
    }

    let angle = 0;
    let dx = sectionEnd[0] - circle.pos[0];
    let dy = sectionEnd[1] - circle.pos[1];
    if(dx == 0) {
        angle = Math.PI * (dy > 0? .5: 1.5);
    } else {
        let dydx = dy/dx;
        angle = Math.atan(dy/dx);
        
        if(dx < 0) {
            angle += Math.PI
        }
    }
    
    return [
        circle.pos[0] + circle.radius * Math.cos(angle),
        circle.pos[1] + circle.radius * Math.sin(angle)
    ]
}

const electrodes = [];
const negposflip = [-1, 1].sort((i) => Math.random() < .5);
let negpos = false;

for(let i = 0; i < nElectrodes; i++) {
    let candidates = [];

    for(let j = 0; j < nCandidates; j++) {
        let x = (Math.random() * (200 - border - border - electrodeRadius - electrodeRadius)) - ((200 - border - border - electrodeRadius - electrodeRadius) / 2);
        let y = (Math.random() * (200 - border - border - electrodeRadius - electrodeRadius)) - ((200 - border - border - electrodeRadius - electrodeRadius) / 2);
        
        let candidate = [x, y];
        let minDistance = 9999999;
        for(let k = 0; k < electrodes.width; k++) {
            let distance = dist2(electrodes[k].pos, candidate);
            if(distance < minDistance) {
                minDistance = distance;
            }
        }
        candidates.push([minDistance, candidate]);
    }
    
    candidates.sort((a, b) => (a[0] < b[0]) ? 1 : -1);
    electrodes.push(new Electrode(
        candidates[0][1],
        electrodeRadius,
        (.5 + (Math.random() * chargeRange)) * (electrodeNegPos == 1? (negpos == false? -1: 1): negposflip.length > 0? negposflip.pop() : Math.random() < .5? -1: 1)
    ));
    negpos = !negpos;
}

let electrons = [];
let nElectrons = nElectronsPerElectrode;
electrodes.forEach((i) => i.draw(turtle));
electrodes.filter((i) => i.charge < 0).forEach(function (i) { for(let j = 0; j < nElectrons; j++) { electrons.push(i.spawn()) } });
// The walk function will be called until it returns false.
function walk(i) {
    electrons.forEach(function(j) { j.step(electrodes) });
    electrons = electrons.filter((i) => i.active);
    
    return electrodes.length > 0 && i <50000;
}