truchet teapot turtle

trying some truchet pattern on teapot

Log in to post a comment.

// created by florian berger (flockaroo) - 2018
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

// utah teapot with truchet pattern

// using slight adaptations of reinder's occlusion from  "Cubic space division #2"
// to enable culling/drawing of poly and to not-close poly

Canvas.setpenopacity(1.);

// Global code will be evaluated once.
const turtle = new Turtle();
const polygonList = [];
const quads = [];
const nx=12;
const ny=8;


function walk(i) {
    return walkCull(i);
}

function walkCull(i) {
    var PatchNum = TeapotPatchNum;
    var num = nx*ny*PatchNum;
    if(i==0){
        for(let j=0;j<num;j++) {
            var face = teapotFace(nx,ny,j);
            var p0=face[0];
            var p1=face[1];
            var p2=face[2];
            var p3=face[3];
            var c=scale3(add3(add3(add3(p0,p1),p2),p3),1.0/4.0);
            p0=transformAll(p0);
            p1=transformAll(p1);
            p2=transformAll(p2);
            p3=transformAll(p3);
    
            //if(cross(sub3(p1,p0),sub3(p2,p0))[2]<0.0)
            {
                insertQuad(p0,p2,p3,p1);
            }
        }
    }
    
    var p0=[quads[i*9+0],quads[i*9+1]];
    var p1=[quads[i*9+2],quads[i*9+3]];
    var p2=[quads[i*9+4],quads[i*9+5]];
    var p3=[quads[i*9+6],quads[i*9+7]];
    const p = new Polygon();
    p.cp.push([p0[0], p0[1]]);
    p.cp.push([p1[0], p1[1]]);
    p.cp.push([p2[0], p2[1]]);
    p.cp.push([p3[0], p3[1]]);
    p.addOutline(0);
    var r=Math.random()*4.;
    if(drawPolygon(turtle, p, false, false))
    {
        drawTruch([p0,p1,p2,p3],r);
        drawPolygon(turtle, p, false, true);
    }
    return i <= num;
}

// The walk function will be called until it returns false.
function walkNoCull(i) {
    var PatchNum = TeapotPatchNum;
    var face = teapotFace(nx,ny,i);
    var p0=face[0];
    var p1=face[1];
    var p2=face[2];
    var p3=face[3];
    var c=scale3(add3(add3(add3(p0,p1),p2),p3),1.0/4.0);
    p0=add3(c,scale3(sub3(p0,c),1.));
    p1=add3(c,scale3(sub3(p1,c),1.));
    p2=add3(c,scale3(sub3(p2,c),1.));
    p3=add3(c,scale3(sub3(p3,c),1.));
    p0=transformAll(p0);
    p1=transformAll(p1);
    p2=transformAll(p2);
    p3=transformAll(p3);
    
    var r=Math.random()*4.;
    // draw front faces 3 times - not very efficent (at least for the turtle)
    if(cross(sub3(p1,p0),sub3(p2,p0))[2]<0.0)
    {
        drawTruch([p0,p1,p3,p2],r);
    }
    return i < nx*ny*PatchNum;
}

function drawTruch(pi,r)
{
    var i0=Math.floor(0.0+r)%4;
    var i1=Math.floor(1.0+r)%4;
    var i2=Math.floor(3.0+r)%4;
    var i3=Math.floor(2.0+r)%4;
    for(var j=0;j<2;j++)
    {
        var  d=.3+.4*j;
    var c01=mix3(pi[i0],pi[i1],d);
    var c23=mix3(pi[i2],pi[i3],d);
    var c02=mix3(pi[i0],pi[i2],d);
    var c13=mix3(pi[i1],pi[i3],d);
    var t1=scale3(sub3(c23,c01),.5*(d));
    var t2=scale3(sub3(c13,c02),.5*(d));
    var t3=scale3(sub3(c23,c01),.5*(1.-d));
    var t4=scale3(sub3(c13,c02),.5*(1.-d));
    p=[ c01, add3(c01,t1), add3(c02,t2), c02 ];
    var po = new Polygon();
    po.cp.push([p[0][0], p[0][1]]);
    for(var i=1;i<=6;i++) { var pa=bezierCurvePos(p, i/6.0); po.cp.push([pa[0], pa[1]]) } 
    po.addOutline(0,1);
    drawPolygon(turtle, po, true, false);
    po = new Polygon();
    p=[ c13, sub3(c13,t4), sub3(c23,t3), c23 ];
    po.cp.push([p[0][0], p[0][1]]);
    for(var i=1;i<=6;i++) { var pa=bezierCurvePos(p, i/6.0); po.cp.push([pa[0], pa[1]]) } 
    po.addOutline(0,1);
    drawPolygon(turtle, po, true, false);
    }
}

