three.js 源码诠释(八十六)extras/geometries/TubeGeometry.js
three.js 源码注释(八十六)extras/geometries/TubeGeometry.js
商域无疆 (http://blog.****.net/omni360/)
本文遵循“署名-非商业用途-保持一致”创作公用协议
转载请保留此句:商域无疆 - 本博客专注于 敏捷开发及移动和物联设备研究:数据可视化、GOLANG、Html5、WEBGL、THREE.JS,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。
俺也是刚开始学,好多地儿肯定不对还请见谅.
以下代码是THREE.JS 源码文件中extras/geometries/TubeGeometry.js文件的注释.
更多更新在 : https://github.com/omni360/three.js.sourcecode
/** * @author WestLangley / https://github.com/WestLangley * @author zz85 / https://github.com/zz85 * @author miningold / https://github.com/miningold * * Modified from the TorusKnotGeometry by @oosmoxiecode * * Creates a tube which extrudes along a 3d spline * * Uses parallel transport frames as described in * http://www.cs.indiana.edu/pub/techreports/TR425.pdf */ /* ///TubeGeometry用来在三维空间内创建一个弯管对象. /// /// 用法: /// var CustomSinCurve = THREE.Curve.create( /// function ( scale ) { //custom curve constructor /// this.scale = (scale === undefined) ? 1 : scale; /// }, /// /// function ( t ) { //getPoint: t is between 0-1 /// var tx = t * 3 - 1.5, /// ty = Math.sin( 2 * Math.PI * t ), /// tz = 0; /// /// return new THREE.Vector3(tx, ty, tz).multiplyScalar(this.scale); /// } /// ); /// /// var path = new CustomSinCurve( 10 ); /// /// var geometry = new THREE.TubeGeometry( /// path, //path /// 20, //segments /// 2, //radius /// 8, //radiusSegments /// false //closed /// ); */ ///<summary>TubeGeometry</summary> ///<param name ="path" type="THREE.Path">弯管的路径</param> ///<param name ="segments" type="int">沿弯管路径上的细分线段数,默认为64</param> ///<param name ="radius" type="float">弯管的半径,默认为1</param> ///<param name ="radialSegments" type="int">弯管圆周方向上的细分线段数,默认为8</param> ///<param name ="closed" type="boolean">是否连接起始点,结束点,true为关闭.,默认为false</param> THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed ) { THREE.Geometry.call( this ); //调用Geometry对象的call方法,将原本属于Geometry的方法交给当前对象TubeGeometry来使用. this.parameters = { path: path, //弯管的路径 segments: segments, //沿弯管路径上的细分线段数,默认为64 radius: radius, //弯管的半径,默认为1 radialSegments: radialSegments, //弯管圆周方向上的细分线段数,默认为8 closed: closed //是否开口,如果设置为true,弯管两端没有封头,默认为false }; segments = segments || 64; //沿弯管路径上的细分线段数,默认为64 radius = radius || 1; //弯管的半径,默认为1 radialSegments = radialSegments || 8; //弯管圆周方向上的细分线段数,默认为8 closed = closed || false; //是否连接起始点,结束点,true为关闭.,默认为false var grid = []; var scope = this, tangent, normal, binormal, numpoints = segments + 1, x, y, z, tx, ty, tz, u, v, cx, cy, pos, pos2 = new THREE.Vector3(), i, j, ip, jp, a, b, c, d, uva, uvb, uvc, uvd; var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ), //调用THREE.TubeGeometry.FrenetFrames()方法,计算弗莱纳框架,得到路径的切线,法线,副法线 tangents = frames.tangents, normals = frames.normals, binormals = frames.binormals; // proxy internals // 内部代理 this.tangents = tangents; this.normals = normals; this.binormals = binormals; /* ///vert方法将x,y,z三个分量压入圆管的顶点数组. */ ///<summary>vert</summary> ///<param name ="x" type="float">三维向量的x分量</param> ///<param name ="y" type="float">三维向量的y分量</param> ///<param name ="z" type="float">三维向量的z分量</param> function vert( x, y, z ) { return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1; } // consruct the grid //构建网格 for ( i = 0; i < numpoints; i ++ ) { grid[ i ] = []; u = i / ( numpoints - 1 ); pos = path.getPointAt( u ); tangent = tangents[ i ]; normal = normals[ i ]; binormal = binormals[ i ]; for ( j = 0; j < radialSegments; j ++ ) { v = j / radialSegments * 2 * Math.PI; cx = - radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. cy = radius * Math.sin( v ); pos2.copy( pos ); pos2.x += cx * normal.x + cy * binormal.x; pos2.y += cx * normal.y + cy * binormal.y; pos2.z += cx * normal.z + cy * binormal.z; grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); } } // construct the mesh // 构建曲面 for ( i = 0; i < segments; i ++ ) { for ( j = 0; j < radialSegments; j ++ ) { ip = ( closed ) ? (i + 1) % segments : i + 1; jp = (j + 1) % radialSegments; a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** b = grid[ ip ][ j ]; c = grid[ ip ][ jp ]; d = grid[ i ][ jp ]; uva = new THREE.Vector2( i / segments, j / radialSegments ); uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments ); uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments ); uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments ); this.faces.push( new THREE.Face3( a, b, d ) ); this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); this.faces.push( new THREE.Face3( b, c, d ) ); this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); } } this.computeFaceNormals(); //计算面的法线 this.computeVertexNormals(); //计算顶点的法线 }; /************************************************* ****下面是TubeGeometry对象的方法属性定义,继承自Geometry对象. **************************************************/ THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); /* ///FrenetFrames方法计算弗莱纳框架,得到路径的切线,法线,副法线. */ ///<summary>FrenetFrames</summary> ///<param name ="path" type="THREE.Path">弯管的路径</param> ///<param name ="segments" type="int">沿弯管路径上的细分线段数,默认为64</param> ///<param name ="closed" type="boolean">是否连接起始点,结束点,true为关闭.,默认为false</param> // For computing of Frenet frames, exposing the tangents, normals and binormals the spline // 计算弗莱纳框架,得到路径的切线,法线,副法线. THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) { var tangent = new THREE.Vector3(), normal = new THREE.Vector3(), binormal = new THREE.Vector3(), tangents = [], normals = [], binormals = [], vec = new THREE.Vector3(), mat = new THREE.Matrix4(), numpoints = segments + 1, theta, epsilon = 0.0001, smallest, tx, ty, tz, i, u, v; // expose internals this.tangents = tangents; this.normals = normals; this.binormals = binormals; // compute the tangent vectors for each segment on the path // 计算路径上的每条细分线段的切线向量 for ( i = 0; i < numpoints; i ++ ) { u = i / ( numpoints - 1 ); tangents[ i ] = path.getTangentAt( u ); tangents[ i ].normalize(); } initialNormal3(); /* function initialNormal1(lastBinormal) { // fixed start binormal. Has dangers of 0 vectors normals[ 0 ] = new THREE.Vector3(); binormals[ 0 ] = new THREE.Vector3(); if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 ); normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize(); binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); } function initialNormal2() { // This uses the Frenet-Serret formula for deriving binormal var t2 = path.getTangentAt( epsilon ); normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize(); binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] ); normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); } */ /* ///initialNormal3方法选择初始化法线向量垂直于第一条切线向量和最小切线方向的xyz分量. */ function initialNormal3() { // select an initial normal vector perpenicular to the first tangent vector, // and in the direction of the smallest tangent xyz component // 选择初始化法线向量垂直于第一条切线向量和最小切线方向的xyz分量 normals[ 0 ] = new THREE.Vector3(); binormals[ 0 ] = new THREE.Vector3(); smallest = Number.MAX_VALUE; tx = Math.abs( tangents[ 0 ].x ); ty = Math.abs( tangents[ 0 ].y ); tz = Math.abs( tangents[ 0 ].z ); if ( tx <= smallest ) { smallest = tx; normal.set( 1, 0, 0 ); } if ( ty <= smallest ) { smallest = ty; normal.set( 0, 1, 0 ); } if ( tz <= smallest ) { normal.set( 0, 0, 1 ); } vec.crossVectors( tangents[ 0 ], normal ).normalize(); normals[ 0 ].crossVectors( tangents[ 0 ], vec ); binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); } // compute the slowly-varying normal and binormal vectors for each segment on the path // 为路径上的每条线段计算缓慢变换的法线和副法线. for ( i = 1; i < numpoints; i ++ ) { normals[ i ] = normals[ i-1 ].clone(); binormals[ i ] = binormals[ i-1 ].clone(); vec.crossVectors( tangents[ i-1 ], tangents[ i ] ); if ( vec.length() > epsilon ) { vec.normalize(); theta = Math.acos( THREE.Math.clamp( tangents[ i-1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); } binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same // 如果曲线路经是闭合的,处理顶点数组的最后一个顶点为第一个顶点. if ( closed ) { theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints-1 ] ), - 1, 1 ) ); theta /= ( numpoints - 1 ); if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) { theta = - theta; } for ( i = 1; i < numpoints; i ++ ) { // twist a little... normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } } };
商域无疆 (http://blog.****.net/omni360/)
本文遵循“署名-非商业用途-保持一致”创作公用协议
转载请保留此句:商域无疆 - 本博客专注于 敏捷开发及移动和物联设备研究:数据可视化、GOLANG、Html5、WEBGL、THREE.JS,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。
以下代码是THREE.JS 源码文件中extras/geometries/TubeGeometry.js文件的注释.
更多更新在 : https://github.com/omni360/three.js.sourcecode