Anodes and cathode
Log in to post a comment.
const nElectrodes = 4; //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; //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.length; 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 electrons.length > 0 && i < 50000;
}