function transformAll(p)
{
    p=add3(p,vec3(0,0,-1.2));
    p=scale3(p,22.0);
    p=rotZ(2.7,p);
    p=rotX(-2.2,p);
    p=project(p);
    return p;
}

function insertQuad(p0,p1,p2,p3)
{
    var z = p0[2]+p1[2]+p2[2]+p3[2];
    var idx=0;
    for(idx=0;idx<quads.length && quads[idx+8]<z;idx+=9);
    
    // hmm, why is the one below not working... !?
    //for(var i=0;i<quads.length;i+=9) {
    //    if(quads[i+8]>z) { idx=i; break; }
    //}
    quads.splice(idx, 0, p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], p3[0], p3[1], z);
}

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

// cubic bezier curve in 2 dimensions
// equivalent to the function above, just in a more intuitive form
function bezierCurvePos(p, t)
{
    // combination of 2 quadric beziers
    var q=[]; 
    var r=[]; 
    for(var i=0;i<3;i++) q.push(mix3(p[i],p[i+1],t));
    for(var i=0;i<2;i++) r.push(mix3(q[i],q[i+1],t));
    return mix3(r[0],r[1],t);
} 
 
// cubic bezier patch in 3 dimensions
function bezierPatchPos(p, uv)
{ 
    var curve = [];
    // cubic interpolation of control points in 1st parameter
    for (var i = 0; i < 4; i++) 
    {
        curve.push( bezierCurvePos([p[i*4],p[i*4+1],p[i*4+2],p[i*4+3]], uv[0]) ); 
    }
    // actual cubic bezier in 2nd parameter
    return bezierCurvePos(curve, uv[1]); 
}

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 teapotFace(pnum_x, pnum_y, idx)
{
    // only write data if index is smaller than total vertex count of teapot
    if(idx>TeapotPatchNum*pnum_x*pnum_y) return [[0,0,0],[0,0,0],[0,0,0],[0,0,0]];
    
    var patchIdx = Math.floor(idx/(pnum_x*pnum_y));    // the actual patch of this vertex
    var vIdx     = idx%(pnum_x*pnum_y);    // the vertex id within this patch
    
    // get the control points
    var pIndices = getPatchIndices(patchIdx);
    if (!pIndices) return [[0,0,0],[0,0,0],[0,0,0],[0,0,0]];
    var p = [];
    for(var i=0; i<16; i++) p.push(getTeapotPoint(pIndices[i]-1));
    
    // construct the quad
    // ...first the 2D bezier parameters of each edge
    //   (we give those back as uv-coords)
    var qIdx=vIdx;
    var qp=vec2(qIdx%pnum_x,Math.floor(qIdx/pnum_x));
    var uv1 = div2(add2(qp,vec2(0,0)),vec2(pnum_x,pnum_y));
    var uv2 = div2(add2(qp,vec2(1,0)),vec2(pnum_x,pnum_y));
    var uv3 = div2(add2(qp,vec2(0,1)),vec2(pnum_x,pnum_y));
    var uv4 = div2(add2(qp,vec2(1,1)),vec2(pnum_x,pnum_y));
    
    // ...then get each edge point
    var p1=bezierPatchPos(p,uv1);
    var p2=bezierPatchPos(p,uv2);
    var p3=bezierPatchPos(p,uv3);
    var p4=bezierPatchPos(p,uv4);
    
    // use eps,eps and -eps,eps instead of eps,0 and 0,eps to avoid 0-normals
    /*var eps=.001;
    var n1=cross(sub3(p1,bezierPatchPos(p,add2(uv1,vec2(eps,eps)))),sub3(p1,bezierPatchPos(p,add2(uv1,vec2(-eps,eps)))));
    var n2=cross(sub3(p2,bezierPatchPos(p,add2(uv2,vec2(eps,eps)))),sub3(p2,bezierPatchPos(p,add2(uv2,vec2(-eps,eps)))));
    var n3=cross(sub3(p3,bezierPatchPos(p,add2(uv3,vec2(eps,eps)))),sub3(p3,bezierPatchPos(p,add2(uv3,vec2(-eps,eps)))));
    var n4=cross(sub3(p4,bezierPatchPos(p,add2(uv4,vec2(eps,eps)))),sub3(p4,bezierPatchPos(p,add2(uv4,vec2(-eps,eps)))));
    n1 = normalize3(n1);
    n2 = normalize3(n2);
    n3 = normalize3(n3);
    n4 = normalize3(n4);*/

    /*var nph= pnum_x;
    var nth= pnum_y;
    return [
    getTorusPoint(idx,      50,20,nph,nth),
    getTorusPoint(idx+1,    50,20,nph,nth),
    getTorusPoint(idx+nth,  50,20,nph,nth),
    getTorusPoint(idx+1+nth,50,20,nph,nth)
    ];*/
    return [p1,p2,p3,p4/*, n1,n2,n3,n4, uv1,uv2,uv3,uv4*/];
} 

