### Fork: turtle torus

// Forked from "turtle torus" by flockaroo
// https://turtletoy.net/turtle/2dc4806767

// created by florian berger (flockaroo) - 2018

// my 1st turtle...

// turtle torus
// Sliders added by rupertxrussell

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

// Global code will be evaluated once.
const turtle = new Turtle();
const nth=16; // min=16, max=100, step=1
const nph=32; // min=32, max=100, step=1

// The walk function will be called until it returns false.
function walk(i) {
p0=getTorusPoint(i,      50,20,nph,nth);
p1=getTorusPoint(i+1,    50,20,nph,nth);
p2=getTorusPoint(i+nth,  50,20,nph,nth);
p3=getTorusPoint(i+1+nth,50,20,nph,nth);
p0=rotX(1.,p0);
p1=rotX(1.,p1);
p2=rotX(1.,p2);
p3=rotX(1.,p3);
p0=project(p0);
p1=project(p1);
p2=project(p2);
p3=project(p3);

turtle.penup();
turtle.goto(p1);
turtle.pendown();
turtle.goto(p0);
turtle.goto(p2);
// draw front faces 3 times - not very efficent (at least for the turtle)
if(vec3_cross(vec3_sub(p1,p0),vec3_sub(p2,p0))[2]>0.0)
{
turtle.goto(p3);
turtle.goto(p1);
turtle.goto(p3);
turtle.goto(p2);
turtle.goto(p0);
turtle.goto(p1);
}
return i < nph*nth;
}

function project(p)
{
p[2]+=180;
return [p[0]/p[2]*180.,p[1]/p[2]*180.,p[2]];
}

function getTorusPoint(i,R,r,nph,nth)
{
th=i/nth*Math.PI*2.0;
ph=th/nph;
return[(R+r*mcos(th))*mcos(ph),(R+r*mcos(th))*msin(ph),r*msin(th)];
}

function mcos(x) {
return Math.cos(x);
}

function msin(x) {
return Math.sin(x);
}

return [a[0]+b[0],a[1]+b[1],a[2]+b[2]];
}

function vec3_sub(a,b) {
return [a[0]-b[0],a[1]-b[1],a[2]-b[2]];
}

function vec3_cross(a,b) {
return [
a[1]*b[2]-b[1]*a[2],
a[2]*b[0]-b[2]*a[0],
a[0]*b[1]-b[0]*a[1]
];
}

function rotX(ph,v) {
return [ v[0],v[1]*mcos(ph)+v[2]*msin(ph), v[2]*mcos(ph)-v[1]*msin(ph) ];
}