From 002fb4719cbe2566a4792cf35fc806324f6a9ac4 Mon Sep 17 00:00:00 2001 From: dnanto Date: Sun, 29 Sep 2024 19:38:21 -0400 Subject: [PATCH] Update democapsid.min.js --- js/democapsid.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/democapsid.min.js b/js/democapsid.min.js index abe04d7..3bed933 100644 --- a/js/democapsid.min.js +++ b/js/democapsid.min.js @@ -3,4 +3,4 @@ * MIT License * Copyright (c) 2020 - 2024, Daniel Antonio Negrón (dnanto/remaindeer) */ -const r="2.1.2",e=Math.sqrt(3),o=Math.sqrt(5),a=(1+o)/2,n=100,s=1e-15,i=1e-5;function mmul(t,r){const[e,o,a]=[t.length,t[0].length,r[0].length];for(var n=new Array(e),s=0;sArray.from({length:t.length},(()=>[])))),e=0;e[new paper.Path.RegularPolygon({center:t.coor,sides:6,radius:r,data:{mer:1}})],radius:r};else if("trihex"===t)a={basis:[[2*r,0],[r,r*e]],tile:t=>[new paper.Path.RegularPolygon({center:t.coor,sides:6,radius:r,data:{mer:1}}).rotate(30),...Array.from({length:2},((e,a)=>new paper.Path.RegularPolygon({center:t.coor.add([r,-1/3*o]),sides:3,radius:2/3*o,data:{mer:2}}).rotate(180).rotate(60*a,t.coor)))],radius:2*o};else if("snubhex"===t)a={basis:[[2.5*r,o],[.5*r,3*o]],tile:t=>[new paper.Path.RegularPolygon({center:t.coor,sides:6,radius:r,data:{mer:1}}).rotate(30),...Array.from({length:6},((r,e)=>new paper.Path.RegularPolygon({center:t.coor.add([0,-o-1/3*o]),sides:3,radius:2/3*o,data:{mer:2}}).rotate(60*e,t.coor))),new paper.Path.RegularPolygon({center:t.coor.add([1.5*r,-1/3*o]),sides:3,radius:2/3*o,data:{mer:2}}),new paper.Path.RegularPolygon({center:t.coor.add([-1.5*r,2/3*o]),sides:3,radius:2/3*o,data:{mer:2}}).rotate(180)],radius:2*o};else if("rhombitrihex"===t)a={basis:[[r+o+.5*r,.5*r+o],[0,2*o+r]],tile:t=>[new paper.Path.RegularPolygon({center:t.coor,sides:6,radius:r,data:{mer:1}}).rotate(30),...Array.from({length:3},((o,a)=>new paper.Path.RegularPolygon({center:t.coor.add([0,r+r*(e/3)]),sides:3,radius:r/e,data:{mer:2}}).rotate(-60*a-30,t.coor))),...Array.from({length:3},((e,a)=>new paper.Path.RegularPolygon({center:t.coor.add([0,-o-.5*r]),sides:4,radius:Math.sqrt(2*r*r)/2,data:{mer:3}}).rotate(60*a,t.coor)))],radius:Math.sqrt(Math.pow(o+r,2)+Math.pow(r/2,2))};else if("dualhex"===t)a={basis:[[1.5*r,o],[0,2*o]],tile:t=>Array.from({length:6},((a,n)=>new paper.Path.RegularPolygon({center:t.coor.add([0,o-r*e/6]),sides:3,radius:r/e,data:{mer:1}}).rotate(60*n,t.coor))),radius:r};else if("dualtrihex"===t)a={basis:[[2*o,0],[o,e*o]],tile:t=>[...Array.from({length:6},((e,a)=>new paper.Path({segments:[[0,0],[.5*o,-.25*r*Math.sin(Math.PI/6)/Math.cos(Math.PI/3)],[o,0],[.5*o,.25*r*Math.sin(Math.PI/6)/Math.cos(Math.PI/3)]].map((r=>t.coor.add(r))),closed:!0,data:{mer:1}}).rotate(60*a,t.coor))),...Array.from({length:6},((e,a)=>new paper.Path({segments:[[-.5*o,.5*r+.25*r*Math.sin(Math.PI/6)/Math.cos(Math.PI/3)],[0,.5*r],[o-.5*o,.5*r+.25*r*Math.sin(Math.PI/6)/Math.cos(Math.PI/3)],[0,.5*r+.25*r*Math.sin(Math.PI/6)*2/Math.cos(Math.PI/3)]].map((r=>t.coor.add(r))),closed:!0,data:{mer:2}}).rotate(60*a,t.coor)))],radius:r};else if("dualsnubhex"===t)a={basis:[[2.5*r,o],[.5*r,2*o+r*e/3*2-r*e/6]],tile:t=>Array.from({length:6},((a,n)=>new paper.Path({segments:[[0,0],[0,o+r*e/6],[.5*r,o+r*e/3],[r,o+r*e/6],[r,r*e/3]].map((r=>t.coor.add(r))),closed:!0,data:{mer:1}}).rotate(60*n,t.coor))),radius:o+r*e/3};else{if("dualrhombitrihex"!==t)throw new Error("incorrect tile mode");a={basis:[[1.5*r,o],[0,2*o]],tile:t=>Array.from({length:6},((a,n)=>new paper.Path({segments:[[0,0],[0,o],[.5*r,o],[e/2*o,.5*o]].map((r=>t.coor.add(r))),closed:!0,data:{mer:1}}).rotate(60*n,t.coor))),radius:r}}return a}function*tile_grid(t,r){const e=t.map((t=>mmul(T(inv2(r)),[t,1].flat().T()).flat())).map((t=>t.map(Math.round))),[o,a,n,s]=[...[0,1].map(((t,r)=>Math.min(...e.map((t=>t[r]))))),...[0,1].map(((t,r)=>Math.max(...e.map((t=>t[r])))))];for(let i=o;ir.sub(t)));return t.add(a.cross(n).mul(s.norm()**2).add(s.cross(a).mul(n.norm()**2)).add(n.cross(s).mul(a.norm()**2)).div(2*det3([a,n,s])))}function body_radius(t){return t[6].sub([0,0,t[6][2]]).norm()}function height(t){return t[0][2]-t[19][2]}function body_height(t){return t[4][2]-t[6][2]}function sd_sphere(t,r){return t.norm()-r}function spherize(t,r,e){return t.uvec().mul(Math.abs(sd_sphere(t,r))*e).add(t)}function cylinderize(t,r,e,o){const[a,n]=[body_radius(r),body_height(r)/2];let s,i;if(5===e)s=[0,0,n-a/2],i=r[0][2]+a/2-n;else if(3===e){const[t,e]=[r[0],r[3]];s=triangle_circumcircle_center(t,e,[e[0],-e[1],e[2]]),i=t.sub(s).norm()}else 2===e&&(p1=r[0],s=tetrahedron_circumsphere_center(p1,...[1,4,5].map((t=>r[t]))),i=p1.sub(s).norm());const[c,d,l,u]=[[0,0,s[2]],[0,0,-s[2]],[0,0,n],[0,0,-n]];if(n[t,r[e]])))}function ico_axis_5(t){const[r,e]=[t[0].norm(),t[1].norm()],a=r*Math.sqrt((5+o)/10),n=[0,0,(1+o)*r/(2*Math.sqrt(5+2*o))],s=[-a,0,0].roro([0,0,1],.3*Math.PI),i=s.add([r,0,0]),c=t[0].angle(t[1]),d=i.add([e,0,0].roro([0,1,0],-Math.PI-c)),l=s.add(d.sub(s).proj(i.sub(s))),u=[l[0],-Math.abs(l[1])*Math.sqrt(a*a*l[1]*l[1]-(l[0]*l[1])**2)/(l[1]*l[1]),0];if(Number.isNaN(u[1]))throw new Error("impossible construction!");const h=u.add([0,0,-Math.sqrt(d[2]*d[2]-(l[1]-u[1])**2)]);return[n,s,i].concat([1,2,3].map((t=>i.roro([0,0,1],2*t/5*Math.PI)))).concat([h]).concat([1,2,3,4].map((t=>h.roro([0,0,1],2*t/5*Math.PI)))).concat([[0,0,h[2]-n[2]]]).map((t=>t.add([0,0,-h[2]/2])))}function ico_axis_3(r,o=n,a=s){const[i,c,d]=[r[0].norm(),r[1].norm(),r[2].sub(r[1]).norm()],l=[0,i*(1/e),0],u=[i/2,-i*(e/6),0],h=[-i/2,-i*(e/6),0],p=[0,-i*(2*e/3),0];function fold(t){let[n,s]=[p.uvec().mul(i*(e/2)),u.sub(h).uvec()];const l=[0,-i*(e/6),0].add(n.roro(s,t)),m=l.roro([0,0,1],2/3*Math.PI);t=r[0].angle(r[1]),[n,s]=[l.sub(u).uvec().mul(c),l.cross(u).uvec()];const b=n.roro(s,t),[_,g]=[u.add(b.proj(n)),u.add(b)];[n,s]=[g.sub(_),u.sub(l).uvec()];const f=t=>d-_.add(n.roro(s,t)).sub(m).norm();brackets(f,0,2*Math.PI,o).next().value;t=bisection(f,...brackets(f,0,2*Math.PI,o).next().value,a,o).slice(-1);const y=_.add(n.roro(s,t));return[l,m,y,Math.abs(l[1])-y.sub([0,0,y[2]]).norm()]}const m=Math.PI/180/10;t=0;for(let e=0;e*mfold(t).slice(-1)[0];try{t=bisection(obj,...brackets(obj,t,Math.PI/4,o).next().value,a,o).slice(-1)}catch(w){throw new Error("impossible construction!")}const[b,_,g]=fold(t).slice(0,-1);if(Number.isNaN(b[0]))throw new Error("impossible construction!");const y=[0,0,1];t=2*Math.PI/3;const M=g.roro(y,-t),v=M.sub([0,0,M[2]]).roro(y,Math.PI/3).uvec().mul(l[1]).add([0,0,M[2]+b[2]-l[2]]);let P=[l,u,h,b,_.roro(y,t),_,g,M,M.roro(y,-t),v,v.roro(y,-t),v.roro(y,-2*t)];return P.map((t=>t.add([0,0,(P[0][2]-P.slice(-1)[0][2])/2])))}function ico_axis_2(r,e=n,o=s){const[i,c,d]=[r[0].norm(),r[1].norm(),r[2].sub(r[1]).norm()],l=[i/2,0,0],u=[-i/2,0,0],h=[0,-i*a/2,-(i*a-i)/2],p=[0,i*a/2,-(i*a-i)/2];function fold(t){let a=u.add(h).div(2),[n,s]=[a.sub(l),h.sub(u).uvec()];const i=a.add(n.roro(s,t)),p=i.roro([0,0,1],Math.PI);t=r[0].angle(r[1]),[n,s]=[h.sub(l).uvec().mul(c),h.cross(l).uvec()];const m=n.roro(s,t);a=l.add(m.proj(n));const b=l.add(m);[n,s]=[b.sub(a),l.sub(h).uvec()];const f=t=>d-a.add(n.roro(s,t)).sub(p).norm();t=bisection(f,...brackets(f,0,2*Math.PI,e).next().value,o,e).slice(-1);const _=a.add(n.roro(s,t));return[i,p,_,i.sub([0,0,i[2]]).norm()-_.sub([0,0,_[2]]).norm()]}const m=Math.PI/180/10;t=0;for(let a=0;a*mfold(t).slice(-1)[0];try{t=bisection(obj,...brackets(obj,t,Math.PI/4,e).next().value,o,e).slice(-1)}catch(v){throw new Error("impossible construction!")}const[b,_,g]=fold(t).slice(0,-1);if(Number.isNaN(b[0]))throw new Error("impossible construction!");obj=t=>l.roro([0,0,1],t).add([0,0,g[2]+b[2]]).sub(_).norm()-c;try{t=bisection(obj,...brackets(obj,0,2*Math.PI,e).next().value,o,e).slice(-1)}catch(v){throw new Error("impossible construction!")}const y=l.roro([0,0,1],t).add([0,0,g[2]+b[2]]),M=y.sub([0,0,y[2]]).uvec().roro([0,0,1],Math.PI/2).mul(p[1]).add([0,0,g[2]+b[2]-p[2]]);return coor=[l,u,h,p,b,_,g,g.roro([0,0,1],Math.PI),M,M.roro([0,0,1],Math.PI),y,y.roro([0,0,1],Math.PI)],coor.map((t=>t.add([0,0,(coor[0][2]-coor.slice(-1)[0][2])/2])))}function model_sa_error(t){const r=ck_vectors(calc_tile(t.t,t.R).basis,t.h,t.k,t.H,t.K),e=[[r[3],r[0]],[r[0],r[1]],[r[1],r[2]]],o=["","",ico_axis_2,ico_axis_3,"",ico_axis_5][t.a](r,n,s),a=ico_config(t.a),i=a.t_id.map((t=>parseInt(t[1]))).reduce(((t,r)=>tc[t[1]-1]+=a.t_rep[r]));const d=e.slice(0,i).map(((t,r)=>[...t[0],0].cross([...t[1],0]).norm()/2*c[r])).sum();return(d-a.v_idx.map((t=>t.map((t=>o[t])))).map(((t,r)=>t[1].sub(t[0]).cross(t[2].sub(t[0])).norm()/2*a.t_rep[r])).sum())/d}function lattice_config(t,r,e,o,a,n){const s=calc_tile(n,a),i=ck_vectors(s.basis,t,r,e,o),c=Array.from(tile_grid(i,s.basis)),d=c.map(s.tile),l=c.filter((t=>t.is_vertex)).map((t=>t.coor)).concat([[0,0]]);return d.flat().forEach((t=>{t.data.offset=t.data.mer+(l.some((r=>[t.position.x,t.position.y].sub(r).norm()<=s.radius))?0:3),t.data.centroid=t.segments.map((t=>t.point)).reduce(((t,r)=>t.add(r))).divide(t.segments.length)})),{tile:s,ck:i,lattice:d}}function calc_facets(t){const r=[[3,0],[0,1],[1,2]].map((r=>[t.ck[r[0]],t.ck[r[1]]])).map((t=>new paper.Path({segments:[[0,0],...t],closed:!0,data:{vectors:[[0,0],...t]}}))),e=r.map((r=>new paper.Group({children:t.lattice.flatMap((t=>t.map((t=>{const e=t.intersect(r,{insert:!1});return e.data.has_centroid=r.contains(e.data.centroid),e.data.centroid_on_vertex=r.segments.map((t=>t.point.getDistance(e.data.centroid))).some((t=>t<1e-5)),e})))).filter((t=>t.segments.length>0)),data:r.data})));return r.forEach((t=>t.remove())),e}function draw_net(t){const[r,e,o,a,n,s]=["h","k","H","K","R","t"].map((r=>t[r])),i=lattice_config(r,e,o,a,n,s),c=i.ck;i.lattice.flat().forEach((r=>r.style.fillColor=t["mer_color_"+r.data.offset]+t["mer_alpha_"+r.data.offset]));const d=calc_facets(i);let l;if(5===t.a){const r=new paper.Group(d.slice(0,2)).rotate(-degrees(c[0].angle([1,0]))).scale(-1,1),e=r.clone().rotate(180),o=e.children[1].children.flatMap((t=>t.segments)).map((t=>t.point)).reduce(((t,r)=>t.y{const[a,n]=[r.clone(),e.clone()];return a.position.x+=o*r.children[0].bounds.width,n.position.x+=o*r.children[0].bounds.width,[a,n]})).flatMap((t=>t.children)),position:paper.view.center,style:{strokeColor:t.line_color+t.line_alpha,strokeWidth:t.line_size,strokeCap:"round",strokeJoin:"round"}}),[r,e].forEach((t=>t.remove()))}else if(3===t.a){const r=c[0].angle([1,0]),e=new paper.Group(d).rotate(-degrees(r)).scale(-1,1),o=d[0].clone().rotate(180);o.position.x+=c[0].norm()/2;const a=[o.bounds.topLeft,o.bounds.topRight,o.bounds.bottomCenter].reduce(((t,r)=>t.add(r))).divide(3),n=o.bounds.topRight.add(new paper.Point([1,0].mul(c[1].norm()).rot(-(Math.PI/3-c[1].angle(c[2]))))),s=o.bounds.topRight.add(new paper.Point([1,0].mul(c[2].norm()).rot(-(Math.PI/3-c[1].angle(c[2])+c[1].angle(c[2]))))),i=new paper.Group([...[1,2,3].map(((t,r)=>e.clone().rotate(120*r,a)))]);i.children.slice(0,-1).forEach((t=>t.children[1].remove()));const u=i.clone().rotate(180);u.bounds.left=Math.min(n.x,s.x),u.bounds.bottom=o.bounds.topRight.y,u.position.y-=u.children[1].children[0].bounds.bottom-n.y,u.children.forEach((t=>i.addChild(t.clone()))),i.position=paper.view.center,l=new paper.Group({children:i.children.flatMap((t=>t.children.flat())),style:{strokeColor:t.line_color+t.line_alpha,strokeWidth:t.line_size,strokeCap:"round",strokeJoin:"round"}}),[o,e,u].forEach((t=>t.remove()))}else{if(2!==t.a)throw new Error("invalid symmetry mode!");{const r=c[0].angle([1,0]),e=new paper.Group(d).rotate(-degrees(r)).rotate(-60).scale(-1,1),o=e.children[1].clone(),a=e.children[0].bounds.topLeft.subtract(e.children[0].bounds.bottomCenter);o.position=o.position.add(a);const n=new paper.Group([e.clone(),e.children[0].clone().rotate(60,e.children[0].bounds.topRight),o.clone()]),s=n.clone().rotate(180,n.children[0].children[0].bounds.topRight);s.position=s.position.add(a.rotate(240));const i=new paper.Group([n,s]),u=i.children[0].children[2].children.filter((t=>1===t.data.offset)).flatMap((t=>t.segments.map((t=>t.point)))).filter((t=>Math.abs(t.getDistance(i.children[0].children[1].bounds.bottomLeft)-c[1].norm())<1e-5)).reduce(((t,r)=>t.yt.children)).flatMap((t=>t.children));l=new paper.Group({children:[...m.filter(((t,r)=>r%3==0)).flatMap((t=>t.children)),...m.filter(((t,r)=>r%3!=0))],style:{strokeColor:t.line_color+t.line_alpha,strokeWidth:t.line_size,strokeCap:"round",strokeJoin:"round"}}),[2,5,8,11].forEach((t=>l.children[t].remove())),[e,o,n,s,i,h].forEach((t=>t.remove()))}}return d.forEach((t=>t.remove())),i.lattice.forEach((t=>t.forEach((t=>t.remove())))),l.scale(-1,1)}function draw_capsid(t){const[r,e,o,a,c,d]=["h","k","H","K","R","t"].map((r=>t[r])),l=lattice_config(r,e,o,a,c,d);l.lattice.flat().forEach((r=>r.style.fillColor=t["mer_color_"+r.data.offset]+t["mer_alpha_"+r.data.offset]));const u=calc_facets(l),h=ico_config(t.a),p=["","",ico_axis_2,ico_axis_3,"",ico_axis_5][t.a](l.ck,n,s),m=2*Math.PI/t.a,b=r==o&&e==a?r=>spherize(r,p[0].norm(),t.s):r=>cylinderize(r,p,t.a,t.s),_=camera(...[t.θ,t.ψ,t.φ].map(radians));let g=[];for(let n=0,s=0;nt.concat(1))))),e=[0,1,2].map((t=>p[h.v_idx[n][t]]));for(let o=0;ot.roro([0,0,1],o*m))),n=mmul(T(a),r),i=t.children.map((t=>{const r=t.segments.map((t=>[t.point.x,t.point.y,1])).map((t=>mmul(n,t.T()).flat())).map((t=>b(t))).map((t=>mmul(_,t.concat(1).T()).flat())),e=mmul(_,b(mmul(n,[t.data.centroid.x,t.data.centroid.y,1].T()).flat()).concat(1).T()).flat();return new paper.Path({segments:r.map((t=>t.slice(0,2))),data:Object.assign({},t.data,{id:s,centroid:e,segments_3D:r,normal:r[1].sub(r[0]).cross(r[2].sub(r[0])).uvec()}),closed:!0,style:t.style})}));g=g.concat(new paper.Group({children:i,data:{type:"facet",centroid:mmul(_,b(a.centroid()).concat(1).T()).flat()}}))}}const y=p.map((t=>mmul(_,t.concat(1).T()).flat()));let M=t.penton_fiber_toggle?h.v_con.map((t=>t.map((t=>y[t])).reduce(((t,r)=>t.add(r)),[0,0,0]).uvec())).map(((r,e)=>[y[e],y[e].add(r.mul(t.fiber_length))])):[];M=M.concat(g.flatMap((t=>t.children)).filter((r=>t["mer_toggle_"+r.data.offset]&&r.data.has_centroid)).map((r=>{const e=r.data.centroid;return[e,e.add(r.data.normal.mul(t.fiber_length))]})));let v=[];M.forEach((t=>{let r=0;for(let e of v)t[0].sub(e[0][0]).norm()[t[0][0],t.map((t=>t[1])).reduce(((t,r)=>t.add(r)),[0,0,0]).div(t.length)])).map((t=>new paper.Path.Line({from:t[0],to:t[1],data:{centroid:t[1].mul(2)}})));const P=t.knob_toggle?M.map((r=>new paper.Path.Circle({center:r.segments[1].point,radius:t.knob_size,data:{centroid:r.data.centroid}}))):[];g=g.concat(M).concat(P),g.sort(((t,r)=>t.data.centroid[2]-r.data.centroid[2]));const w=new paper.Group({children:g,position:paper.view.center,style:{strokeColor:t.line_color+t.line_alpha,strokeWidth:t.line_size,strokeCap:"round",strokeJoin:"round"}});return P.forEach((r=>r.style.fillColor=t.knob_color+t.knob_alpha)),M.forEach((r=>{r.style.strokeColor=t.fiber_color+t.fiber_alpha,r.style.strokeWidth=t.fiber_size})),u.forEach((t=>t.remove())),l.lattice.forEach((t=>t.forEach((t=>t.remove())))),w}Array.prototype.mul=function(t){return this.map((r=>r*t))},Array.prototype.div=function(t){return this.map((r=>r/t))},Array.prototype.add=function(t){return this.map(((r,e)=>r+t[e]))},Array.prototype.sub=function(t){return this.map(((r,e)=>r-t[e]))},Array.prototype.dot=function(t){return this.map(((r,e)=>r*t[e])).reduce(((t,r)=>t+r))},Array.prototype.sum=function(t=0){return this.reduce(((t,r)=>t+r),t)},Array.prototype.centroid=function(){return this.reduce(((t,r)=>t.add(r))).div(this.length)},Array.prototype.cross=function(t){return[this[1]*t[2]-this[2]*t[1],this[2]*t[0]-this[0]*t[2],this[0]*t[1]-this[1]*t[0]]},Array.prototype.rot=function(t){const[r,e]=[Math.cos(t),Math.sin(t)];return mmul([[r,-e],[e,r]],this.T()).flat()},Array.prototype.roro=function(t,r){return this.mul(Math.cos(r)).add(t.cross(this).mul(Math.sin(r))).add(t.mul(t.dot(this)).mul(1-Math.cos(r)))},Array.prototype.norm=function(){return Math.sqrt(this.map((t=>t*t)).sum())},Array.prototype.uvec=function(){return this.div(this.norm())},Array.prototype.angle=function(t){return Math.acos(this.dot(t)/(this.norm()*t.norm()))},Array.prototype.proj=function(t){return t.mul(this.dot(t)/t.dot(t))},Array.prototype.T=function(){return this.map((t=>[t]))},Array.prototype.has=function(t){return this.some((r=>r.length===t.length&&r.every(((r,e)=>r===t[e]))))},"undefined"!=typeof exports&&(module.exports={ico_axis_2:ico_axis_2,ico_axis_3:ico_axis_3,ico_axis_5:ico_axis_5}); \ No newline at end of file +const r="2.1.3",e=Math.sqrt(3),o=Math.sqrt(5),a=(1+o)/2,n=100,s=1e-15,i=1e-5;function mmul(t,r){const[e,o,a]=[t.length,t[0].length,r[0].length];for(var n=new Array(e),s=0;sArray.from({length:t.length},(()=>[])))),e=0;e[new paper.Path.RegularPolygon({center:t.coor,sides:6,radius:r,data:{mer:1}})],radius:r};else if("trihex"===t)a={basis:[[2*r,0],[r,r*e]],tile:t=>[new paper.Path.RegularPolygon({center:t.coor,sides:6,radius:r,data:{mer:1}}).rotate(30),...Array.from({length:2},((e,a)=>new paper.Path.RegularPolygon({center:t.coor.add([r,-1/3*o]),sides:3,radius:2/3*o,data:{mer:2}}).rotate(180).rotate(60*a,t.coor)))],radius:2*o};else if("snubhex"===t)a={basis:[[2.5*r,o],[.5*r,3*o]],tile:t=>[new paper.Path.RegularPolygon({center:t.coor,sides:6,radius:r,data:{mer:1}}).rotate(30),...Array.from({length:6},((r,e)=>new paper.Path.RegularPolygon({center:t.coor.add([0,-o-1/3*o]),sides:3,radius:2/3*o,data:{mer:2}}).rotate(60*e,t.coor))),new paper.Path.RegularPolygon({center:t.coor.add([1.5*r,-1/3*o]),sides:3,radius:2/3*o,data:{mer:2}}),new paper.Path.RegularPolygon({center:t.coor.add([-1.5*r,2/3*o]),sides:3,radius:2/3*o,data:{mer:2}}).rotate(180)],radius:2*o};else if("rhombitrihex"===t)a={basis:[[r+o+.5*r,.5*r+o],[0,2*o+r]],tile:t=>[new paper.Path.RegularPolygon({center:t.coor,sides:6,radius:r,data:{mer:1}}).rotate(30),...Array.from({length:3},((o,a)=>new paper.Path.RegularPolygon({center:t.coor.add([0,r+r*(e/3)]),sides:3,radius:r/e,data:{mer:2}}).rotate(-60*a-30,t.coor))),...Array.from({length:3},((e,a)=>new paper.Path.RegularPolygon({center:t.coor.add([0,-o-.5*r]),sides:4,radius:Math.sqrt(2*r*r)/2,data:{mer:3}}).rotate(60*a,t.coor)))],radius:Math.sqrt(Math.pow(o+r,2)+Math.pow(r/2,2))};else if("dualhex"===t)a={basis:[[1.5*r,o],[0,2*o]],tile:t=>Array.from({length:6},((a,n)=>new paper.Path.RegularPolygon({center:t.coor.add([0,o-r*e/6]),sides:3,radius:r/e,data:{mer:1}}).rotate(60*n,t.coor))),radius:r};else if("dualtrihex"===t)a={basis:[[2*o,0],[o,e*o]],tile:t=>[...Array.from({length:6},((e,a)=>new paper.Path({segments:[[0,0],[.5*o,-.25*r*Math.sin(Math.PI/6)/Math.cos(Math.PI/3)],[o,0],[.5*o,.25*r*Math.sin(Math.PI/6)/Math.cos(Math.PI/3)]].map((r=>t.coor.add(r))),closed:!0,data:{mer:1}}).rotate(60*a,t.coor))),...Array.from({length:6},((e,a)=>new paper.Path({segments:[[-.5*o,.5*r+.25*r*Math.sin(Math.PI/6)/Math.cos(Math.PI/3)],[0,.5*r],[o-.5*o,.5*r+.25*r*Math.sin(Math.PI/6)/Math.cos(Math.PI/3)],[0,.5*r+.25*r*Math.sin(Math.PI/6)*2/Math.cos(Math.PI/3)]].map((r=>t.coor.add(r))),closed:!0,data:{mer:2}}).rotate(60*a,t.coor)))],radius:r};else if("dualsnubhex"===t)a={basis:[[2.5*r,o],[.5*r,2*o+r*e/3*2-r*e/6]],tile:t=>Array.from({length:6},((a,n)=>new paper.Path({segments:[[0,0],[0,o+r*e/6],[.5*r,o+r*e/3],[r,o+r*e/6],[r,r*e/3]].map((r=>t.coor.add(r))),closed:!0,data:{mer:1}}).rotate(60*n,t.coor))),radius:o+r*e/3};else{if("dualrhombitrihex"!==t)throw new Error("incorrect tile mode");a={basis:[[1.5*r,o],[0,2*o]],tile:t=>Array.from({length:6},((a,n)=>new paper.Path({segments:[[0,0],[0,o],[.5*r,o],[e/2*o,.5*o]].map((r=>t.coor.add(r))),closed:!0,data:{mer:1}}).rotate(60*n,t.coor))),radius:r}}return a}function*tile_grid(t,r){const e=t.map((t=>mmul(T(inv2(r)),[t,1].flat().T()).flat())).map((t=>t.map(Math.round))),[o,a,n,s]=[...[0,1].map(((t,r)=>Math.min(...e.map((t=>t[r]))))),...[0,1].map(((t,r)=>Math.max(...e.map((t=>t[r])))))];for(let i=o;ir.sub(t)));return t.add(a.cross(n).mul(s.norm()**2).add(s.cross(a).mul(n.norm()**2)).add(n.cross(s).mul(a.norm()**2)).div(2*det3([a,n,s])))}function body_radius(t){return t[6].sub([0,0,t[6][2]]).norm()}function height(t){return t[0][2]-t[19][2]}function body_height(t){return t[4][2]-t[6][2]}function sd_sphere(t,r){return t.norm()-r}function spherize(t,r,e){return t.uvec().mul(Math.abs(sd_sphere(t,r))*e).add(t)}function cylinderize(t,r,e,o){const[a,n]=[body_radius(r),body_height(r)/2];let s,i;if(5===e)s=[0,0,n-a/2],i=r[0][2]+a/2-n;else if(3===e){const[t,e]=[r[0],r[3]];s=triangle_circumcircle_center(t,e,[e[0],-e[1],e[2]]),i=t.sub(s).norm()}else 2===e&&(p1=r[0],s=tetrahedron_circumsphere_center(p1,...[1,4,5].map((t=>r[t]))),i=p1.sub(s).norm());const[c,d,l,u]=[[0,0,s[2]],[0,0,-s[2]],[0,0,n],[0,0,-n]];if(n[t,r[e]])))}function ico_axis_5(t){const[r,e]=[t[0].norm(),t[1].norm()],a=r*Math.sqrt((5+o)/10),n=[0,0,(1+o)*r/(2*Math.sqrt(5+2*o))],s=[-a,0,0].roro([0,0,1],.3*Math.PI),i=s.add([r,0,0]),c=t[0].angle(t[1]),d=i.add([e,0,0].roro([0,1,0],-Math.PI-c)),l=s.add(d.sub(s).proj(i.sub(s))),u=[l[0],-Math.abs(l[1])*Math.sqrt(a*a*l[1]*l[1]-(l[0]*l[1])**2)/(l[1]*l[1]),0];if(Number.isNaN(u[1]))throw new Error("impossible construction!");const h=u.add([0,0,-Math.sqrt(d[2]*d[2]-(l[1]-u[1])**2)]);return[n,s,i].concat([1,2,3].map((t=>i.roro([0,0,1],2*t/5*Math.PI)))).concat([h]).concat([1,2,3,4].map((t=>h.roro([0,0,1],2*t/5*Math.PI)))).concat([[0,0,h[2]-n[2]]]).map((t=>t.add([0,0,-h[2]/2])))}function ico_axis_3(r,o=n,a=s){const[i,c,d]=[r[0].norm(),r[1].norm(),r[2].sub(r[1]).norm()],l=[0,i*(1/e),0],u=[i/2,-i*(e/6),0],h=[-i/2,-i*(e/6),0],p=[0,-i*(2*e/3),0];function fold(t){let[n,s]=[p.uvec().mul(i*(e/2)),u.sub(h).uvec()];const l=[0,-i*(e/6),0].add(n.roro(s,t)),m=l.roro([0,0,1],2/3*Math.PI);t=r[0].angle(r[1]),[n,s]=[l.sub(u).uvec().mul(c),l.cross(u).uvec()];const b=n.roro(s,t),[_,g]=[u.add(b.proj(n)),u.add(b)];[n,s]=[g.sub(_),u.sub(l).uvec()];const f=t=>d-_.add(n.roro(s,t)).sub(m).norm();brackets(f,0,2*Math.PI,o).next().value;t=bisection(f,...brackets(f,0,2*Math.PI,o).next().value,a,o).slice(-1);const y=_.add(n.roro(s,t));return[l,m,y,Math.abs(l[1])-y.sub([0,0,y[2]]).norm()]}const m=Math.PI/180/10;t=0;for(let e=0;e*mfold(t).slice(-1)[0];try{t=bisection(obj,...brackets(obj,t,Math.PI/4,o).next().value,a,o).slice(-1)}catch(w){throw new Error("impossible construction!")}const[b,_,g]=fold(t).slice(0,-1);if(Number.isNaN(b[0]))throw new Error("impossible construction!");const y=[0,0,1];t=2*Math.PI/3;const M=g.roro(y,-t),v=M.sub([0,0,M[2]]).roro(y,Math.PI/3).uvec().mul(l[1]).add([0,0,M[2]+b[2]-l[2]]);let P=[l,u,h,b,_.roro(y,t),_,g,M,M.roro(y,-t),v,v.roro(y,-t),v.roro(y,-2*t)];return P.map((t=>t.add([0,0,(P[0][2]-P.slice(-1)[0][2])/2])))}function ico_axis_2(r,e=n,o=s){const[i,c,d]=[r[0].norm(),r[1].norm(),r[2].sub(r[1]).norm()],l=[i/2,0,0],u=[-i/2,0,0],h=[0,-i*a/2,-(i*a-i)/2],p=[0,i*a/2,-(i*a-i)/2];function fold(t){let a=u.add(h).div(2),[n,s]=[a.sub(l),h.sub(u).uvec()];const i=a.add(n.roro(s,t)),p=i.roro([0,0,1],Math.PI);t=r[0].angle(r[1]),[n,s]=[h.sub(l).uvec().mul(c),h.cross(l).uvec()];const m=n.roro(s,t);a=l.add(m.proj(n));const b=l.add(m);[n,s]=[b.sub(a),l.sub(h).uvec()];const f=t=>d-a.add(n.roro(s,t)).sub(p).norm();t=bisection(f,...brackets(f,0,2*Math.PI,e).next().value,o,e).slice(-1);const _=a.add(n.roro(s,t));return[i,p,_,i.sub([0,0,i[2]]).norm()-_.sub([0,0,_[2]]).norm()]}const m=Math.PI/180/10;t=0;for(let a=0;a*mfold(t).slice(-1)[0];try{t=bisection(obj,...brackets(obj,t,Math.PI/4,e).next().value,o,e).slice(-1)}catch(v){throw new Error("impossible construction!")}const[b,_,g]=fold(t).slice(0,-1);if(Number.isNaN(b[0]))throw new Error("impossible construction!");obj=t=>l.roro([0,0,1],t).add([0,0,g[2]+b[2]]).sub(_).norm()-c;try{t=bisection(obj,...brackets(obj,0,2*Math.PI,e).next().value,o,e).slice(-1)}catch(v){throw new Error("impossible construction!")}const y=l.roro([0,0,1],t).add([0,0,g[2]+b[2]]),M=y.sub([0,0,y[2]]).uvec().roro([0,0,1],Math.PI/2).mul(p[1]).add([0,0,g[2]+b[2]-p[2]]);return coor=[l,u,h,p,b,_,g,g.roro([0,0,1],Math.PI),M,M.roro([0,0,1],Math.PI),y,y.roro([0,0,1],Math.PI)],coor.map((t=>t.add([0,0,(coor[0][2]-coor.slice(-1)[0][2])/2])))}function model_sa_error(t){const r=ck_vectors(calc_tile(t.t,t.R).basis,t.h,t.k,t.H,t.K),e=[[r[3],r[0]],[r[0],r[1]],[r[1],r[2]]],o=["","",ico_axis_2,ico_axis_3,"",ico_axis_5][t.a](r,n,s),a=ico_config(t.a),i=a.t_id.map((t=>parseInt(t[1]))).reduce(((t,r)=>tc[t[1]-1]+=a.t_rep[r]));const d=e.slice(0,i).map(((t,r)=>[...t[0],0].cross([...t[1],0]).norm()/2*c[r])).sum();return(d-a.v_idx.map((t=>t.map((t=>o[t])))).map(((t,r)=>t[1].sub(t[0]).cross(t[2].sub(t[0])).norm()/2*a.t_rep[r])).sum())/d}function lattice_config(t,r,e,o,a,n){const s=calc_tile(n,a),i=ck_vectors(s.basis,t,r,e,o),c=Array.from(tile_grid(i,s.basis)),d=c.map(s.tile),l=c.filter((t=>t.is_vertex)).map((t=>t.coor)).concat([[0,0]]);return d.flat().forEach((t=>{t.data.offset=t.data.mer+(l.some((r=>[t.position.x,t.position.y].sub(r).norm()<=s.radius))?0:3),t.data.centroid=t.segments.map((t=>t.point)).reduce(((t,r)=>t.add(r))).divide(t.segments.length)})),{tile:s,ck:i,lattice:d}}function calc_facets(t){const r=[[3,0],[0,1],[1,2]].map((r=>[t.ck[r[0]],t.ck[r[1]]])).map((t=>new paper.Path({segments:[[0,0],...t],closed:!0,data:{vectors:[[0,0],...t]}}))),e=r.map((r=>new paper.Group({children:t.lattice.flatMap((t=>t.map((t=>{const e=t.intersect(r,{insert:!1});return e.data.has_centroid=r.contains(e.data.centroid),e.data.centroid_on_vertex=r.segments.map((t=>t.point.getDistance(e.data.centroid))).some((t=>t<1e-5)),e})))).filter((t=>t.segments.length>0)),data:r.data})));return r.forEach((t=>t.remove())),e}function draw_net(t){const[r,e,o,a,n,s]=["h","k","H","K","R","t"].map((r=>t[r])),i=lattice_config(r,e,o,a,n,s),c=i.ck;i.lattice.flat().forEach((r=>r.style.fillColor=t["mer_color_"+r.data.offset]+t["mer_alpha_"+r.data.offset]));const d=calc_facets(i);let l;if(5===t.a){const r=new paper.Group(d.slice(0,2)).rotate(-degrees(c[0].angle([1,0]))).scale(-1,1),e=r.clone().rotate(180),o=e.children[1].children.flatMap((t=>t.segments)).map((t=>t.point)).reduce(((t,r)=>t.y{const[a,n]=[r.clone(),e.clone()];return a.position.x+=o*r.children[0].bounds.width,n.position.x+=o*r.children[0].bounds.width,[a,n]})).flatMap((t=>t.children)),position:paper.view.center,style:{strokeColor:t.line_color+t.line_alpha,strokeWidth:t.line_size,strokeCap:"round",strokeJoin:"round"}}),[r,e].forEach((t=>t.remove()))}else if(3===t.a){const r=c[0].angle([1,0]),e=new paper.Group(d).rotate(-degrees(r)).scale(-1,1),o=d[0].clone().rotate(180);o.position.x+=c[0].norm()/2;const a=[o.bounds.topLeft,o.bounds.topRight,o.bounds.bottomCenter].reduce(((t,r)=>t.add(r))).divide(3),n=o.bounds.topRight.add(new paper.Point([1,0].mul(c[1].norm()).rot(-(Math.PI/3-c[1].angle(c[2]))))),s=o.bounds.topRight.add(new paper.Point([1,0].mul(c[2].norm()).rot(-(Math.PI/3-c[1].angle(c[2])+c[1].angle(c[2]))))),i=new paper.Group([...[1,2,3].map(((t,r)=>e.clone().rotate(120*r,a)))]);i.children.slice(0,-1).forEach((t=>t.children[1].remove()));const u=i.clone().rotate(180);u.bounds.left=Math.min(n.x,s.x),u.bounds.bottom=o.bounds.topRight.y,u.position.y-=u.children[1].children[0].bounds.bottom-n.y,u.children.forEach((t=>i.addChild(t.clone()))),i.position=paper.view.center,l=new paper.Group({children:i.children.flatMap((t=>t.children.flat())),style:{strokeColor:t.line_color+t.line_alpha,strokeWidth:t.line_size,strokeCap:"round",strokeJoin:"round"}}),[o,e,u].forEach((t=>t.remove()))}else{if(2!==t.a)throw new Error("invalid symmetry mode!");{const r=c[0].angle([1,0]),e=new paper.Group(d).rotate(-degrees(r)).rotate(-60).scale(-1,1),o=e.children[1].clone(),a=e.children[0].bounds.topLeft.subtract(e.children[0].bounds.bottomCenter);o.position=o.position.add(a);const n=new paper.Group([e.clone(),e.children[0].clone().rotate(60,e.children[0].bounds.topRight),o.clone()]),s=n.clone().rotate(180,n.children[0].children[0].bounds.topRight);s.position=s.position.add(a.rotate(240));const i=new paper.Group([n,s]),u=i.children[0].children[2].children.filter((t=>1===t.data.offset)).flatMap((t=>t.segments.map((t=>t.point)))).filter((t=>Math.abs(t.getDistance(i.children[0].children[1].bounds.bottomLeft)-c[1].norm())<1e-5)).reduce(((t,r)=>t.yt.children)).flatMap((t=>t.children));l=new paper.Group({children:[...m.filter(((t,r)=>r%3==0)).flatMap((t=>t.children)),...m.filter(((t,r)=>r%3!=0))],style:{strokeColor:t.line_color+t.line_alpha,strokeWidth:t.line_size,strokeCap:"round",strokeJoin:"round"}}),[2,5,8,11].forEach((t=>l.children[t].remove())),[e,o,n,s,i,h].forEach((t=>t.remove()))}}return d.forEach((t=>t.remove())),i.lattice.forEach((t=>t.forEach((t=>t.remove())))),l.scale(-1,1)}function draw_capsid(t){const[r,e,o,a,c,d]=["h","k","H","K","R","t"].map((r=>t[r])),l=lattice_config(r,e,o,a,c,d);l.lattice.flat().forEach((r=>r.style.fillColor=t["mer_color_"+r.data.offset]+t["mer_alpha_"+r.data.offset]));const u=calc_facets(l),h=ico_config(t.a),p=["","",ico_axis_2,ico_axis_3,"",ico_axis_5][t.a](l.ck,n,s),m=2*Math.PI/t.a,b=r==o&&e==a?r=>spherize(r,p[0].norm(),t.s):r=>cylinderize(r,p,t.a,t.s),_=camera(...[t.θ,t.ψ,t.φ].map(radians));let g=[];for(let n=0,s=0;nt.concat(1))))),e=[0,1,2].map((t=>p[h.v_idx[n][t]]));for(let o=0;ot.roro([0,0,1],o*m))),n=mmul(T(a),r),i=t.children.map((t=>{const r=t.segments.map((t=>[t.point.x,t.point.y,1])).map((t=>mmul(n,t.T()).flat())).map((t=>b(t))).map((t=>mmul(_,t.concat(1).T()).flat())),e=mmul(_,b(mmul(n,[t.data.centroid.x,t.data.centroid.y,1].T()).flat()).concat(1).T()).flat();return new paper.Path({segments:r.map((t=>t.slice(0,2))),data:Object.assign({},t.data,{id:s,centroid:e,segments_3D:r,normal:r[1].sub(r[0]).cross(r[2].sub(r[0])).uvec()}),closed:!0,style:t.style})}));g=g.concat(new paper.Group({children:i,data:{type:"facet",centroid:mmul(_,b(a.centroid()).concat(1).T()).flat()}}))}}const y=p.map((t=>mmul(_,t.concat(1).T()).flat()));let M=t.penton_fiber_toggle?h.v_con.map((t=>t.map((t=>y[t])).reduce(((t,r)=>t.add(r)),[0,0,0]).uvec())).map(((r,e)=>[y[e],y[e].add(r.mul(t.fiber_length))])):[];M=M.concat(g.flatMap((t=>t.children)).filter((r=>t["mer_toggle_"+r.data.offset]&&r.data.has_centroid)).map((r=>{const e=r.data.centroid;return[e,e.add(r.data.normal.mul(t.fiber_length))]})));let v=[];M.forEach((t=>{let r=0;for(let e of v)t[0].sub(e[0][0]).norm()[t[0][0],t.map((t=>t[1])).reduce(((t,r)=>t.add(r)),[0,0,0]).div(t.length)])).map((t=>new paper.Path.Line({from:t[0],to:t[1],data:{centroid:t[1].mul(2)}})));const P=t.knob_toggle?M.map((r=>new paper.Path.Circle({center:r.segments[1].point,radius:t.knob_size,data:{centroid:r.data.centroid}}))):[];g=g.concat(M).concat(P),g.sort(((t,r)=>t.data.centroid[2]-r.data.centroid[2]));const w=new paper.Group({children:g,position:paper.view.center,style:{strokeColor:t.line_color+t.line_alpha,strokeWidth:t.line_size,strokeCap:"round",strokeJoin:"round"}});return P.forEach((r=>r.style.fillColor=t.knob_color+t.knob_alpha)),M.forEach((r=>{r.style.strokeColor=t.fiber_color+t.fiber_alpha,r.style.strokeWidth=t.fiber_size})),u.forEach((t=>t.remove())),l.lattice.forEach((t=>t.forEach((t=>t.remove())))),w}Array.prototype.mul=function(t){return this.map((r=>r*t))},Array.prototype.div=function(t){return this.map((r=>r/t))},Array.prototype.add=function(t){return this.map(((r,e)=>r+t[e]))},Array.prototype.sub=function(t){return this.map(((r,e)=>r-t[e]))},Array.prototype.dot=function(t){return this.map(((r,e)=>r*t[e])).reduce(((t,r)=>t+r))},Array.prototype.sum=function(t=0){return this.reduce(((t,r)=>t+r),t)},Array.prototype.centroid=function(){return this.reduce(((t,r)=>t.add(r))).div(this.length)},Array.prototype.cross=function(t){return[this[1]*t[2]-this[2]*t[1],this[2]*t[0]-this[0]*t[2],this[0]*t[1]-this[1]*t[0]]},Array.prototype.rot=function(t){const[r,e]=[Math.cos(t),Math.sin(t)];return mmul([[r,-e],[e,r]],this.T()).flat()},Array.prototype.roro=function(t,r){return this.mul(Math.cos(r)).add(t.cross(this).mul(Math.sin(r))).add(t.mul(t.dot(this)).mul(1-Math.cos(r)))},Array.prototype.norm=function(){return Math.sqrt(this.map((t=>t*t)).sum())},Array.prototype.uvec=function(){return this.div(this.norm())},Array.prototype.angle=function(t){return Math.acos(this.dot(t)/(this.norm()*t.norm()))},Array.prototype.proj=function(t){return t.mul(this.dot(t)/t.dot(t))},Array.prototype.T=function(){return this.map((t=>[t]))},Array.prototype.has=function(t){return this.some((r=>r.length===t.length&&r.every(((r,e)=>r===t[e]))))},"undefined"!=typeof exports&&(module.exports={ico_axis_2:ico_axis_2,ico_axis_3:ico_axis_3,ico_axis_5:ico_axis_5}); \ No newline at end of file