function mymix(a,b,f) { return a*(1.0-f)+b*f; }
function mcos(x) { return Math.cos(x); }
function msin(x) { return Math.sin(x); }


function vec3(x,y,z) { return [x,y,z]; }
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 div3(a,b) { return [a[0]/b[0],a[1]/b[1],a[2]/b[2]]; }
function mul3(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 scale3(a,b) { return [a[0]*b,a[1]*b,a[2]*b]; }
function length3(a) { return Math.sqrt(dot3(a,a)); }
function normalize3(a) { return scale3(a,1.0/length3(a)); }
function mix3(a,b,f) { return add3(scale3(a,(1.0-f)),scale3(b,f)); }

function vec2(x,y) { return [x,y]; }
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 div2(a,b) { return [a[0]/b[0],a[1]/b[1]]; }
function mul2(a,b) { return [a[0]*b[0],a[1]*b[1]]; }
function dot2(a,b) { return a[0]*b[0]+a[1]*b[1]; }
function scale2(a,b) { return [a[0]*b,a[1]*b]; }
function length2(a) { return Math.sqrt(dot2(a,a)); }
function normalize2(a) { return scale2(a,1.0/length2(a)); }
function mix2(a,b,f) { return add2(scale2(a,(1.0-f)),scale2(b,f)); }

function 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) ];
}

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

// teapot data....

const TeapotPatchNum=32

function getPatchIndices(pIdx)
{
    return teapot_indices[pIdx];
}
 
function getTeapotPoint(idx)
{
    return teapot_points[idx];
}

