summaryrefslogtreecommitdiff
path: root/Build/source/utils/asymptote/webgl/gl.js
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/utils/asymptote/webgl/gl.js')
-rw-r--r--Build/source/utils/asymptote/webgl/gl.js1130
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();
+ }
+}