diff options
Diffstat (limited to 'Build/source/utils/asymptote/webgl/gl.js')
-rw-r--r-- | Build/source/utils/asymptote/webgl/gl.js | 1130 |
1 files changed, 956 insertions, 174 deletions
diff --git a/Build/source/utils/asymptote/webgl/gl.js b/Build/source/utils/asymptote/webgl/gl.js index ec5930e0193..2cdef3e776e 100644 --- a/Build/source/utils/asymptote/webgl/gl.js +++ b/Build/source/utils/asymptote/webgl/gl.js @@ -41,7 +41,6 @@ let maxMaterials; // Limit on number of materials allowed in shader let halfCanvasWidth,halfCanvasHeight; let pixel=0.75; // Adaptive rendering constant. -let BezierFactor=0.4; let FillFactor=0.1; let Zoom; @@ -54,8 +53,6 @@ let resizeStep=1.2; let lastzoom; let H; // maximum camera view half-height -let Fuzz2=1000*Number.EPSILON; -let Fuzz4=Fuzz2*Fuzz2; let third=1/3; let rotMat=mat4.create(); @@ -65,9 +62,8 @@ let viewMat=mat4.create(); // view matrix let projViewMat=mat4.create(); // projection view matrix let normMat=mat3.create(); let viewMat3=mat3.create(); // 3x3 view matrix -let rotMats=mat4.create(); let cjMatInv=mat4.create(); -let translMat=mat4.create(); +let T=mat4.create(); // Temporary matrix let zmin,zmax; let center={x:0,y:0,z:0}; @@ -89,6 +85,7 @@ let colorBuffer; let indexBuffer; let remesh=true; +let wireframe=0; let mouseDownOrTouchActive=false; let lastMouseX=null; let lastMouseY=null; @@ -147,13 +144,20 @@ function initShaders() maxMaterials=Math.floor((maxUniforms-14)/4); Nmaterials=Math.min(Math.max(Nmaterials,Materials.length),maxMaterials); - noNormalShader=initShader(); pixelShader=initShader(["WIDTH"]); materialShader=initShader(["NORMAL"]); colorShader=initShader(["NORMAL","COLOR"]); transparentShader=initShader(["NORMAL","COLOR","TRANSPARENT"]); } +function deleteShaders() +{ + gl.deleteProgram(transparentShader); + gl.deleteProgram(colorShader); + gl.deleteProgram(materialShader); + gl.deleteProgram(pixelShader); +} + // Create buffers for the patch and its subdivisions. function setBuffers() { @@ -177,7 +181,6 @@ function saveAttributes() a.Nmaterials=Nmaterials; a.maxMaterials=maxMaterials; - a.noNormalShader=noNormalShader; a.pixelShader=pixelShader; a.materialShader=materialShader; a.colorShader=colorShader; @@ -193,7 +196,6 @@ function restoreAttributes() Nmaterials=a.Nmaterials; maxMaterials=a.maxMaterials; - noNormalShader=a.noNormalShader; pixelShader=a.pixelShader; materialShader=a.materialShader; colorShader=a.colorShader; @@ -241,6 +243,14 @@ function initGL() setBuffers(); indexExt=gl.getExtension("OES_element_index_uint"); + + TRIANGLES=gl.TRIANGLES; + material0Data=new vertexBuffer(gl.POINTS); + material1Data=new vertexBuffer(gl.LINES); + materialData=new vertexBuffer(); + colorData=new vertexBuffer(); + transparentData=new vertexBuffer(); + triangleData=new vertexBuffer(); } function getShader(gl,shaderScript,type,options=[]) @@ -251,7 +261,7 @@ function getShader(gl,shaderScript,type,options=[]) #else precision mediump float; #endif - #define nlights ${Lights.length}\n + #define nlights ${wireframe == 0 ? Lights.length : 0}\n const int Nlights=${Math.max(Lights.length,1)};\n #define Nmaterials ${Nmaterials}\n`; @@ -274,8 +284,7 @@ function drawBuffer(data,shader,indices=data.indices) { if(data.indices.length == 0) return; - let pixel=shader == pixelShader; - let normal=shader != noNormalShader && !pixel; + let normal=shader != pixelShader; setUniforms(data,shader); @@ -283,7 +292,7 @@ function drawBuffer(data,shader,indices=data.indices) gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(data.vertices), gl.STATIC_DRAW); gl.vertexAttribPointer(positionAttribute,3,gl.FLOAT,false, - normal ? 24 : (pixel ? 16 : 12),0); + normal ? 24 : 16,0); if(normal && Lights.length > 0) gl.vertexAttribPointer(normalAttribute,3,gl.FLOAT,false,24,12); else if(pixel) @@ -306,13 +315,16 @@ function drawBuffer(data,shader,indices=data.indices) indexExt ? new Uint32Array(indices) : new Uint16Array(indices),gl.STATIC_DRAW); - gl.drawElements(normal ? gl.TRIANGLES : (pixel ? gl.POINTS : gl.LINES), + gl.drawElements(normal ? (wireframe ? gl.LINES : data.type) : gl.POINTS, indices.length, indexExt ? gl.UNSIGNED_INT : gl.UNSIGNED_SHORT,0); } +let TRIANGLES; + class vertexBuffer { - constructor() { + constructor(type) { + this.type=type ? type : TRIANGLES; this.clear(); } clear() { @@ -354,15 +366,6 @@ class vertexBuffer { return this.nvertices++; } - // material vertex without normal - vertex1(v) { - this.vertices.push(v[0]); - this.vertices.push(v[1]); - this.vertices.push(v[2]); - this.materialIndices.push(materialIndex); - return this.nvertices++; - } - // material vertex with width and without normal vertex0(v,width) { this.vertices.push(v[0]); @@ -400,13 +403,12 @@ class vertexBuffer { } } -let material0Data=new vertexBuffer(); // pixels -let material1Data=new vertexBuffer(); // material Bezier curves -let materialData=new vertexBuffer(); // material Bezier patches & triangles -let colorData=new vertexBuffer(); // colored Bezier patches & triangles -let transparentData=new vertexBuffer(); // transparent patches & triangles -let triangleData=new vertexBuffer(); // opaque indexed triangles - +let material0Data; // pixels +let material1Data; // material Bezier curves +let materialData; // material Bezier patches & triangles +let colorData; // colored Bezier patches & triangles +let transparentData; // transparent patches & triangles +let triangleData; // opaque indexed triangles let materialIndex; @@ -590,7 +592,7 @@ class BezierPatch extends Geometry { for(let i=1; i < n; ++i) this.epsilon=Math.max(this.epsilon, abs2([p[i][0]-p0[0],p[i][1]-p0[1],p[i][2]-p0[2]])); - this.epsilon *= Fuzz4; + this.epsilon *= Number.EPSILON } processTriangle(p) { @@ -600,15 +602,30 @@ class BezierPatch extends Geometry { let n=unit(cross([p1[0]-p0[0],p1[1]-p0[1],p1[2]-p0[2]], [p2[0]-p0[0],p2[1]-p0[1],p2[2]-p0[2]])); if(!this.offscreen([p0,p1,p2])) { + let i0,i1,i2; if(this.color) { - this.data.indices.push(this.data.Vertex(p0,n,this.color[0])); - this.data.indices.push(this.data.Vertex(p1,n,this.color[1])); - this.data.indices.push(this.data.Vertex(p2,n,this.color[2])); + i0=this.data.Vertex(p0,n,this.color[0]); + i1=this.data.Vertex(p1,n,this.color[1]); + i2=this.data.Vertex(p2,n,this.color[2]); } else { - this.data.indices.push(this.vertex(p0,n)); - this.data.indices.push(this.vertex(p1,n)); - this.data.indices.push(this.vertex(p2,n)); + i0=this.vertex(p0,n); + i1=this.vertex(p1,n); + i2=this.vertex(p2,n); } + + if(wireframe == 0) { + this.data.indices.push(i0); + this.data.indices.push(i1); + this.data.indices.push(i2); + } else { + this.data.indices.push(i0); + this.data.indices.push(i1); + this.data.indices.push(i1); + this.data.indices.push(i2); + this.data.indices.push(i2); + this.data.indices.push(i0); + } + this.append(); } } @@ -636,53 +653,83 @@ class BezierPatch extends Geometry { i2=this.vertex(p2,n); i3=this.vertex(p3,n); } - this.data.indices.push(i0); - this.data.indices.push(i1); - this.data.indices.push(i2); - this.data.indices.push(i0); - this.data.indices.push(i2); - this.data.indices.push(i3); + if(wireframe == 0) { + this.data.indices.push(i0); + this.data.indices.push(i1); + this.data.indices.push(i2); + + this.data.indices.push(i0); + this.data.indices.push(i2); + this.data.indices.push(i3); + } else { + this.data.indices.push(i0); + this.data.indices.push(i1); + this.data.indices.push(i1); + this.data.indices.push(i2); + this.data.indices.push(i2); + this.data.indices.push(i3); + this.data.indices.push(i3); + this.data.indices.push(i0); + } this.append(); } } + curve(p,a,b,c,d) { + new BezierCurve([p[a],p[b],p[c],p[d]],0,materialIndex, + this.Min,this.Max).render(); + } + process(p) { - if(this.transparent) // Override materialIndex to encode color vs material + if(this.transparent && wireframe != 1) + // Override materialIndex to encode color vs material materialIndex=this.color ? -1-materialIndex : 1+materialIndex; if(p.length == 10) return this.process3(p); if(p.length == 3) return this.processTriangle(p); if(p.length == 4) return this.processQuad(p); + if(wireframe == 1) { + this.curve(p,0,4,8,12); + this.curve(p,12,13,14,15); + this.curve(p,15,11,7,3); + this.curve(p,3,2,1,0); + return; + } + let p0=p[0]; let p3=p[3]; let p12=p[12]; let p15=p[15]; let n0=this.normal(p3,p[2],p[1],p0,p[4],p[8],p12); - if(iszero(n0)) { + if(abs2(n0) < this.epsilon) { n0=this.normal(p3,p[2],p[1],p0,p[13],p[14],p15); - if(iszero(n0)) n0=this.normal(p15,p[11],p[7],p3,p[4],p[8],p12); + if(abs2(n0) < this.epsilon) + n0=this.normal(p15,p[11],p[7],p3,p[4],p[8],p12); } let n1=this.normal(p0,p[4],p[8],p12,p[13],p[14],p15); - if(iszero(n1)) { + if(abs2(n1) < this.epsilon) { n1=this.normal(p0,p[4],p[8],p12,p[11],p[7],p3); - if(iszero(n1)) n1=this.normal(p3,p[2],p[1],p0,p[13],p[14],p15); + if(abs2(n1) < this.epsilon) + n1=this.normal(p3,p[2],p[1],p0,p[13],p[14],p15); } let n2=this.normal(p12,p[13],p[14],p15,p[11],p[7],p3); - if(iszero(n2)) { + if(abs2(n2) < this.epsilon) { n2=this.normal(p12,p[13],p[14],p15,p[2],p[1],p0); - if(iszero(n2)) n2=this.normal(p0,p[4],p[8],p12,p[11],p[7],p3); + if(abs2(n2) < this.epsilon) + n2=this.normal(p0,p[4],p[8],p12,p[11],p[7],p3); } let n3=this.normal(p15,p[11],p[7],p3,p[2],p[1],p0); - if(iszero(n3)) { + if(abs2(n3) < this.epsilon) { n3=this.normal(p15,p[11],p[7],p3,p[4],p[8],p12); - if(iszero(n3)) n3=this.normal(p12,p[13],p[14],p15,p[2],p[1],p0); + if(abs2(n3) < this.epsilon) + n3=this.normal(p12,p[13],p[14],p15,p[2],p[1],p0); } if(this.color) { @@ -719,29 +766,43 @@ class BezierPatch extends Geometry { } Render(p,I0,I1,I2,I3,P0,P1,P2,P3,flat0,flat1,flat2,flat3,C0,C1,C2,C3) { - if(this.Distance(p) < this.res2) { // Bezier patch is flat + let d=this.Distance(p); + if(d[0] < this.res2 && d[1] < this.res2) { // Bezier patch is flat if(!this.offscreen([P0,P1,P2])) { - this.data.indices.push(I0); - this.data.indices.push(I1); - this.data.indices.push(I2); + if(wireframe == 0) { + this.data.indices.push(I0); + this.data.indices.push(I1); + this.data.indices.push(I2); + } else { + this.data.indices.push(I0); + this.data.indices.push(I1); + this.data.indices.push(I1); + this.data.indices.push(I2); + } } if(!this.offscreen([P0,P2,P3])) { - this.data.indices.push(I0); - this.data.indices.push(I2); - this.data.indices.push(I3); + if(wireframe == 0) { + this.data.indices.push(I0); + this.data.indices.push(I2); + this.data.indices.push(I3); + } else { + this.data.indices.push(I2); + this.data.indices.push(I3); + this.data.indices.push(I3); + this.data.indices.push(I0); + } } } else { // Approximate bounds by bounding box of control polyhedron. if(this.offscreen(p)) return; - /* Control points are indexed as follows: + /* Control points are indexed as follows: Coordinate +----- Index - - 03 13 23 33 + 03 13 23 33 +-----+-----+-----+ |3 |7 |11 |15 | | | | @@ -755,10 +816,225 @@ class BezierPatch extends Geometry { | | | | |00 |10 |20 |30 +-----+-----+-----+ - 0 4 8 12 + 0 4 8 12 + + */ + let p0=p[0]; + let p3=p[3]; + let p12=p[12]; + let p15=p[15]; - Subdivision: + if(d[0] < this.res2) { // flat in horizontal direction; split vertically + /* + P refers to a corner + m refers to a midpoint + s refers to a subpatch + + +--------+--------+ + |P3 P2| + | | + | s1 | + | | + | | + m1 +-----------------+ m0 + | | + | | + | s0 | + | | + |P0 P1| + +-----------------+ + + */ + + let c0=new Split3(p0,p[1],p[2],p3); + let c1=new Split3(p[4],p[5],p[6],p[7]); + let c2=new Split3(p[8],p[9],p[10],p[11]); + let c3=new Split3(p12,p[13],p[14],p15); + + let s0=[p0 ,c0.m0,c0.m3,c0.m5, + p[4],c1.m0,c1.m3,c1.m5, + p[8],c2.m0,c2.m3,c2.m5, + p12 ,c3.m0,c3.m3,c3.m5]; + + let s1=[c0.m5,c0.m4,c0.m2,p3, + c1.m5,c1.m4,c1.m2,p[7], + c2.m5,c2.m4,c2.m2,p[11], + c3.m5,c3.m4,c3.m2,p15]; + + let n0=this.normal(s0[12],s0[13],s0[14],s0[15],s0[11],s0[7],s0[3]); + if(abs2(n0) <= this.epsilon) { + n0=this.normal(s0[12],s0[13],s0[14],s0[15],s0[2],s0[1],s0[0]); + if(abs2(n0) <= this.epsilon) + n0=this.normal(s0[0],s0[4],s0[8],s0[12],s0[11],s0[7],s0[3]); + } + + let n1=this.normal(s1[3],s1[2],s1[1],s1[0],s1[4],s1[8],s1[12]); + if(abs2(n1) <= this.epsilon) { + n1=this.normal(s1[3],s1[2],s1[1],s1[0],s1[13],s1[14],s1[15]); + if(abs2(n1) <= this.epsilon) + n1=this.normal(s1[15],s1[11],s1[7],s1[3],s1[4],s1[8],s1[12]); + } + + let e=this.Epsilon; + + // A kludge to remove subdivision cracks, only applied the first time + // an edge is found to be flat before the rest of the subpatch is. + + let m0=[0.5*(P1[0]+P2[0]), + 0.5*(P1[1]+P2[1]), + 0.5*(P1[2]+P2[2])]; + if(!flat1) { + if((flat1=Straightness(p12,p[13],p[14],p15) < this.res2)) { + let r=unit(this.differential(s1[12],s1[8],s1[4],s1[0])); + m0=[m0[0]-e*r[0],m0[1]-e*r[1],m0[2]-e*r[2]]; + } + else m0=s0[15]; + } + + let m1=[0.5*(P3[0]+P0[0]), + 0.5*(P3[1]+P0[1]), + 0.5*(P3[2]+P0[2])]; + if(!flat3) { + if((flat3=Straightness(p0,p[1],p[2],p3) < this.res2)) { + let r=unit(this.differential(s0[3],s0[7],s0[11],s0[15])); + m1=[m1[0]-e*r[0],m1[1]-e*r[1],m1[2]-e*r[2]]; + } + else m1=s1[0]; + } + + if(C0) { + let c0=Array(4); + let c1=Array(4); + for(let i=0; i < 4; ++i) { + c0[i]=0.5*(C1[i]+C2[i]); + c1[i]=0.5*(C3[i]+C0[i]); + } + + let i0=this.data.Vertex(m0,n0,c0); + let i1=this.data.Vertex(m1,n1,c1); + + this.Render(s0,I0,I1,i0,i1,P0,P1,m0,m1,flat0,flat1,false,flat3, + C0,C1,c0,c1); + this.Render(s1,i1,i0,I2,I3,m1,m0,P2,P3,false,flat1,flat2,flat3, + c1,c0,C2,C3); + } else { + let i0=this.vertex(m0,n0); + let i1=this.vertex(m1,n1); + + this.Render(s0,I0,I1,i0,i1,P0,P1,m0,m1,flat0,flat1,false,flat3); + this.Render(s1,i1,i0,I2,I3,m1,m0,P2,P3,false,flat1,flat2,flat3); + } + return; + } + + if(d[1] < this.res2) { // flat in vertical direction; split horizontally + /* + P refers to a corner + m refers to a midpoint + s refers to a subpatch + + m1 + +--------+--------+ + |P3 | P2| + | | | + | | | + | | | + | | | + | s0 | s1 | + | | | + | | | + | | | + | | | + |P0 | P1| + +--------+--------+ + m0 + + */ + + let c0=new Split3(p0,p[4],p[8],p12); + let c1=new Split3(p[1],p[5],p[9],p[13]); + let c2=new Split3(p[2],p[6],p[10],p[14]); + let c3=new Split3(p3,p[7],p[11],p15); + + let s0=[p0,p[1],p[2],p3, + c0.m0,c1.m0,c2.m0,c3.m0, + c0.m3,c1.m3,c2.m3,c3.m3, + c0.m5,c1.m5,c2.m5,c3.m5]; + + let s1=[c0.m5,c1.m5,c2.m5,c3.m5, + c0.m4,c1.m4,c2.m4,c3.m4, + c0.m2,c1.m2,c2.m2,c3.m2, + p12,p[13],p[14],p15]; + + let n0=this.normal(s0[0],s0[4],s0[8],s0[12],s0[13],s0[14],s0[15]); + if(abs2(n0) <= this.epsilon) { + n0=this.normal(s0[0],s0[4],s0[8],s0[12],s0[11],s0[7],s0[3]); + if(abs2(n0) <= this.epsilon) + n0=this.normal(s0[3],s0[2],s0[1],s0[0],s0[13],s0[14],s0[15]); + } + + let n1=this.normal(s1[15],s1[11],s1[7],s1[3],s1[2],s1[1],s1[0]); + if(abs2(n1) <= this.epsilon) { + n1=this.normal(s1[15],s1[11],s1[7],s1[3],s1[4],s1[8],s1[12]); + if(abs2(n1) <= this.epsilon) + n1=this.normal(s1[12],s1[13],s1[14],s1[15],s1[2],s1[1],s1[0]); + } + + let e=this.Epsilon; + + // A kludge to remove subdivision cracks, only applied the first time + // an edge is found to be flat before the rest of the subpatch is. + + let m0=[0.5*(P0[0]+P1[0]), + 0.5*(P0[1]+P1[1]), + 0.5*(P0[2]+P1[2])]; + if(!flat0) { + if((flat0=Straightness(p0,p[4],p[8],p12) < this.res2)) { + let r=unit(this.differential(s1[0],s1[1],s1[2],s1[3])); + m0=[m0[0]-e*r[0],m0[1]-e*r[1],m0[2]-e*r[2]]; + } + else m0=s0[12]; + } + + let m1=[0.5*(P2[0]+P3[0]), + 0.5*(P2[1]+P3[1]), + 0.5*(P2[2]+P3[2])]; + if(!flat2) { + if((flat2=Straightness(p15,p[11],p[7],p3) < this.res2)) { + let r=unit(this.differential(s0[15],s0[14],s0[13],s0[12])); + m1=[m1[0]-e*r[0],m1[1]-e*r[1],m1[2]-e*r[2]]; + } + else m1=s1[3]; + } + + if(C0) { + let c0=Array(4); + let c1=Array(4); + for(let i=0; i < 4; ++i) { + c0[i]=0.5*(C0[i]+C1[i]); + c1[i]=0.5*(C2[i]+C3[i]); + } + + let i0=this.data.Vertex(m0,n0,c0); + let i1=this.data.Vertex(m1,n1,c1); + + this.Render(s0,I0,i0,i1,I3,P0,m0,m1,P3,flat0,false,flat2,flat3, + C0,c0,c1,C3); + this.Render(s1,i0,I1,I2,i1,m0,P1,P2,m1,flat0,flat1,flat2,false, + c0,C1,C2,c1); + } else { + let i0=this.vertex(m0,n0); + let i1=this.vertex(m1,n1); + + this.Render(s0,I0,i0,i1,I3,P0,m0,m1,P3,flat0,false,flat2,flat3); + this.Render(s1,i0,I1,I2,i1,m0,P1,P2,m1,flat0,flat1,flat2,false); + } + return; + } + + /* + Horizontal and vertical subdivision: P refers to a corner m refers to a midpoint s refers to a subpatch @@ -769,8 +1045,8 @@ class BezierPatch extends Geometry { | | | | s3 | s2 | | | | - | |m4 | - m3+--------+--------+m1 + | | m4 | + m3 +--------+--------+ m1 | | | | | | | s0 | s1 | @@ -779,13 +1055,8 @@ class BezierPatch extends Geometry { +--------+--------+ m0 */ - - // Subdivide patch: - let p0=p[0]; - let p3=p[3]; - let p12=p[12]; - let p15=p[15]; + // Subdivide patch: let c0=new Split3(p0,p[1],p[2],p3); let c1=new Split3(p[4],p[5],p[6],p[7]); @@ -812,30 +1083,30 @@ class BezierPatch extends Geometry { let m4=s0[15]; let n0=this.normal(s0[0],s0[4],s0[8],s0[12],s0[13],s0[14],s0[15]); - if(iszero(n0)) { + if(abs2(n0) < this.epsilon) { n0=this.normal(s0[0],s0[4],s0[8],s0[12],s0[11],s0[7],s0[3]); - if(iszero(n0)) + if(abs2(n0) < this.epsilon) n0=this.normal(s0[3],s0[2],s0[1],s0[0],s0[13],s0[14],s0[15]); } let n1=this.normal(s1[12],s1[13],s1[14],s1[15],s1[11],s1[7],s1[3]); - if(iszero(n1)) { + if(abs2(n1) < this.epsilon) { n1=this.normal(s1[12],s1[13],s1[14],s1[15],s1[2],s1[1],s1[0]); - if(iszero(n1)) + if(abs2(n1) < this.epsilon) n1=this.normal(s1[0],s1[4],s1[8],s1[12],s1[11],s1[7],s1[3]); } let n2=this.normal(s2[15],s2[11],s2[7],s2[3],s2[2],s2[1],s2[0]); - if(iszero(n2)) { + if(abs2(n2) < this.epsilon) { n2=this.normal(s2[15],s2[11],s2[7],s2[3],s2[4],s2[8],s2[12]); - if(iszero(n2)) + if(abs2(n2) < this.epsilon) n2=this.normal(s2[12],s2[13],s2[14],s2[15],s2[2],s2[1],s2[0]); } let n3=this.normal(s3[3],s3[2],s3[1],s3[0],s3[4],s3[8],s3[12]); - if(iszero(n3)) { + if(abs2(n3) < this.epsilon) { n3=this.normal(s3[3],s3[2],s3[1],s3[0],s3[13],s3[14],s3[15]); - if(iszero(n3)) + if(abs2(n3) < this.epsilon) n3=this.normal(s3[15],s3[11],s3[7],s3[3],s3[4],s3[8],s3[12]); } @@ -850,7 +1121,7 @@ class BezierPatch extends Geometry { 0.5*(P0[2]+P1[2])]; if(!flat0) { if((flat0=Straightness(p0,p[4],p[8],p12) < this.res2)) { - let r=unit(this.derivative(s1[0],s1[1],s1[2],s1[3])); + let r=unit(this.differential(s1[0],s1[1],s1[2],s1[3])); m0=[m0[0]-e*r[0],m0[1]-e*r[1],m0[2]-e*r[2]]; } else m0=s0[12]; @@ -861,7 +1132,7 @@ class BezierPatch extends Geometry { 0.5*(P1[2]+P2[2])]; if(!flat1) { if((flat1=Straightness(p12,p[13],p[14],p15) < this.res2)) { - let r=unit(this.derivative(s2[12],s2[8],s2[4],s2[0])); + let r=unit(this.differential(s2[12],s2[8],s2[4],s2[0])); m1=[m1[0]-e*r[0],m1[1]-e*r[1],m1[2]-e*r[2]]; } else m1=s1[15]; @@ -872,7 +1143,7 @@ class BezierPatch extends Geometry { 0.5*(P2[2]+P3[2])]; if(!flat2) { if((flat2=Straightness(p15,p[11],p[7],p3) < this.res2)) { - let r=unit(this.derivative(s3[15],s2[14],s2[13],s1[12])); + let r=unit(this.differential(s3[15],s3[14],s3[13],s3[12])); m2=[m2[0]-e*r[0],m2[1]-e*r[1],m2[2]-e*r[2]]; } else m2=s2[3]; @@ -883,7 +1154,7 @@ class BezierPatch extends Geometry { 0.5*(P3[2]+P0[2])]; if(!flat3) { if((flat3=Straightness(p0,p[1],p[2],p3) < this.res2)) { - let r=unit(this.derivative(s0[3],s0[7],s0[11],s0[15])); + let r=unit(this.differential(s0[3],s0[7],s0[11],s0[15])); m3=[m3[0]-e*r[0],m3[1]-e*r[1],m3[2]-e*r[2]]; } else m3=s3[0]; @@ -934,7 +1205,12 @@ class BezierPatch extends Geometry { // Render a Bezier triangle via subdivision. process3(p) { - this.Res2=BezierFactor*BezierFactor*this.res2; + if(wireframe == 1) { + this.curve(p,0,1,3,6); + this.curve(p,6,7,8,9); + this.curve(p,9,5,2,0); + return; + } let p0=p[0]; let p6=p[6]; @@ -966,11 +1242,20 @@ class BezierPatch extends Geometry { } Render3(p,I0,I1,I2,P0,P1,P2,flat0,flat1,flat2,C0,C1,C2) { - if(this.Distance3(p) < this.Res2) { // Bezier triangle is flat + if(this.Distance3(p) < this.res2) { // Bezier triangle is flat if(!this.offscreen([P0,P1,P2])) { - this.data.indices.push(I0); - this.data.indices.push(I1); - this.data.indices.push(I2); + if(wireframe == 0) { + this.data.indices.push(I0); + this.data.indices.push(I1); + this.data.indices.push(I2); + } else { + this.data.indices.push(I0); + this.data.indices.push(I1); + this.data.indices.push(I1); + this.data.indices.push(I2); + this.data.indices.push(I2); + this.data.indices.push(I0); + } } } else { // Approximate bounds by bounding box of control polyhedron. @@ -1174,7 +1459,7 @@ class BezierPatch extends Geometry { 0.5*(P1[2]+P2[2])]; if(!flat0) { if((flat0=Straightness(r300,p210,p120,u030) < this.res2)) { - let r=unit(this.sumderivative(c[0],c[2],c[5],c[9],c[1],c[3],c[6])); + let r=unit(this.sumdifferential(c[0],c[2],c[5],c[9],c[1],c[3],c[6])); m0=[m0[0]-e*r[0],m0[1]-e*r[1],m0[2]-e*r[2]]; } else m0=r030; @@ -1186,7 +1471,7 @@ class BezierPatch extends Geometry { 0.5*(P2[2]+P0[2])]; if(!flat1) { if((flat1=Straightness(l003,p012,p021,u030) < this.res2)) { - let r=unit(this.sumderivative(c[6],c[3],c[1],c[0],c[7],c[8],c[9])); + let r=unit(this.sumdifferential(c[6],c[3],c[1],c[0],c[7],c[8],c[9])); m1=[m1[0]-e*r[0],m1[1]-e*r[1],m1[2]-e*r[2]]; } else m1=l030; @@ -1197,7 +1482,7 @@ class BezierPatch extends Geometry { 0.5*(P0[2]+P1[2])]; if(!flat2) { if((flat2=Straightness(l003,p102,p201,r300) < this.res2)) { - let r=unit(this.sumderivative(c[9],c[8],c[7],c[6],c[5],c[2],c[0])); + let r=unit(this.sumdifferential(c[9],c[8],c[7],c[6],c[5],c[2],c[0])); m2=[m2[0]-e*r[0],m2[1]-e*r[1],m2[2]-e*r[2]]; } else m2=l300; @@ -1241,20 +1526,23 @@ class BezierPatch extends Geometry { let p12=p[12]; let p15=p[15]; - // Check the flatness of a patch. - let d=Distance2(p15,p0,this.normal(p3,p[2],p[1],p0,p[4],p[8],p12)); - - // Determine how straight the edges are. - d=Math.max(d,Straightness(p0,p[1],p[2],p3)); - d=Math.max(d,Straightness(p0,p[4],p[8],p12)); - d=Math.max(d,Straightness(p3,p[7],p[11],p15)); - d=Math.max(d,Straightness(p12,p[13],p[14],p15)); - - // Determine how straight the interior control curves are. - d=Math.max(d,Straightness(p[4],p[5],p[6],p[7])); - d=Math.max(d,Straightness(p[8],p[9],p[10],p[11])); - d=Math.max(d,Straightness(p[1],p[5],p[9],p[13])); - return Math.max(d,Straightness(p[2],p[6],p[10],p[14])); + // Check the horizontal flatness. + let h=Flatness(p0,p12,p3,p15); + // Check straightness of the horizontal edges and interior control curves. + h=Math.max(Straightness(p0,p[4],p[8],p12)); + h=Math.max(h,Straightness(p[1],p[5],p[9],p[13])); + h=Math.max(h,Straightness(p3,p[7],p[11],p15)); + h=Math.max(h,Straightness(p[2],p[6],p[10],p[14])); + + // Check the vertical flatness. + let v=Flatness(p0,p3,p12,p15); + // Check straightness of the vertical edges and interior control curves. + v=Math.max(v,Straightness(p0,p[1],p[2],p3)); + v=Math.max(v,Straightness(p[4],p[5],p[6],p[7])); + v=Math.max(v,Straightness(p[8],p[9],p[10],p[11])); + v=Math.max(v,Straightness(p12,p[13],p[14],p15)); + + return [h,v]; } // Check the flatness of a Bezier triangle @@ -1275,60 +1563,76 @@ class BezierPatch extends Geometry { return Math.max(d,Straightness(p6,p[7],p[8],p9)); } - derivative(p0,p1,p2,p3) { - let lp=[p1[0]-p0[0],p1[1]-p0[1],p1[2]-p0[2]]; - if(abs2(lp) > this.epsilon) - return lp; + // Return the differential of the Bezier curve p0,p1,p2,p3 at 0. + differential(p0,p1,p2,p3) { + let p=[3*(p1[0]-p0[0]),3*(p1[1]-p0[1]),3*(p1[2]-p0[2])]; + if(abs2(p) > this.epsilon) + return p; - let lpp=bezierPP(p0,p1,p2); - if(abs2(lpp) > this.epsilon) - return lpp; + p=bezierPP(p0,p1,p2); + if(abs2(p) > this.epsilon) + return p; return bezierPPP(p0,p1,p2,p3); } - sumderivative(p0,p1,p2,p3,p4,p5,p6) { - let d0=this.derivative(p0,p1,p2,p3); - let d1=this.derivative(p0,p4,p5,p6); + sumdifferential(p0,p1,p2,p3,p4,p5,p6) { + let d0=this.differential(p0,p1,p2,p3); + let d1=this.differential(p0,p4,p5,p6); return [d0[0]+d1[0],d0[1]+d1[1],d0[2]+d1[2]]; } normal(left3,left2,left1,middle,right1,right2,right3) { - let ux=right1[0]-middle[0]; - let uy=right1[1]-middle[1]; - let uz=right1[2]-middle[2]; - let vx=left1[0]-middle[0]; - let vy=left1[1]-middle[1]; - let vz=left1[2]-middle[2]; + let ux=3*(right1[0]-middle[0]); + let uy=3*(right1[1]-middle[1]); + let uz=3*(right1[2]-middle[2]); + let vx=3*(left1[0]-middle[0]); + let vy=3*(left1[1]-middle[1]); + let vz=3*(left1[2]-middle[2]); + let n=[uy*vz-uz*vy, uz*vx-ux*vz, ux*vy-uy*vx]; if(abs2(n) > this.epsilon) - return unit(n); + return n; let lp=[vx,vy,vz]; let rp=[ux,uy,uz]; + let lpp=bezierPP(middle,left1,left2); let rpp=bezierPP(middle,right1,right2); + let a=cross(rpp,lp); let b=cross(rp,lpp); n=[a[0]+b[0], a[1]+b[1], a[2]+b[2]]; if(abs2(n) > this.epsilon) - return unit(n); + return n; let lppp=bezierPPP(middle,left1,left2,left3); let rppp=bezierPPP(middle,right1,right2,right3); - a=cross(rpp,lpp); - b=cross(rp,lppp); - let c=cross(rppp,lp); - let d=cross(rppp,lpp); - let e=cross(rpp,lppp); - let f=cross(rppp,lppp); - return unit([9*a[0]+3*(b[0]+c[0]+d[0]+e[0])+f[0], - 9*a[1]+3*(b[1]+c[1]+d[1]+e[1])+f[1], - 9*a[2]+3*(b[2]+c[2]+d[2]+e[2])+f[2]]); + + a=cross(rp,lppp); + b=cross(rppp,lp); + let c=cross(rpp,lpp); + + n=[a[0]+b[0]+c[0], + a[1]+b[1]+c[1], + a[2]+b[2]+c[2]]; + if(abs2(n) > this.epsilon) + return n; + + a=cross(rppp,lpp); + b=cross(rpp,lppp); + + n=[a[0]+b[0], + a[1]+b[1], + a[2]+b[2]]; + if(abs2(n) > this.epsilon) + return n; + + return cross(rppp,lppp); } } @@ -1350,17 +1654,26 @@ class BezierCurve extends Geometry { let p0=p[0]; let p1=p[1]; if(!this.offscreen([p0,p1])) { - this.data.indices.push(this.data.vertex1(p0)); - this.data.indices.push(this.data.vertex1(p1)); + let n=[0,0,1]; + this.data.indices.push(this.data.vertex(p0,n)); + this.data.indices.push(this.data.vertex(p1,n)); this.append(); } } process(p) { if(p.length == 2) return this.processLine(p); + + let p0=p[0]; + let p1=p[1]; + let p2=p[2]; + let p3=p[3]; + + let n0=this.normal(bezierP(p0,p1),bezierPP(p0,p1,p2)); + let n1=this.normal(bezierP(p2,p3),bezierPP(p3,p2,p1)); - let i0=this.data.vertex1(p[0]); - let i3=this.data.vertex1(p[3]); + let i0=this.data.vertex(p0,n0); + let i3=this.data.vertex(p3,n1); this.Render(p,i0,i3); if(this.data.indices.length > 0) this.append(); @@ -1394,12 +1707,21 @@ class BezierCurve extends Geometry { let s0=[p0,m0,m3,m5]; let s1=[m5,m4,m2,p3]; - let i0=this.data.vertex1(m5); + let n0=this.normal(bezierPh(p0,p1,p2,p3),bezierPPh(p0,p1,p2,p3)); + let i0=this.data.vertex(m5,n0); this.Render(s0,I0,i0); this.Render(s1,i0,I1); } } + + normal(bP,bPP) { + let bPbP=dot(bP,bP); + let bPbPP=dot(bP,bPP); + return [bPbP*bPP[0]-bPbPP*bP[0], + bPbP*bPP[1]-bPbPP*bP[1], + bPbP*bPP[2]-bPbPP*bP[2]]; + } } class Pixel extends Geometry { @@ -1454,7 +1776,8 @@ class Triangles extends Geometry { process(p) { // Override materialIndex to encode color vs material - materialIndex=this.Colors.length > 0 ? -1-materialIndex : 1+materialIndex; + materialIndex=this.Colors.length > 0 ? + -1-materialIndex : 1+materialIndex; for(let i=0, n=this.Indices.length; i < n; ++i) { let index=this.Indices[i]; @@ -1472,13 +1795,31 @@ class Triangles extends Geometry { let C1=this.Colors[CI[1]]; let C2=this.Colors[CI[2]]; this.transparent |= C0[3]+C1[3]+C2[3] < 765; - this.data.iVertex(PI[0],P0,this.Normals[NI[0]],C0); - this.data.iVertex(PI[1],P1,this.Normals[NI[1]],C1); - this.data.iVertex(PI[2],P2,this.Normals[NI[2]],C2); + if(wireframe == 0) { + this.data.iVertex(PI[0],P0,this.Normals[NI[0]],C0); + this.data.iVertex(PI[1],P1,this.Normals[NI[1]],C1); + this.data.iVertex(PI[2],P2,this.Normals[NI[2]],C2); + } else { + this.data.iVertex(PI[0],P0,this.Normals[NI[0]],C0); + this.data.iVertex(PI[1],P1,this.Normals[NI[1]],C1); + this.data.iVertex(PI[1],P1,this.Normals[NI[1]],C1); + this.data.iVertex(PI[2],P2,this.Normals[NI[2]],C2); + this.data.iVertex(PI[2],P2,this.Normals[NI[2]],C2); + this.data.iVertex(PI[0],P0,this.Normals[NI[0]],C0); + } } else { - this.data.iVertex(PI[0],P0,this.Normals[NI[0]]); - this.data.iVertex(PI[1],P1,this.Normals[NI[1]]); - this.data.iVertex(PI[2],P2,this.Normals[NI[2]]); + if(wireframe == 0) { + this.data.iVertex(PI[0],P0,this.Normals[NI[0]]); + this.data.iVertex(PI[1],P1,this.Normals[NI[1]]); + this.data.iVertex(PI[2],P2,this.Normals[NI[2]]); + } else { + this.data.iVertex(PI[0],P0,this.Normals[NI[0]]); + this.data.iVertex(PI[1],P1,this.Normals[NI[1]]); + this.data.iVertex(PI[1],P1,this.Normals[NI[1]]); + this.data.iVertex(PI[2],P2,this.Normals[NI[2]]); + this.data.iVertex(PI[2],P2,this.Normals[NI[2]]); + this.data.iVertex(PI[0],P0,this.Normals[NI[0]]); + } } } } @@ -1546,11 +1887,6 @@ class Split3 { } } -function iszero(v) -{ - return v[0] == 0 && v[1] == 0 && v[2] == 0; -} - function unit(v) { let norm=1/(Math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]) || 1); @@ -1574,17 +1910,26 @@ function cross(u,v) u[0]*v[1]-u[1]*v[0]]; } -// Return one-sixth of the second derivative of the Bezier curve defined -// by a,b,c,d at 0. +// Return one-third of the first derivative of the Bezier curve defined +// by a,b,c,d at t=0. +function bezierP(a,b) +{ + return [b[0]-a[0], + b[1]-a[1], + b[2]-a[2]]; +} + +// Return one-half of the second derivative of the Bezier curve defined +// by a,b,c,d at t=0. function bezierPP(a,b,c) { - return [a[0]+c[0]-2*b[0], - a[1]+c[1]-2*b[1], - a[2]+c[2]-2*b[2]]; + return [3*(a[0]+c[0])-6*b[0], + 3*(a[1]+c[1])-6*b[1], + 3*(a[2]+c[2])-6*b[2]]; } -// Return one-third of the third derivative of the Bezier curve defined by -// a,b,c,d at 0. +// Return one-sixth of the third derivative of the Bezier curve defined by +// a,b,c,d at t=0. function bezierPPP(a,b,c,d) { return [d[0]-a[0]+3*(b[0]-c[0]), @@ -1592,6 +1937,24 @@ function bezierPPP(a,b,c,d) d[2]-a[2]+3*(b[2]-c[2])]; } +// Return four-thirds of the first derivative of the Bezier curve defined by +// a,b,c,d at t=1/2. +function bezierPh(a,b,c,d) +{ + return [c[0]+d[0]-a[0]-b[0], + c[1]+d[1]-a[1]-b[1], + c[2]+d[2]-a[2]-b[2]]; +} + +// Return two-thirds of the second derivative of the Bezier curve defined by +// a,b,c,d at t=1/2. +function bezierPPh(a,b,c,d) +{ + return [3*a[0]-5*b[0]+c[0]+d[0], + 3*a[1]-5*b[1]+c[1]+d[1], + 3*a[2]-5*b[2]+c[2]+d[2]]; +} + /** * Return the maximum distance squared of points c0 and c1 from * the respective internal control points of z0--z1. @@ -1603,14 +1966,12 @@ function Straightness(z0,c0,c1,z1) abs2([z1[0]-v[0]-c1[0],z1[1]-v[1]-c1[1],z1[2]-v[2]-c1[2]])); } -/** - * Return the perpendicular distance squared of a point z from the plane - * through u with unit normal n. - */ -function Distance2(z,u,n) +// Return one ninth of the relative flatness squared of a--b and c--d. +function Flatness(a,b,c,d) { - let d=dot([z[0]-u[0],z[1]-u[1],z[2]-u[2]],n); - return d*d; + let u=[b[0]-a[0],b[1]-a[1],b[2]-a[2]]; + let v=[d[0]-c[0],d[1]-c[1],d[2]-c[2]]; + return Math.max(abs2(cross(u,unit(v))),abs2(cross(v,unit(u))))/9; } // Return the vertices of the box containing 3d points m and M. @@ -1620,6 +1981,22 @@ function corners(m,M) [M[0],m[1],m[2]],[M[0],m[1],M[2]],[M[0],M[1],m[2]],M]; } +function minbound(v) { + return [ + Math.min(v[0][0],v[1][0],v[2][0],v[3][0],v[4][0],v[5][0],v[6][0],v[7][0]), + Math.min(v[0][1],v[1][1],v[2][1],v[3][1],v[4][1],v[5][1],v[6][1],v[7][1]), + Math.min(v[0][2],v[1][2],v[2][2],v[3][2],v[4][2],v[5][2],v[6][2],v[7][2]) + ]; +} + +function maxbound(v) { + return [ + Math.max(v[0][0],v[1][0],v[2][0],v[3][0],v[4][0],v[5][0],v[6][0],v[7][0]), + Math.max(v[0][1],v[1][1],v[2][1],v[3][1],v[4][1],v[5][1],v[6][1],v[7][1]), + Math.max(v[0][2],v[1][2],v[2][2],v[3][2],v[4][2],v[5][2],v[6][2],v[7][2]) + ]; +} + /** * Perform a change of basis * @param {*} out Out Matrix @@ -1630,10 +2007,10 @@ function corners(m,M) function COBTarget(out,mat) { - mat4.fromTranslation(translMat,[center.x,center.y,center.z]) - mat4.invert(cjMatInv,translMat); + mat4.fromTranslation(T,[center.x,center.y,center.z]) + mat4.invert(cjMatInv,T); mat4.multiply(out,mat,cjMatInv); - mat4.multiply(out,translMat,out); + mat4.multiply(out,T,out); } function setUniforms(data,shader) @@ -1647,7 +2024,7 @@ function setUniforms(data,shader) if(pixel) gl.enableVertexAttribArray(widthAttribute); - let normals=shader != noNormalShader && !pixel && Lights.length > 0; + let normals=!pixel && Lights.length > 0; if(normals) gl.enableVertexAttribArray(normalAttribute); @@ -1728,8 +2105,8 @@ function rotateScene(lastX,lastY,rawX,rawY,factor) if(lastX == rawX && lastY == rawY) return; let [angle,axis]=arcball([lastX,-lastY],[rawX,-rawY]); - mat4.fromRotation(rotMats,2*factor*ArcballFactor*angle/lastzoom,axis); - mat4.multiply(rotMat,rotMats,rotMat); + mat4.fromRotation(T,2*factor*ArcballFactor*angle/lastzoom,axis); + mat4.multiply(rotMat,T,rotMat); } function shiftScene(lastX,lastY,rawX,rawY) @@ -1897,6 +2274,17 @@ function handleKey(event) case 'h': home(); break; + case 'm': + ++wireframe; + if(wireframe == 3) wireframe=0; + if(wireframe != 2) { + if(!embedded) + deleteShaders(); + initShaders(); + } + remesh=true; + draw(); + break; case '+': case '=': case '>': @@ -2028,7 +2416,7 @@ function drawMaterial0() function drawMaterial1() { - drawBuffer(material1Data,noNormalShader); + drawBuffer(material1Data,materialShader); material1Data.clear(); } @@ -2053,6 +2441,11 @@ function drawTriangle() function drawTransparent() { let indices=transparentData.indices; + if(wireframe > 0) { + drawBuffer(transparentData,transparentShader,indices); + transparentData.clear(); + return; + } if(indices.length > 0) { transformVertices(transparentData.vertices); @@ -2122,7 +2515,7 @@ function draw() context.drawImage(offscreen,0,0); } - remesh=false; + if(wireframe == 0) remesh=false; } function setDimensions(width,height,X,Y) @@ -2238,9 +2631,9 @@ function shrink() Math.max((canvasHeight/resizeStep+0.5),1)); } -let pixelShader,noNormalShader,materialShader,colorShader,transparentShader; +let pixelShader,materialShader,colorShader,transparentShader; -function webGLStart() +function webGLInit() { canvas=document.getElementById("Asymptote"); embedded=window.top.document != document; @@ -2296,3 +2689,392 @@ function webGLStart() canvas.addEventListener("touchmove",handleTouchMove,false); document.addEventListener("keydown",handleKey,false); } + +let listen=false; + +class Align { + constructor(center,dir) { + this.center=center; + if(dir) { + let theta=dir[0]; + let phi=dir[1]; + + this.ct=Math.cos(theta); + this.st=Math.sin(theta); + this.cp=Math.cos(phi); + this.sp=Math.sin(phi); + } + } + + T0(v) { + return [v[0]+this.center[0],v[1]+this.center[1],v[2]+this.center[2]]; + } + + T(v) { + let x=v[0]; + let Y=v[1]; + let z=v[2]; + let X=x*this.ct+z*this.st; + return [X*this.cp-Y*this.sp+this.center[0], + X*this.sp+Y*this.cp+this.center[1], + -x*this.st+z*this.ct+this.center[2]]; + }; +} + +function Tcorners(T,m,M) { + let v=[T(m),T([m[0],m[1],M[2]]),T([m[0],M[1],m[2]]), + T([m[0],M[1],M[2]]),T([M[0],m[1],m[2]]), + T([M[0],m[1],M[2]]),T([M[0],M[1],m[2]]),T(M)]; + return [minbound(v),maxbound(v)]; +} + +// draw a sphere of radius r about center +// (or optionally a hemisphere symmetric about direction dir) +function sphere(center,r,CenterIndex,MaterialIndex,dir) +{ + let octant=[[ + [1,0,0], + [1,0,0.370106805057161], + [0.798938033457256,0,0.6932530716149], + [0.500083269410627,0,0.866169630634358], + + [1,0.552284749830793,0], + [1,0.552284749830793,0.370106805057161], + [0.798938033457256,0.441241291938247,0.6932530716149], + [0.500083269410627,0.276188363341013,0.866169630634358], + + [0.552284749830793,1,0], + [0.552284749830793,1,0.370106805057161], + [0.441241291938247,0.798938033457256,0.6932530716149], + [0.276188363341013,0.500083269410627,0.866169630634358], + + [0,1,0], + [0,1,0.370106805057161], + [0,0.798938033457256,0.6932530716149], + [0,0.500083269410627,0.866169630634358] + ],[ + [0.500083269410627,0,0.866169630634358], + [0.500083269410627,0.276188363341013,0.866169630634358], + [0.35297776917154,0,0.951284475617087], + [0.276188363341013,0.500083269410627,0.866169630634358], + [0.264153721902467,0.264153721902467,1], + [0.182177944773632,0,1], + [0,0.500083269410627,0.866169630634358], + [0,0.35297776917154,0.951284475617087], + [0,0.182177944773632,1], + [0,0,1] + ]]; + + let rx,ry,rz; + let A=new Align(center,dir); + let s,t,z; + + if(dir) { + s=1; + z=0; + t=A.T.bind(A); + } else { + s=-1; + z=-r; + t=A.T0.bind(A); + } + + function T(V) { + let p=Array(V.length); + for(let i=0; i < V.length; ++i) { + let v=V[i]; + p[i]=t([rx*v[0],ry*v[1],rz*v[2]]); + } + return p; + } + + let v=Tcorners(t,[-r,-r,z],[r,r,r]); + let Min=v[0], Max=v[1]; + for(let i=-1; i <= 1; i += 2) { + rx=i*r; + for(let j=-1; j <= 1; j += 2) { + ry=j*r; + for(let k=s; k <= 1; k += 2) { + rz=k*r; + for(let m=0; m < 2; ++m) + P.push(new BezierPatch(T(octant[m]),CenterIndex,MaterialIndex, + Min,Max)); + } + } + } +} + +let a=4/3*(Math.sqrt(2)-1); + +// draw a disk of radius r aligned in direction dir +function disk(center,r,CenterIndex,MaterialIndex,dir) +{ + let b=1-2*a/3; + + let unitdisk=[ + [1,0,0], + [1,-a,0], + [a,-1,0], + [0,-1,0], + + [1,a,0], + [b,0,0], + [0,-b,0], + [-a,-1,0], + + [a,1,0], + [0,b,0], + [-b,0,0], + [-1,-a,0], + + [0,1,0], + [-a,1,0], + [-1,a,0], + [-1,0,0] + ]; + + let A=new Align(center,dir); + + function T(V) { + let p=Array(V.length); + for(let i=0; i < V.length; ++i) { + let v=V[i]; + p[i]=A.T([r*v[0],r*v[1],0]); + } + return p; + } + + let v=Tcorners(A.T.bind(A),[-r,-r,0],[r,r,0]); + P.push(new BezierPatch(T(unitdisk),CenterIndex,MaterialIndex,v[0],v[1])); +} + +// draw a cylinder with circular base of radius r about center and height h +// aligned in direction dir +function cylinder(center,r,h,CenterIndex,MaterialIndex,dir,core) +{ + let unitcylinder=[ + [1,0,0], + [1,0,1/3], + [1,0,2/3], + [1,0,1], + + [1,a,0], + [1,a,1/3], + [1,a,2/3], + [1,a,1], + + [a,1,0], + [a,1,1/3], + [a,1,2/3], + [a,1,1], + + [0,1,0], + [0,1,1/3], + [0,1,2/3], + [0,1,1] + ]; + + let rx,ry,rz; + let A=new Align(center,dir); + + function T(V) { + let p=Array(V.length); + for(let i=0; i < V.length; ++i) { + let v=V[i]; + p[i]=A.T([rx*v[0],ry*v[1],h*v[2]]); + } + return p; + } + + let v=Tcorners(A.T.bind(A),[-r,-r,0],[r,r,h]); + let Min=v[0], Max=v[1]; + + for(let i=-1; i <= 1; i += 2) { + rx=i*r; + for(let j=-1; j <= 1; j += 2) { + ry=j*r; + P.push(new BezierPatch(T(unitcylinder),CenterIndex,MaterialIndex, + Min,Max)); + } + } + + if(core) { + let Center=A.T([0,0,h]); + P.push(new BezierCurve([center,Center],CenterIndex,MaterialIndex, + center,Center)); + } +} + +function rmf(z0,c0,c1,z1,t) +{ + class Rmf { + constructor(p,r,t) { + this.p=p; + this.r=r; + this.t=t; + this.s=cross(t,r); + } + } + + // Return a unit vector perpendicular to a given unit vector v. + function perp(v) + { + let u=cross(v,[0,1,0]); + let norm=Number.EPSILON*abs2(v); + if(abs2(u) > norm) return unit(u); + u=cross(v,[0,0,1]); + return (abs2(u) > norm) ? unit(u) : [1,0,0]; + } + + let norm=Number.EPSILON*Math.max(abs2(z0),abs2(c0),abs2(c1), + abs2(z1)); + +// Special case of dir for t in (0,1]. + function dir(t) { + if(t == 1) { + let dir=[z1[0]-c1[0], + z1[1]-c1[1], + z1[2]-c1[2]]; + if(abs2(dir) > norm) return unit(dir); + dir=[2*c1[0]-c0[0]-z1[0], + 2*c1[1]-c0[1]-z1[1], + 2*c1[2]-c0[2]-z1[2]]; + if(abs2(dir) > norm) return unit(dir); + return [z1[0]-z0[0]+3*(c0[0]-c1[0]), + z1[1]-z0[1]+3*(c0[1]-c1[1]), + z1[2]-z0[2]+3*(c0[2]-c1[2])]; + } + let a=[z1[0]-z0[0]+3*(c0[0]-c1[0]), + z1[1]-z0[1]+3*(c0[1]-c1[1]), + z1[2]-z0[2]+3*(c0[2]-c1[2])]; + let b=[2*(z0[0]+c1[0])-4*c0[0], + 2*(z0[1]+c1[1])-4*c0[1], + 2*(z0[2]+c1[2])-4*c0[2]]; + let c=[c0[0]-z0[0],c0[1]-z0[1],c0[2]-z0[2]]; + let t2=t*t; + let dir=[a[0]*t2+b[0]*t+c[0], + a[1]*t2+b[1]*t+c[1], + a[2]*t2+b[2]*t+c[2]]; + if(abs2(dir) > norm) return unit(dir); + t2=2*t; + dir=[a[0]*t2+b[0], + a[1]*t2+b[1], + a[2]*t2+b[2]]; + if(abs2(dir) > norm) return unit(dir); + return unit(a); + } + + let R=Array(t.length); + let T=[c0[0]-z0[0], + c0[1]-z0[1], + c0[2]-z0[2]]; + if(abs2(T) < norm) { + T=[z0[0]-2*c0[0]+c1[0], + z0[1]-2*c0[1]+c1[1], + z0[2]-2*c0[2]+c1[2]]; + if(abs2(T) < norm) + T=[z1[0]-z0[0]+3*(c0[0]-c1[0]), + z1[1]-z0[1]+3*(c0[1]-c1[1]), + z1[2]-z0[2]+3*(c0[2]-c1[2])]; + } + T=unit(T); + let Tp=perp(T); + R[0]=new Rmf(z0,Tp,T); + for(let i=1; i < t.length; ++i) { + let Ri=R[i-1]; + let s=t[i]; + let onemt=1-s; + let onemt2=onemt*onemt; + let onemt3=onemt2*onemt; + let s3=3*s; + onemt2 *= s3; + onemt *= s3*s; + let t3=s*s*s; + let p=[ + onemt3*z0[0]+onemt2*c0[0]+onemt*c1[0]+t3*z1[0], + onemt3*z0[1]+onemt2*c0[1]+onemt*c1[1]+t3*z1[1], + onemt3*z0[2]+onemt2*c0[2]+onemt*c1[2]+t3*z1[2]]; + let v1=[p[0]-Ri.p[0],p[1]-Ri.p[1],p[2]-Ri.p[2]]; + if(v1[0] != 0 || v1[1] != 0 || v1[2] != 0) { + let r=Ri.r; + let u1=unit(v1); + let ti=Ri.t; + let dotu1ti=dot(u1,ti) + let tp=[ti[0]-2*dotu1ti*u1[0], + ti[1]-2*dotu1ti*u1[1], + ti[2]-2*dotu1ti*u1[2]]; + ti=dir(s); + let dotu1r2=2*dot(u1,r); + let rp=[r[0]-dotu1r2*u1[0],r[1]-dotu1r2*u1[1],r[2]-dotu1r2*u1[2]]; + let u2=unit([ti[0]-tp[0],ti[1]-tp[1],ti[2]-tp[2]]); + let dotu2rp2=2*dot(u2,rp); + rp=[rp[0]-dotu2rp2*u2[0],rp[1]-dotu2rp2*u2[1],rp[2]-dotu2rp2*u2[2]]; + R[i]=new Rmf(p,unit(rp),unit(ti)); + } else + R[i]=R[i-1]; + } + return R; +} + +// draw a tube of width w using control points v +function tube(v,w,CenterIndex,MaterialIndex,Min,Max,core) +{ + let Rmf=rmf(v[0],v[1],v[2],v[3],[0,1/3,2/3,1]); + + let aw=a*w; + let arc=[[w,0],[w,aw],[aw,w],[0,w]]; + + function f(a,b,c,d) { + let s=Array(16); + for(let i=0; i < 4; ++i) { + let R=Rmf[i]; + + let R0=R.r[0], R1=R.s[0]; + let T0=R0*a+R1*b; + let T1=R0*c+R1*d; + + R0=R.r[1]; R1=R.s[1]; + let T4=R0*a+R1*b; + let T5=R0*c+R1*d; + + R0=R.r[2]; R1=R.s[2]; + let T8=R0*a+R1*b; + let T9=R0*c+R1*d; + + let w=v[i]; + let w0=w[0]; w1=w[1]; w2=w[2]; + for(let j=0; j < 4; ++j) { + let u=arc[j]; + let x=u[0], y=u[1]; + s[4*i+j]=[T0*x+T1*y+w0, + T4*x+T5*y+w1, + T8*x+T9*y+w2]; + } + } + P.push(new BezierPatch(s,CenterIndex,MaterialIndex,Min,Max)); + } + + f(1,0,0,1); + f(0,-1,1,0); + f(-1,0,0,-1); + f(0,1,-1,0); + + if(core) + P.push(new BezierCurve(v,CenterIndex,MaterialIndex,Min,Max)); +} + +function webGLStart() +{ + if(window.innerWidth == 0 || window.innerHeight == 0) { + if(!listen) { + listen=true; + window.addEventListener("resize",webGLStart,false); + } + } else { + if(listen) { + window.removeEventListener("resize",webGLStart,false); + listen=false; + } + webGLInit(); + } +} |