diff --git a/docs/api/extras/geometries/TorusKnotBufferGeometry.html b/docs/api/extras/geometries/TorusKnotBufferGeometry.html
new file mode 100644
index 0000000000000000000000000000000000000000..292536bcd41fcaab8c2c94261a478d3bf28d566f
--- /dev/null
+++ b/docs/api/extras/geometries/TorusKnotBufferGeometry.html
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+ [page:BufferGeometry] →
+
+ [name]
+
+ This is the [page:BufferGeometry] port of [page:TorusKnotGeometry].
+
+
+
+
+ Example
+
+ var geometry = new THREE.TorusKnotBufferGeometry( 10, 3, 100, 16 );
+ var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
+ var torusKnot = new THREE.Mesh( geometry, material );
+ scene.add( torusKnot );
+
+
+
+ Constructor
+
+
+ [name]([page:Float radius], [page:Float tube], [page:Integer radialSegments], [page:Integer tubularSegments], [page:Integer p], [page:Integer q], [page:Float heightScale], [page:Float arc])
+
+
+ radius — Default is 100.
+ tube — Diameter of the tube. Default is 40.
+ radialSegments — Default is 64.
+ tubularSegments — Default is 8.
+ p — This value determines, how many times the geometry winds around its axis of rotational symmetry. Default is 2.
+ q — This value determines, how many times the geometry winds around a circle in the interior of the torus. Default is 3.
+ heightScale — Default is 1.
+ arc — Central angle. Default is Math.PI * 2.
+
+
+
+
+ Properties
+
+
+
+ Each of the contructor parameters is accessible as a property of the same name. Any modification of these properties after instantiation does not change the geometry.
+
+
+
+ Source
+
+ [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+
+
diff --git a/docs/api/extras/geometries/TorusKnotGeometry.html b/docs/api/extras/geometries/TorusKnotGeometry.html
index 82cade9500646d6e39af170e0ace36a40642077f..be2878328aa9f8d863192a4c2a613f770fcd9f88 100644
--- a/docs/api/extras/geometries/TorusKnotGeometry.html
+++ b/docs/api/extras/geometries/TorusKnotGeometry.html
@@ -29,15 +29,18 @@
Constructor
- [name]([page:Float radius], [page:Float tube], [page:Integer radialSegments], [page:Integer tubularSegments], [page:Integer p], [page:Integer q], [page:Float heightScale])
+ [name]([page:Float radius], [page:Float tube], [page:Integer radialSegments], [page:Integer tubularSegments], [page:Integer p], [page:Integer q], [page:Float heightScale], [page:Float arc])
- radius — Default is 100.
- tube — Default is 40.
- radialSegments — Default is 64.
- tubularSegments — Default is 8.
- p — Default is 2.
- q — Default is 3.
- heightScale — Default is 1.
+
+ radius — Default is 100.
+ tube — Diameter of the tube. Default is 40.
+ radialSegments — Default is 64.
+ tubularSegments — Default is 8.
+ p — This value determines, how many times the geometry winds around its axis of rotational symmetry. Default is 2.
+ q — This value determines, how many times the geometry winds around a circle in the interior of the torus. Default is 3.
+ heightScale — Default is 1.
+ arc — Central angle. Default is Math.PI * 2.
+
diff --git a/docs/list.js b/docs/list.js
index 5cf09a0c5dde3f05f3fa30aba5adc975869fcb6b..c72eeb5b22c02ded6fd28d451d04a1d7e605dc41 100644
--- a/docs/list.js
+++ b/docs/list.js
@@ -215,6 +215,7 @@ var list = {
[ "TextGeometry", "api/extras/geometries/TextGeometry" ],
[ "TorusBufferGeometry", "api/extras/geometries/TorusBufferGeometry" ],
[ "TorusGeometry", "api/extras/geometries/TorusGeometry" ],
+ [ "TorusKnotBufferGeometry", "api/extras/geometries/TorusKnotBufferGeometry" ],
[ "TorusKnotGeometry", "api/extras/geometries/TorusKnotGeometry" ],
[ "TubeGeometry", "api/extras/geometries/TubeGeometry" ]
],
diff --git a/docs/scenes/js/geometry.js b/docs/scenes/js/geometry.js
index 68fe8e0c4186921aad17ea40f52e29d9c551c289..fd34e508db53a98d4e1e19bbb5060772a6f0d7ea 100644
--- a/docs/scenes/js/geometry.js
+++ b/docs/scenes/js/geometry.js
@@ -747,6 +747,45 @@ var guis = {
},
+ TorusKnotBufferGeometry : function( mesh ) {
+
+ var data = {
+ radius : 10,
+ tube : 3,
+ radialSegments : 64,
+ tubularSegments : 8,
+ p : 2,
+ q : 3,
+ heightScale : 1,
+ arc : twoPi
+ };
+
+ function generateGeometry() {
+
+ updateGroupGeometry( mesh,
+ new THREE.TorusKnotBufferGeometry(
+ data.radius, data.tube, data.radialSegments, data.tubularSegments,
+ data.p, data.q, data.heightScale, data.arc
+ )
+ );
+
+ }
+
+ var folder = gui.addFolder( 'THREE.TorusKnotBufferGeometry' );
+
+ folder.add( data, 'radius', 1, 20 ).onChange( generateGeometry );
+ folder.add( data, 'tube', 0.1, 10 ).onChange( generateGeometry );
+ folder.add( data, 'radialSegments', 3, 300 ).step( 1 ).onChange( generateGeometry );
+ folder.add( data, 'tubularSegments', 3, 20 ).step( 1 ).onChange( generateGeometry );
+ folder.add( data, 'p', 1, 20 ).step( 1 ).onChange( generateGeometry );
+ folder.add( data, 'q', 1, 20 ).step( 1 ).onChange( generateGeometry );
+ folder.add( data, 'heightScale', 1, 20 ).onChange( generateGeometry );
+ folder.add( data, 'arc', 0.1, twoPi ).onChange( generateGeometry );
+
+ generateGeometry();
+
+ },
+
TorusKnotGeometry : function( mesh ) {
var data = {
@@ -756,7 +795,8 @@ var guis = {
tubularSegments : 8,
p : 2,
q : 3,
- heightScale : 1
+ heightScale : 1,
+ arc : twoPi
};
function generateGeometry() {
@@ -764,13 +804,13 @@ var guis = {
updateGroupGeometry( mesh,
new THREE.TorusKnotGeometry(
data.radius, data.tube, data.radialSegments, data.tubularSegments,
- data.p, data.q, data.heightScale
+ data.p, data.q, data.heightScale, data.arc
)
);
}
- var folder = gui.addFolder( 'THREE.TorusGeometry' );
+ var folder = gui.addFolder( 'THREE.TorusKnotGeometry' );
folder.add( data, 'radius', 1, 20 ).onChange( generateGeometry );
folder.add( data, 'tube', 0.1, 10 ).onChange( generateGeometry );
@@ -779,6 +819,7 @@ var guis = {
folder.add( data, 'p', 1, 20 ).step( 1 ).onChange( generateGeometry );
folder.add( data, 'q', 1, 20 ).step( 1 ).onChange( generateGeometry );
folder.add( data, 'heightScale', 1, 20 ).onChange( generateGeometry );
+ folder.add( data, 'arc', 0.1, twoPi ).onChange( generateGeometry );
generateGeometry();
diff --git a/src/extras/geometries/TorusKnotBufferGeometry.js b/src/extras/geometries/TorusKnotBufferGeometry.js
new file mode 100644
index 0000000000000000000000000000000000000000..1becf230fd0800fc572b84b35bf6c4c3eecf844f
--- /dev/null
+++ b/src/extras/geometries/TorusKnotBufferGeometry.js
@@ -0,0 +1,168 @@
+/**
+ * @author Mugen87 / https://github.com/Mugen87
+ *
+ * see: http://www.blackpawn.com/texts/pqtorus/
+ */
+THREE.TorusKnotBufferGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale, arc ) {
+
+ THREE.BufferGeometry.call( this );
+
+ this.type = 'TorusKnotBufferGeometry';
+
+ this.parameters = {
+ radius: radius,
+ tube: tube,
+ radialSegments: radialSegments,
+ tubularSegments: tubularSegments,
+ p: p,
+ q: q,
+ heightScale: heightScale,
+ arc: arc
+ };
+
+ radius = radius || 100;
+ tube = tube || 40;
+ radialSegments = Math.floor( radialSegments ) || 64;
+ tubularSegments = Math.floor( tubularSegments ) || 6;
+ p = p || 2;
+ q = q || 3;
+ heightScale = heightScale || 1;
+ arc = arc || Math.PI * 2;
+
+ // used to calculate buffer length
+ var vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) );
+ var indexCount = radialSegments * tubularSegments * 2 * 3;
+
+ // buffers
+ var indices = new THREE.BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 );
+ var vertices = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );
+ var normals = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );
+ var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 );
+
+ // helper variables
+ var i, j, index = 0, indexOffset = 0;
+
+ var vertex = new THREE.Vector3();
+ var normal = new THREE.Vector3();
+ var uv = new THREE.Vector2();
+
+ var P1 = new THREE.Vector3();
+ var P2 = new THREE.Vector3();
+
+ var B = new THREE.Vector3();
+ var T = new THREE.Vector3();
+ var N = new THREE.Vector3();
+
+ // generate vertices, normals and uvs
+
+ for ( i = 0; i <= radialSegments; ++ i ) {
+
+ // the radian "u" is used to calculate the position on the torus curve of the current radial segement
+
+ var u = i / radialSegments * p * arc;
+
+ // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead.
+ // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions
+
+ calculatePositionOnCurve( u, p, q, radius, heightScale, P1 );
+ calculatePositionOnCurve( u + 0.01, p, q, radius, heightScale, P2 );
+
+ // calculate orthonormal basis
+
+ T.subVectors( P2, P1 );
+ N.addVectors( P2, P1 );
+ B.crossVectors( T, N );
+ N.crossVectors( B, T );
+
+ // normalize B, N. T can be ignored, we don't use it
+
+ B.normalize();
+ N.normalize();
+
+ for ( j = 0; j <= tubularSegments; ++ j ) {
+
+ // now calculate the vertices. they are nothing more than an extrusion of the torus curve.
+ // because we extrude a shape in the xy-plane, there is no need to calculate a z-value.
+
+ var v = j / tubularSegments * Math.PI * 2;
+ var cx = - tube * Math.cos( v );
+ var cy = tube * Math.sin( v );
+
+ // now calculate the final vertex position.
+ // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve
+
+ vertex.x = P1.x + ( cx * N.x + cy * B.x );
+ vertex.y = P1.y + ( cx * N.y + cy * B.y );
+ vertex.z = P1.z + ( cx * N.z + cy * B.z );
+
+ // vertex
+ vertices.setXYZ( index, vertex.x, vertex.y, vertex.z );
+
+ // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal)
+ normal.subVectors( vertex, P1 ).normalize();
+ normals.setXYZ( index, normal.x, normal.y, normal.z );
+
+ // uv
+ uv.x = i / radialSegments;
+ uv.y = j / tubularSegments;
+ uvs.setXY( index, uv.x, uv.y );
+
+ // increase index
+ index ++;
+
+ }
+
+ }
+
+ // generate indices
+
+ for ( j = 1; j <= radialSegments; j ++ ) {
+
+ for ( i = 1; i <= tubularSegments; i ++ ) {
+
+ // indices
+ var a = ( tubularSegments + 1 ) * ( j - 1 ) + ( i - 1 );
+ var b = ( tubularSegments + 1 ) * j + ( i - 1 );
+ var c = ( tubularSegments + 1 ) * j + i;
+ var d = ( tubularSegments + 1 ) * ( j - 1 ) + i;
+
+ // face one
+ indices.setX( indexOffset, a ); indexOffset++;
+ indices.setX( indexOffset, b ); indexOffset++;
+ indices.setX( indexOffset, d ); indexOffset++;
+
+ // face two
+ indices.setX( indexOffset, b ); indexOffset++;
+ indices.setX( indexOffset, c ); indexOffset++;
+ indices.setX( indexOffset, d ); indexOffset++;
+
+ }
+
+ }
+
+ // build geometry
+
+ this.setIndex( indices );
+ this.addAttribute( 'position', vertices );
+ this.addAttribute( 'normal', normals );
+ this.addAttribute( 'uv', uvs );
+
+ // this function calculates the current position on the torus curve
+
+ function calculatePositionOnCurve( u, p, q, radius, heightScale, position ) {
+
+ var cu = Math.cos( u );
+ var su = Math.sin( u );
+ var quOverP = q / p * u;
+ var cs = Math.cos( quOverP );
+
+ position.x = radius * ( 2 + cs ) * 0.5 * cu;
+ position.y = radius * ( 2 + cs ) * su * 0.5;
+ position.z = heightScale * radius * Math.sin( quOverP ) * 0.5;
+
+ }
+
+};
+
+THREE.TorusKnotBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.TorusKnotBufferGeometry.prototype.constructor = THREE.TorusKnotBufferGeometry;
diff --git a/src/extras/geometries/TorusKnotGeometry.js b/src/extras/geometries/TorusKnotGeometry.js
index b823733b9abbc83683efb93b5f414efa44ae772e..93c77118e088dd2ccd65aa9697d46b39068955c2 100644
--- a/src/extras/geometries/TorusKnotGeometry.js
+++ b/src/extras/geometries/TorusKnotGeometry.js
@@ -1,9 +1,8 @@
/**
* @author oosmoxiecode
- * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473
*/
-THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) {
+THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale, arc ) {
THREE.Geometry.call( this );
@@ -16,97 +15,12 @@ THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegmen
tubularSegments: tubularSegments,
p: p,
q: q,
- heightScale: heightScale
+ heightScale: heightScale,
+ arc: arc
};
- radius = radius || 100;
- tube = tube || 40;
- radialSegments = radialSegments || 64;
- tubularSegments = tubularSegments || 8;
- p = p || 2;
- q = q || 3;
- heightScale = heightScale || 1;
-
- var grid = new Array( radialSegments );
- var tang = new THREE.Vector3();
- var n = new THREE.Vector3();
- var bitan = new THREE.Vector3();
-
- for ( var i = 0; i < radialSegments; ++ i ) {
-
- grid[ i ] = new Array( tubularSegments );
- var u = i / radialSegments * 2 * p * Math.PI;
- var p1 = getPos( u, q, p, radius, heightScale );
- var p2 = getPos( u + 0.01, q, p, radius, heightScale );
- tang.subVectors( p2, p1 );
- n.addVectors( p2, p1 );
-
- bitan.crossVectors( tang, n );
- n.crossVectors( bitan, tang );
- bitan.normalize();
- n.normalize();
-
- for ( var j = 0; j < tubularSegments; ++ j ) {
-
- var v = j / tubularSegments * 2 * Math.PI;
- var cx = - tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
- var cy = tube * Math.sin( v );
-
- var pos = new THREE.Vector3();
- pos.x = p1.x + cx * n.x + cy * bitan.x;
- pos.y = p1.y + cx * n.y + cy * bitan.y;
- pos.z = p1.z + cx * n.z + cy * bitan.z;
-
- grid[ i ][ j ] = this.vertices.push( pos ) - 1;
-
- }
-
- }
-
- for ( var i = 0; i < radialSegments; ++ i ) {
-
- for ( var j = 0; j < tubularSegments; ++ j ) {
-
- var ip = ( i + 1 ) % radialSegments;
- var jp = ( j + 1 ) % tubularSegments;
-
- var a = grid[ i ][ j ];
- var b = grid[ ip ][ j ];
- var c = grid[ ip ][ jp ];
- var d = grid[ i ][ jp ];
-
- var uva = new THREE.Vector2( i / radialSegments, j / tubularSegments );
- var uvb = new THREE.Vector2( ( i + 1 ) / radialSegments, j / tubularSegments );
- var uvc = new THREE.Vector2( ( i + 1 ) / radialSegments, ( j + 1 ) / tubularSegments );
- var uvd = new THREE.Vector2( i / radialSegments, ( j + 1 ) / tubularSegments );
-
- 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();
-
- function getPos( u, in_q, in_p, radius, heightScale ) {
-
- var cu = Math.cos( u );
- var su = Math.sin( u );
- var quOverP = in_q / in_p * u;
- var cs = Math.cos( quOverP );
-
- var tx = radius * ( 2 + cs ) * 0.5 * cu;
- var ty = radius * ( 2 + cs ) * su * 0.5;
- var tz = heightScale * radius * Math.sin( quOverP ) * 0.5;
-
- return new THREE.Vector3( tx, ty, tz );
-
- }
+ this.fromBufferGeometry( new THREE.TorusKnotBufferGeometry( radius, tube, radialSegments, tubularSegments, p, q, heightScale, arc ) );
+ this.mergeVertices();
};
diff --git a/utils/build/includes/extras.json b/utils/build/includes/extras.json
index b072e8f576961f0bce3af78227270fbbf59f2ae4..30538042f4ebd7b228500e2e5969de3e56a0c482 100644
--- a/utils/build/includes/extras.json
+++ b/utils/build/includes/extras.json
@@ -38,6 +38,7 @@
"src/extras/geometries/TextGeometry.js",
"src/extras/geometries/TorusBufferGeometry.js",
"src/extras/geometries/TorusGeometry.js",
+ "src/extras/geometries/TorusKnotBufferGeometry.js",
"src/extras/geometries/TorusKnotGeometry.js",
"src/extras/geometries/TubeGeometry.js",
"src/extras/geometries/PolyhedronGeometry.js",