const teapot_indices = [
    // the 32 teapot patches
    [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
    [4,17,18,19,8,20,21,22,12,23,24,25,16,26,27,28],
    [19,29,30,31,22,32,33,34,25,35,36,37,28,38,39,40],
    [31,41,42,1,34,43,44,5,37,45,46,9,40,47,48,13],
    [13,14,15,16,49,50,51,52,53,54,55,56,57,58,59,60],
    [16,26,27,28,52,61,62,63,56,64,65,66,60,67,68,69],
    [28,38,39,40,63,70,71,72,66,73,74,75,69,76,77,78],
    [40,47,48,13,72,79,80,49,75,81,82,53,78,83,84,57],
    [57,58,59,60,85,86,87,88,89,90,91,92,93,94,95,96],
    [60,67,68,69,88,97,98,99,92,100,101,102,96,103,104,105],
    [69,76,77,78,99,106,107,108,102,109,110,111,105,112,113,114],
    [78,83,84,57,108,115,116,85,111,117,118,89,114,119,120,93],
    [121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136],
    [124,137,138,121,128,139,140,125,132,141,142,129,136,143,144,133],
    [133,134,135,136,145,146,147,148,149,150,151,152,69,153,154,155],
    [136,143,144,133,148,156,157,145,152,158,159,149,155,160,161,69],
    [162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177],
    [165,178,179,162,169,180,181,166,173,182,183,170,177,184,185,174],
    [174,175,176,177,186,187,188,189,190,191,192,193,194,195,196,197],
    [177,184,185,174,189,198,199,186,193,200,201,190,197,202,203,194],
    [204,204,204,204,207,208,209,210,211,211,211,211,212,213,214,215],
    [204,204,204,204,210,217,218,219,211,211,211,211,215,220,221,222],
    [204,204,204,204,219,224,225,226,211,211,211,211,222,227,228,229],
    [204,204,204,204,226,230,231,207,211,211,211,211,229,232,233,212],
    [212,213,214,215,234,235,236,237,238,239,240,241,242,243,244,245],
    [215,220,221,222,237,246,247,248,241,249,250,251,245,252,253,254],
    [222,227,228,229,248,255,256,257,251,258,259,260,254,261,262,263],
    [229,232,233,212,257,264,265,234,260,266,267,238,263,268,269,242],
    [270,270,270,270,279,280,281,282,275,276,277,278,271,272,273,274],
    [270,270,270,270,282,289,290,291,278,286,287,288,274,283,284,285],
    [270,270,270,270,291,298,299,300,288,295,296,297,285,292,293,294],
    [270,270,270,270,300,305,306,279,297,303,304,275,294,301,302,271]
];

teapot_points = [
    // the 306 teapot points
    vec3(1.4,0.0,2.4             ),
    vec3(1.4,-0.784,2.4          ),
    vec3(0.784,-1.4,2.4          ),
    vec3(0.0,-1.4,2.4            ),
    vec3(1.3375,0.0,2.53125      ),
    vec3(1.3375,-0.749,2.53125   ),
    vec3(0.749,-1.3375,2.53125   ),
    vec3(0.0,-1.3375,2.53125     ),
    vec3(1.4375,0.0,2.53125      ),
    vec3(1.4375,-0.805,2.53125   ),
    vec3(0.805,-1.4375,2.53125   ),
    vec3(0.0,-1.4375,2.53125     ),
    vec3(1.5,0.0,2.4             ),
    vec3(1.5,-0.84,2.4           ),
    vec3(0.84,-1.5,2.4           ),
    vec3(0.0,-1.5,2.4            ),
    vec3(-0.784,-1.4,2.4         ),
    vec3(-1.4,-0.784,2.4         ),
    vec3(-1.4,0.0,2.4            ),
    vec3(-0.749,-1.3375,2.53125  ),
    vec3(-1.3375,-0.749,2.53125  ),
    vec3(-1.3375,0.0,2.53125     ),
    vec3(-0.805,-1.4375,2.53125  ),
    vec3(-1.4375,-0.805,2.53125  ),
    vec3(-1.4375,0.0,2.53125     ),
    vec3(-0.84,-1.5,2.4          ),
    vec3(-1.5,-0.84,2.4          ),
    vec3(-1.5,0.0,2.4            ),
    vec3(-1.4,0.784,2.4          ),
    vec3(-0.784,1.4,2.4          ),
    vec3(0.0,1.4,2.4             ),
    vec3(-1.3375,0.749,2.53125   ),
    vec3(-0.749,1.3375,2.53125   ),
    vec3(0.0,1.3375,2.53125      ),
    vec3(-1.4375,0.805,2.53125   ),
    vec3(-0.805,1.4375,2.53125   ),
    vec3(0.0,1.4375,2.53125      ),
    vec3(-1.5,0.84,2.4           ),
    vec3(-0.84,1.5,2.4           ),
    vec3(0.0,1.5,2.4             ),
    vec3(0.784,1.4,2.4           ),
    vec3(1.4,0.784,2.4           ),
    vec3(0.749,1.3375,2.53125    ),
    vec3(1.3375,0.749,2.53125    ),
    vec3(0.805,1.4375,2.53125    ),
    vec3(1.4375,0.805,2.53125    ),
    vec3(0.84,1.5,2.4            ),
    vec3(1.5,0.84,2.4            ),
    vec3(1.75,0.0,1.875          ),
    vec3(1.75,-0.98,1.875        ),
    vec3(0.98,-1.75,1.875        ),
    vec3(0.0,-1.75,1.875         ),
    vec3(2.0,0.0,1.35            ),
    vec3(2.0,-1.12,1.35          ),
    vec3(1.12,-2.0,1.35          ),
    vec3(0.0,-2.0,1.35           ),
    vec3(2.0,0.0,0.9             ),
    vec3(2.0,-1.12,0.9           ),
    vec3(1.12,-2.0,0.9           ),
    vec3(0.0,-2.0,0.9            ),
    vec3(-0.98,-1.75,1.875       ),
    vec3(-1.75,-0.98,1.875       ),
    vec3(-1.75,0.0,1.875         ),
    vec3(-1.12,-2.0,1.35         ),
    vec3(-2.0,-1.12,1.35         ),
    vec3(-2.0,0.0,1.35           ),
    vec3(-1.12,-2.0,0.9          ),
    vec3(-2.0,-1.12,0.9          ),
    vec3(-2.0,0.0,0.9            ),
    vec3(-1.75,0.98,1.875        ),
    vec3(-0.98,1.75,1.875        ),
    vec3(0.0,1.75,1.875          ),
    vec3(-2.0,1.12,1.35          ),
    vec3(-1.12,2.0,1.35          ),
    vec3(0.0,2.0,1.35            ),
    vec3(-2.0,1.12,0.9           ),
    vec3(-1.12,2.0,0.9           ),
    vec3(0.0,2.0,0.9             ),
    vec3(0.98,1.75,1.875         ),
    vec3(1.75,0.98,1.875         ),
    vec3(1.12,2.0,1.35           ),
    vec3(2.0,1.12,1.35           ),
    vec3(1.12,2.0,0.9            ),
    vec3(2.0,1.12,0.9            ),
    vec3(2.0,0.0,0.45            ),
    vec3(2.0,-1.12,0.45          ),
    vec3(1.12,-2.0,0.45          ),
    vec3(0.0,-2.0,0.45           ),
    vec3(1.5,0.0,0.225           ),
    vec3(1.5,-0.84,0.225         ),
    vec3(0.84,-1.5,0.225         ),
    vec3(0.0,-1.5,0.225          ),
    vec3(1.5,0.0,0.15            ),
    vec3(1.5,-0.84,0.15          ),
    vec3(0.84,-1.5,0.15          ),
    vec3(0.0,-1.5,0.15           ),
    vec3(-1.12,-2.0,0.45         ),
    vec3(-2.0,-1.12,0.45         ),
    vec3(-2.0,0.0,0.45           ),
    vec3(-0.84,-1.5,0.225        ),
    vec3(-1.5,-0.84,0.225        ),
    vec3(-1.5,0.0,0.225          ),
    vec3(-0.84,-1.5,0.15         ),
    vec3(-1.5,-0.84,0.15         ),
    vec3(-1.5,0.0,0.15           ),
    vec3(-2.0,1.12,0.45          ),
    vec3(-1.12,2.0,0.45          ),
    vec3(0.0,2.0,0.45            ),
    vec3(-1.5,0.84,0.225         ),
    vec3(-0.84,1.5,0.225         ),
    vec3(0.0,1.5,0.225           ),
    vec3(-1.5,0.84,0.15          ),
    vec3(-0.84,1.5,0.15          ),
    vec3(0.0,1.5,0.15            ),
    vec3(1.12,2.0,0.45           ),
    vec3(2.0,1.12,0.45           ),
    vec3(0.84,1.5,0.225          ),
    vec3(1.5,0.84,0.225          ),
    vec3(0.84,1.5,0.15           ),
    vec3(1.5,0.84,0.15           ),
    vec3(-1.6,0.0,2.025          ),
    vec3(-1.6,-0.3,2.025         ),
    vec3(-1.5,-0.3,2.25          ),
    vec3(-1.5,0.0,2.25           ),
    vec3(-2.3,0.0,2.025          ),
    vec3(-2.3,-0.3,2.025         ),
    vec3(-2.5,-0.3,2.25          ),
    vec3(-2.5,0.0,2.25           ),
    vec3(-2.7,0.0,2.025          ),
    vec3(-2.7,-0.3,2.025         ),
    vec3(-3.0,-0.3,2.25          ),
    vec3(-3.0,0.0,2.25           ),
    vec3(-2.7,0.0,1.8            ),
    vec3(-2.7,-0.3,1.8           ),
    vec3(-3.0,-0.3,1.8           ),
    vec3(-3.0,0.0,1.8            ),
    vec3(-1.5,0.3,2.25           ),
    vec3(-1.6,0.3,2.025          ),
    vec3(-2.5,0.3,2.25           ),
    vec3(-2.3,0.3,2.025          ),
    vec3(-3.0,0.3,2.25           ),
    vec3(-2.7,0.3,2.025          ),
    vec3(-3.0,0.3,1.8            ),
    vec3(-2.7,0.3,1.8            ),
    vec3(-2.7,0.0,1.575          ),
    vec3(-2.7,-0.3,1.575         ),
    vec3(-3.0,-0.3,1.35          ),
    vec3(-3.0,0.0,1.35           ),
    vec3(-2.5,0.0,1.125          ),
    vec3(-2.5,-0.3,1.125         ),
    vec3(-2.65,-0.3,0.9375       ),
    vec3(-2.65,0.0,0.9375        ),
    vec3(-2.0,-0.3,0.9           ),
    vec3(-1.9,-0.3,0.6           ),
    vec3(-1.9,0.0,0.6            ),
    vec3(-3.0,0.3,1.35           ),
    vec3(-2.7,0.3,1.575          ),
    vec3(-2.65,0.3,0.9375        ),
    vec3(-2.5,0.3,1.125          ),
    vec3(-1.9,0.3,0.6            ),
    vec3(-2.0,0.3,0.9            ),
    vec3(1.7,0.0,1.425           ),
    vec3(1.7,-0.66,1.425         ),
    vec3(1.7,-0.66,0.6           ),
    vec3(1.7,0.0,0.6             ),
    vec3(2.6,0.0,1.425           ),
    vec3(2.6,-0.66,1.425         ),
    vec3(3.1,-0.66,0.825         ),
    vec3(3.1,0.0,0.825           ),
    vec3(2.3,0.0,2.1             ),
    vec3(2.3,-0.25,2.1           ),
    vec3(2.4,-0.25,2.025         ),
    vec3(2.4,0.0,2.025           ),
    vec3(2.7,0.0,2.4             ),
    vec3(2.7,-0.25,2.4           ),
    vec3(3.3,-0.25,2.4           ),
    vec3(3.3,0.0,2.4             ),
    vec3(1.7,0.66,0.6            ),
    vec3(1.7,0.66,1.425          ),
    vec3(3.1,0.66,0.825          ),
    vec3(2.6,0.66,1.425          ),
    vec3(2.4,0.25,2.025          ),
    vec3(2.3,0.25,2.1            ),
    vec3(3.3,0.25,2.4            ),
    vec3(2.7,0.25,2.4            ),
    vec3(2.8,0.0,2.475           ),
    vec3(2.8,-0.25,2.475         ),
    vec3(3.525,-0.25,2.49375     ),
    vec3(3.525,0.0,2.49375       ),
    vec3(2.9,0.0,2.475           ),
    vec3(2.9,-0.15,2.475         ),
    vec3(3.45,-0.15,2.5125       ),
    vec3(3.45,0.0,2.5125         ),
    vec3(2.8,0.0,2.4             ),
    vec3(2.8,-0.15,2.4           ),
    vec3(3.2,-0.15,2.4           ),
    vec3(3.2,0.0,2.4             ),
    vec3(3.525,0.25,2.49375      ),
    vec3(2.8,0.25,2.475          ),
    vec3(3.45,0.15,2.5125        ),
    vec3(2.9,0.15,2.475          ),
    vec3(3.2,0.15,2.4            ),
    vec3(2.8,0.15,2.4            ),
    vec3(0.0,0.0,3.15            ),
    vec3(0.0,-0.002,3.15         ),
    vec3(0.002,0.0,3.15          ),
    vec3(0.8,0.0,3.15            ),
    vec3(0.8,-0.45,3.15          ),
    vec3(0.45,-0.8,3.15          ),
    vec3(0.0,-0.8,3.15           ),
    vec3(0.0,0.0,2.85            ),
    vec3(0.2,0.0,2.7             ),
    vec3(0.2,-0.112,2.7          ),
    vec3(0.112,-0.2,2.7          ),
    vec3(0.0,-0.2,2.7            ),
    vec3(-0.002,0.0,3.15         ),
    vec3(-0.45,-0.8,3.15         ),
    vec3(-0.8,-0.45,3.15         ),
    vec3(-0.8,0.0,3.15           ),
    vec3(-0.112,-0.2,2.7         ),
    vec3(-0.2,-0.112,2.7         ),
    vec3(-0.2,0.0,2.7            ),
    vec3(0.0,0.002,3.15          ),
    vec3(-0.8,0.45,3.15          ),
    vec3(-0.45,0.8,3.15          ),
    vec3(0.0,0.8,3.15            ),
    vec3(-0.2,0.112,2.7          ),
    vec3(-0.112,0.2,2.7          ),
    vec3(0.0,0.2,2.7             ),
    vec3(0.45,0.8,3.15           ),
    vec3(0.8,0.45,3.15           ),
    vec3(0.112,0.2,2.7           ),
    vec3(0.2,0.112,2.7           ),
    vec3(0.4,0.0,2.55            ),
    vec3(0.4,-0.224,2.55         ),
    vec3(0.224,-0.4,2.55         ),
    vec3(0.0,-0.4,2.55           ),
    vec3(1.3,0.0,2.55            ),
    vec3(1.3,-0.728,2.55         ),
    vec3(0.728,-1.3,2.55         ),
    vec3(0.0,-1.3,2.55           ),
    vec3(1.3,0.0,2.4             ),
    vec3(1.3,-0.728,2.4          ),
    vec3(0.728,-1.3,2.4          ),
    vec3(0.0,-1.3,2.4            ),
    vec3(-0.224,-0.4,2.55        ),
    vec3(-0.4,-0.224,2.55        ),
    vec3(-0.4,0.0,2.55           ),
    vec3(-0.728,-1.3,2.55        ),
    vec3(-1.3,-0.728,2.55        ),
    vec3(-1.3,0.0,2.55           ),
    vec3(-0.728,-1.3,2.4         ),
    vec3(-1.3,-0.728,2.4         ),
    vec3(-1.3,0.0,2.4            ),
    vec3(-0.4,0.224,2.55         ),
    vec3(-0.224,0.4,2.55         ),
    vec3(0.0,0.4,2.55            ),
    vec3(-1.3,0.728,2.55         ),
    vec3(-0.728,1.3,2.55         ),
    vec3(0.0,1.3,2.55            ),
    vec3(-1.3,0.728,2.4          ),
    vec3(-0.728,1.3,2.4          ),
    vec3(0.0,1.3,2.4             ),
    vec3(0.224,0.4,2.55          ),
    vec3(0.4,0.224,2.55          ),
    vec3(0.728,1.3,2.55          ),
    vec3(1.3,0.728,2.55          ),
    vec3(0.728,1.3,2.4           ),
    vec3(1.3,0.728,2.4           ),
    vec3(0.0,0.0,0.0             ),
    vec3(1.5,0.0,0.15            ),
    vec3(1.5,0.84,0.15           ),
    vec3(0.84,1.5,0.15           ),
    vec3(0.0,1.5,0.15            ),
    vec3(1.5,0.0,0.075           ),
    vec3(1.5,0.84,0.075          ),
    vec3(0.84,1.5,0.075          ),
    vec3(0.0,1.5,0.075           ),
    vec3(1.425,0.0,0.0           ),
    vec3(1.425,0.798,0.0         ),
    vec3(0.798,1.425,0.0         ),
    vec3(0.0,1.425,0.0           ),
    vec3(-0.84,1.5,0.15          ),
    vec3(-1.5,0.84,0.15          ),
    vec3(-1.5,0.0,0.15           ),
    vec3(-0.84,1.5,0.075         ),
    vec3(-1.5,0.84,0.075         ),
    vec3(-1.5,0.0,0.075          ),
    vec3(-0.798,1.425,0.0        ),
    vec3(-1.425,0.798,0.0        ),
    vec3(-1.425,0.0,0.0          ),
    vec3(-1.5,-0.84,0.15         ),
    vec3(-0.84,-1.5,0.15         ),
    vec3(0.0,-1.5,0.15           ),
    vec3(-1.5,-0.84,0.075        ),
    vec3(-0.84,-1.5,0.075        ),
    vec3(0.0,-1.5,0.075          ),
    vec3(-1.425,-0.798,0.0       ),
    vec3(-0.798,-1.425,0.0       ),
    vec3(0.0,-1.425,0.0          ),
    vec3(0.84,-1.5,0.15          ),
    vec3(1.5,-0.84,0.15          ),
    vec3(0.84,-1.5,0.075         ),
    vec3(1.5,-0.84,0.075         ),
    vec3(0.798,-1.425,0.0        ),
    vec3(1.425,-0.798,0.0        )
    ];
    
////////////////////////////
// reinder's occlusion code parts from "Cubic space division #2"
////////////////////////////

function drawPolygon(turtle, p, doDraw, doCull) {
    let vis = true;
    for (let j=0; j<polygonList.length; j++) {
        if(!p.boolean(polygonList[j])) {
            vis = false;
            break;
        }
    }
    if (vis) {
        if(doDraw) p.draw(turtle, 0);
        if(doCull) polygonList.push(p);
        return true;
    }
    return false;
}

// polygon functions
function LineSegment(p1, p2) {
    this.p1 = p1;
    this.p2 = p2;
}
function Polygon() {
    this.cp = []; // clip path: array of [x,y] pairs
    this.dp = []; // 2d line to draw: array of linesegments
}
Polygon.prototype.addOutline = function(s=0,e=0) {
    for (let i=s, l=this.cp.length; i<l-e; i++) {
        this.dp.push(new LineSegment(this.cp[i], this.cp[(i+1)%l]));
    }
}
Polygon.prototype.createPoly = function(x,y,c,r,a) {
    this.cp = [];
    for (let i=0; i<c; i++) {
        this.cp.push( [x + Math.sin(i*Math.PI*2/c+a) * r, y + Math.cos(i*Math.PI*2/c+a) * r] );
    }
}
Polygon.prototype.draw = function(t, inp=0) {
    if (this.dp.length ==0) {
        return;
    }
    for (let i=0, l=this.dp.length; i<l; i++) {
        const d = this.dp[i];
        if (!vec2_equal(d.p1, t.pos())) {
            t.penup();
            t.goto([d.p1[0]+inp*(Math.random()-.5), d.p1[1]+inp*(Math.random()-.5)]);
            t.pendown();   
        }
        t.goto([d.p2[0]+inp*(Math.random()-.5), d.p2[1]+inp*(Math.random()-.5)]);
    }
}
Polygon.prototype.inside = function(p) {
    // find number of i ntersection points from p to far away
    // if even your outside
    const p1 = [0.1, -1000];
    let int = 0;
    for (let i=0, l=this.cp.length; i<l; i++) {
        if (vec2_find_segment_intersect(p, p1, this.cp[i], this.cp[(i+1)%l])) {
            int ++;
        }    
    }
    return int & 1;
}
Polygon.prototype.boolean = function(p, diff = true) {
    // very naive polygon diff algorithm - made this up myself
    const ndp = [];
    for (let i=0, l=this.dp.length; i<l; i++) {
        const ls = this.dp[i];
        
        // find all intersections with clip path
        const int = [];
        for (let j=0, cl=p.cp.length; j<cl; j++) {
            const pint = vec2_find_segment_intersect(ls.p1,ls.p2,p.cp[j],p.cp[(j+1)%cl]);
            if (pint) {
                int.push(pint);
            }
        }
        if (int.length == 0) { // 0 intersections, inside or outside?
            if (diff == !p.inside(ls.p1)) {
                ndp.push(ls);
            }
        } else {
            int.push(ls.p1);
            int.push(ls.p2);
            // order intersection points on line ls.p1 to ls.p2
            const cmp = [ls.p2[0]-ls.p1[0], ls.p2[1]-ls.p1[1]];
            int.sort( (a,b) => {
                const db = vec2_dot([b[0]-ls.p1[0], b[1]-ls.p1[1]], cmp);
                const da = vec2_dot([a[0]-ls.p1[0], a[1]-ls.p1[1]], cmp);
                return da - db;
            });
            for (let j=0; j<int.length-1; j++) {
                if (!vec2_equal(int[j], int[j+1])) {
                    if (diff == !p.inside([(int[j][0]+int[j+1][0])/2,(int[j][1]+int[j+1][1])/2])) {
                        ndp.push(new LineSegment(int[j], int[j+1]));
                    }
                }
            }
        }
    }
    this.dp = ndp;
    return this.dp.length > 0;
}

// vec functions
const vec2_equal = (a,b) => vec2_dist_sqr(a,b) < 0.01;
const vec2_dot = (a, b) => a[0]*b[0]+a[1]*b[1];
const vec2_dist_sqr = (a, b) => (a[0]-b[0])*(a[0]-b[0]) + (a[1]-b[1])*(a[1]-b[1]);
//port of http://paulbourke.net/geometry/pointlineplane/Helpers.cs
function vec2_find_segment_intersect(l1p1, l1p2, l2p1, l2p2) {
    const d = (l2p2[1] - l2p1[1]) * (l1p2[0] - l1p1[0]) - (l2p2[0] - l2p1[0]) * (l1p2[1] - l1p1[1]);
    const n_a = (l2p2[0] - l2p1[0]) * (l1p1[1] - l2p1[1]) - (l2p2[1] - l2p1[1]) * (l1p1[0] - l2p1[0]);
    const n_b = (l1p2[0] - l1p1[0]) * (l1p1[1] - l2p1[1]) - (l1p2[1] - l1p1[1]) * (l1p1[0] - l2p1[0]);
    if (d == 0) {
        return false;
    }
    const ua = n_a / d;
    const ub = n_b / d;
    if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) {
        return [l1p1[0] + (ua * (l1p2[0] - l1p1[0])), l1p1[1] + (ua * (l1p2[1] - l1p1[1])) ];
    }
    return false;  
}