|
95 | 95 | var r=Math.cos(x); |
96 | 96 | return Math.sign(r)*Math.pow(Math.abs(r),n); |
97 | 97 | } |
98 | | -var RevolutionSurface=function(func,minval,maxval){ |
99 | | - this.func=func |
100 | | - this.minval=minval |
101 | | - this.maxval=maxval |
102 | | - this.evaluate=function(u,v){ |
103 | | - v=1-v; |
104 | | - v*=GLMath.PiTimes2; |
105 | | - u=minval+(maxval-minval)*u; |
106 | | - var r=this.func(u); |
107 | | - return [u,r*Math.cos(v),r*Math.sin(v)]; |
108 | | - } |
109 | | -} |
| 98 | + |
110 | 99 | /** |
111 | | -* |
112 | | -* |
113 | | -* @param {Function} phi Function of a single variable: |
114 | | -* y = f(x). This determines the "radius" of the surface |
115 | | -* of revolution along the y-axis. |
116 | | -* @param {Function} psi Function of a single variable |
117 | | -* that transforms the value x: x = f(a). If null, uses |
118 | | -* the function x = f(a) = x. |
119 | | -* |
| 100 | +* Parametric evaluator for a surface of revolution, which results by revolving |
| 101 | +* an X/Y curve around an axis. |
| 102 | +* @class |
| 103 | +* @alias SurfaceOfRevolution |
| 104 | +* @param {Function} curve Curve to rotate about the X-axis. |
| 105 | +* The curve function must contain a function |
| 106 | +* named "evaluate", which takes the following parameter:<ul> |
| 107 | +* <li><code>u</code> - A curve coordinate, generally from 0 to 1. |
| 108 | +* </ul> |
| 109 | +* The evaluator function returns an array of at least 2 elements: the first |
| 110 | +* element is the X coordinate of the curve's position, and the second |
| 111 | +* element is the Y coordinate. |
| 112 | +* @param {number} minval Smallest U-coordinate. |
| 113 | +* @param {number} maxval Largest U-coordinate. |
120 | 114 | */ |
121 | | -var SurfaceOfRevolution=function(phi,psi,minval,maxval){ |
122 | | - this.phi=phi; |
123 | | - this.psi=psi; |
| 115 | +var SurfaceOfRevolution=function(curve,minval,maxval){ |
| 116 | + this.curve=curve |
124 | 117 | this.minval=minval |
125 | 118 | this.maxval=maxval |
126 | 119 | this.evaluate=function(u,v){ |
|
129 | 122 | u=minval+(maxval-minval)*u; |
130 | 123 | var cosv = Math.cos(v); |
131 | 124 | var sinv = (v>=0 && v<6.283185307179586) ? (v<=3.141592653589793 ? Math.sqrt(1.0-cosv*cosv) : -Math.sqrt(1.0-cosv*cosv)) : Math.sin(v); |
132 | | - var r=this.phi(u); |
133 | | - return [this.psi ? this.psi(u) : u,r*cosv,r*sinv]; |
| 125 | + var curvepos=this.curve.evaluate(u); |
| 126 | + return [curvepos[0],curvepos[1]*cosv,curvepos[1]*sinv]; |
134 | 127 | } |
135 | 128 | } |
136 | | - |
137 | | -var FunctionSurface={ |
138 | | -create:function(func,minval,maxval){ |
139 | | - return new SurfaceOfRevolution(func,null,minval,maxval); |
| 129 | +/** |
| 130 | +* Parametric evaluator for a surface of revolution whose curve is the graph of |
| 131 | +* a single-variable function. |
| 132 | +* @param {Function} func Function whose graph will be |
| 133 | +* rotated about the X-axis. The function takes a number |
| 134 | +* as a single parameter and returns a number. |
| 135 | +* @param {number} minval Smallest parameter of the function. |
| 136 | +* @param {number} maxval Largest parameter of the function. |
| 137 | +* @return {SurfaceOfRevolution} |
| 138 | +*/ |
| 139 | +SurfaceOfRevolution.fromFunction=function(func,minval,maxval){ |
| 140 | + return new SurfaceOfRevolution({ |
| 141 | + "evaluate":function(u){ |
| 142 | + return [u,func(u),0]; |
| 143 | + }},minval,maxval); |
140 | 144 | } |
141 | | -}; |
142 | | - |
143 | | -var Torus={ |
144 | | -create:function(outerRadius,innerRadius,minval,maxval){ |
145 | | - return new SurfaceOfRevolution(function(v){ |
146 | | - return outerRadius+innerRadius*Math.cos(v); |
147 | | - },function(v){ |
148 | | - return innerRadius+Math.sin(v); |
149 | | - },minval,maxval); |
| 145 | +/** |
| 146 | +* Parametric evaluator for a torus, a special case of a surface of revolution. |
| 147 | +* @param {number} outerRadius Radius from the center to the innermost |
| 148 | +* part of the torus. |
| 149 | +* @param {number} innerRadius Radius from the inner edge to the innermost |
| 150 | +* part of the torus. |
| 151 | +* @param {Function|undefined} curve Object describing |
| 152 | +* a curve to serve as the cross section of the torus. |
| 153 | +* The curve need not be closed; in fact, certain special surfaces can result |
| 154 | +* by leaving the ends open. |
| 155 | +* The curve function must contain a function |
| 156 | +* named "evaluate", which takes the following parameter:<ul> |
| 157 | +* <li><code>u</code> - A curve coordinate, generally from 0 to 1. |
| 158 | +* </ul> |
| 159 | +* The evaluator function returns an array of at least 2 elements: the first |
| 160 | +* element is the X coordinate of the curve's position, and the second |
| 161 | +* element is the Y coordinate. If null or omitted, uses a circular cross section. |
| 162 | +* @return {SurfaceOfRevolution} |
| 163 | +*/ |
| 164 | +SurfaceOfRevolution.torus=function(outerRadius,innerRadius,curve){ |
| 165 | + if(!curve)curve={ |
| 166 | + "evaluate":function(u){ |
| 167 | + u*=GLMath.PiTimes2; |
| 168 | + return [Math.sin(u),Math.cos(v)] |
| 169 | + } |
| 170 | + } |
| 171 | + return new SurfaceOfRevolution({ |
| 172 | + "evaluate":function(u){ |
| 173 | + var curvept=curve.evaluate(u) |
| 174 | + var x=innerRadius*curvept[1]; |
| 175 | + var y=outerRadius+innerRadius*curvept[0]; |
| 176 | + return [x,y,0]; |
| 177 | + }},0,GLMath.PiTimes2); |
150 | 178 | } |
151 | | -}; |
152 | 179 |
|
153 | 180 | var KleinBottle=function(){ |
154 | 181 | this.evaluate=function(u,v){ |
155 | 182 | var cospi; |
156 | 183 | u*=GLMath.PiTimes2; |
157 | 184 | v*=GLMath.PiTimes2; |
158 | 185 | var x, y, z; |
159 | | - var sinu=Math.sin(u); |
160 | | - var sinv=Math.sin(v); |
161 | | - var cosu=Math.cos(u); |
162 | | - var cosv=Math.cos(v); |
| 186 | + var cosu = Math.cos(u); |
| 187 | + var sinu = (u>=0 && u<6.283185307179586) ? (u<=3.141592653589793 ? Math.sqrt(1.0-cosu*cosu) : -Math.sqrt(1.0-cosu*cosu)) : Math.sin(u); |
| 188 | + var cosv = Math.cos(v); |
| 189 | + var sinv = (v>=0 && v<6.283185307179586) ? (v<=3.141592653589793 ? Math.sqrt(1.0-cosv*cosv) : -Math.sqrt(1.0-cosv*cosv)) : Math.sin(v); |
163 | 190 | if(u<Math.PI){ |
164 | 191 | x = 3 * cosu * (1 + sinu) + (2 * (1 - cosu / 2)) * cosu * cosv; |
165 | 192 | z = -8 * sinu - 2 * (1 - cosu / 2) * sinu * cosv; |
|
277 | 304 | }) |
278 | 305 | addLink("Surface of revolution for f(x) = sin x",function(){ |
279 | 306 | pushSettings(function(allsettings){ |
280 | | - return makeMesh(FunctionSurface.create(function(x){ |
| 307 | + return makeMesh(SurfaceOfRevolution.fromFunction(function(x){ |
281 | 308 | return Math.sin(x) |
282 | 309 | },-Math.PI,Math.PI)) |
283 | 310 | },{ |
284 | 311 | }); |
285 | 312 | }) |
286 | 313 | addLink("Surface of revolution for f(x) = x<sup>2</sup>",function(){ |
287 | 314 | pushSettings(function(allsettings){ |
288 | | - return makeMesh(FunctionSurface.create(function(x){ |
| 315 | + return makeMesh(SurfaceOfRevolution.fromFunction(function(x){ |
289 | 316 | return x*x |
290 | 317 | },-1,1)) |
291 | 318 | },{ |
|
295 | 322 | addLink("Möbius-like strip",function(){ |
296 | 323 | pushSettings(function(allsettings){ |
297 | 324 | return makeMesh( |
298 | | - new ExtrudedTube(new TorusKnot( |
299 | | - allsettings["torusknot-p"], |
300 | | - allsettings["torusknot-q"] |
301 | | - ),0.1,TorusKnot.getFlatCurve(5)),100); |
| 325 | + new MoebiusLikeStrip( |
| 326 | + allsettings["moeb-maj"], |
| 327 | + allsettings["moeb-a"], |
| 328 | + allsettings["moeb-b"] |
| 329 | + )); |
302 | 330 | },{ |
303 | 331 | "moeb-maj":["Size",1.25,0.05,3.0,0.05], |
304 | 332 | "moeb-a":["Height",0.125,0.05,2.0,0.05], |
|
337 | 365 | "moeb2-w":["Width",0.25,0.05,2,0.05] |
338 | 366 | }); |
339 | 367 | }); |
| 368 | + |
| 369 | +var bsplineSurf=null; |
| 370 | +addLink("B-Spline Surface",function(){ |
| 371 | + pushSettings(function(allsettings){ |
| 372 | + var bspline=[] |
| 373 | + for(var i=0;i<=5;i++){ |
| 374 | + var c=[] |
| 375 | + for(var j=0;j<=5;j++){ |
| 376 | + var cp=[ |
| 377 | + (j*5.0/5)-2.5, |
| 378 | + (i*5.0/5)-2.5, |
| 379 | + Math.random()*2.5-1.25 |
| 380 | + ] |
| 381 | + c.push(cp) |
| 382 | + } |
| 383 | + bspline.push(c) |
| 384 | + } |
| 385 | + if(!bsplineSurf) |
| 386 | + bsplineSurf=BSplineSurface.clamped(bspline,3,3) |
| 387 | + return makeMesh(bsplineSurf); |
| 388 | + },{ |
| 389 | + }); |
| 390 | +}) |
340 | 391 | // Create the 3D scene; find the HTML canvas and pass it |
341 | 392 | // to Scene3D. |
342 | 393 | var scene=new Scene3D(document.getElementById("canvas")); |
|
0 commit comments