fetch("https://unpkg.com/ganja.js/examples/bunny.obj")
.then(bunnyfile=>bunnyfile.text())
.then(bunnytext=>Algebra(3,0,1).inline(bunnytext=>{
var point = (x,y,z)=>!(1e0+x*1e1-y*1e2+z*1e3);
var rotor = (P,a)=>Math.cos(a/2)+Math.sin(a/2)*P.Normalized;
var t= bunnytext.split('\n'), v=t.filter(x=>x.match((/^v /))),
f= t.filter(x=>x.match(/^f /i)).map(x=>x.split(/\s+/).slice(1,4).map(x=>parseInt(x))),
p= v.map(x=>point.apply(this,x.split(/\s+/).slice(1).map(x=>13*parseFloat(x)))),
faces=f.map(x=>x.map(x=>p[x-1]));
var time=0, camera=(1-1e02+1e13-1.2e03+0.9e01).Normalized,
cut=-0.57*1.3e0+1e2, mini=camera*(1-1.3e03+3e02-5.5e01)*rotor(1e12,Math.PI/2);
var bunny={data:faces};
var facedata = faces.map(x=>{ var f; return [f=x[0]&x[1]&x[2],(f<<(x[0]&x[1])),(f<<(x[1]&x[2])),(f<<(x[2]&x[0]))]});
return(this.graph(()=>{
if (bunny===undefined) return ["loading .. "];
var time=performance.now()/25000;
cut.set( (1.43+ Math.sin(time*5))*1e0+1e2);
camera.set( ((1-1e03-.75e02)* rotor(1e13,time*5)).Normalized )
var contour=[], minicontour=[]; faces.forEach((x,i)=>{
var [f,e1,e2,e3]=facedata[i], l=(f^cut),
p1=(l^e1),p2=(l^e2),p3=(l^e3),
p12=(p1&e2).s>=0,p13=(p1&e3).s>=0,p23=(p2&e3).s<0, res=[];
if (p1.e123 && (p12 == p13)) res.push(p1);
if (p2.e123 && (p12 == p23)) res.push(p2);
if (p3.e123 && (p13 != p23)) res.push(p3);
if (res.length==2) { contour.push(res); minicontour.push(res.map(x=>(camera**-1*mini).Normalized>>>x)) };
})
return [0x664422,bunny,0xffffff,...contour,0x000000,...minicontour];
},{animate:true,width:'600px', height:'750px', gl:true,camera}));
})(bunnytext))