TorusKnotGeometry.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /**
  2. * @author oosmoxiecode
  3. * @author Mugen87 / https://github.com/Mugen87
  4. *
  5. * based on http://www.blackpawn.com/texts/pqtorus/
  6. */
  7. import { Geometry } from '../core/Geometry';
  8. import { BufferGeometry } from '../core/BufferGeometry';
  9. import { Float32BufferAttribute } from '../core/BufferAttribute';
  10. import { Vector3 } from '../math/Vector3';
  11. // TorusKnotGeometry
  12. function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) {
  13. Geometry.call( this );
  14. this.type = 'TorusKnotGeometry';
  15. this.parameters = {
  16. radius: radius,
  17. tube: tube,
  18. tubularSegments: tubularSegments,
  19. radialSegments: radialSegments,
  20. p: p,
  21. q: q
  22. };
  23. if ( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' );
  24. this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) );
  25. this.mergeVertices();
  26. }
  27. TorusKnotGeometry.prototype = Object.create( Geometry.prototype );
  28. TorusKnotGeometry.prototype.constructor = TorusKnotGeometry;
  29. // TorusKnotBufferGeometry
  30. function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) {
  31. BufferGeometry.call( this );
  32. this.type = 'TorusKnotBufferGeometry';
  33. this.parameters = {
  34. radius: radius,
  35. tube: tube,
  36. tubularSegments: tubularSegments,
  37. radialSegments: radialSegments,
  38. p: p,
  39. q: q
  40. };
  41. radius = radius || 100;
  42. tube = tube || 40;
  43. tubularSegments = Math.floor( tubularSegments ) || 64;
  44. radialSegments = Math.floor( radialSegments ) || 8;
  45. p = p || 2;
  46. q = q || 3;
  47. // buffers
  48. var indices = [];
  49. var vertices = [];
  50. var normals = [];
  51. var uvs = [];
  52. // helper variables
  53. var i, j;
  54. var vertex = new Vector3();
  55. var normal = new Vector3();
  56. var P1 = new Vector3();
  57. var P2 = new Vector3();
  58. var B = new Vector3();
  59. var T = new Vector3();
  60. var N = new Vector3();
  61. // generate vertices, normals and uvs
  62. for ( i = 0; i <= tubularSegments; ++ i ) {
  63. // the radian "u" is used to calculate the position on the torus curve of the current tubular segement
  64. var u = i / tubularSegments * p * Math.PI * 2;
  65. // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead.
  66. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions
  67. calculatePositionOnCurve( u, p, q, radius, P1 );
  68. calculatePositionOnCurve( u + 0.01, p, q, radius, P2 );
  69. // calculate orthonormal basis
  70. T.subVectors( P2, P1 );
  71. N.addVectors( P2, P1 );
  72. B.crossVectors( T, N );
  73. N.crossVectors( B, T );
  74. // normalize B, N. T can be ignored, we don't use it
  75. B.normalize();
  76. N.normalize();
  77. for ( j = 0; j <= radialSegments; ++ j ) {
  78. // now calculate the vertices. they are nothing more than an extrusion of the torus curve.
  79. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value.
  80. var v = j / radialSegments * Math.PI * 2;
  81. var cx = - tube * Math.cos( v );
  82. var cy = tube * Math.sin( v );
  83. // now calculate the final vertex position.
  84. // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve
  85. vertex.x = P1.x + ( cx * N.x + cy * B.x );
  86. vertex.y = P1.y + ( cx * N.y + cy * B.y );
  87. vertex.z = P1.z + ( cx * N.z + cy * B.z );
  88. vertices.push( vertex.x, vertex.y, vertex.z );
  89. // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal)
  90. normal.subVectors( vertex, P1 ).normalize();
  91. normals.push( normal.x, normal.y, normal.z );
  92. // uv
  93. uvs.push( i / tubularSegments );
  94. uvs.push( j / radialSegments );
  95. }
  96. }
  97. // generate indices
  98. for ( j = 1; j <= tubularSegments; j ++ ) {
  99. for ( i = 1; i <= radialSegments; i ++ ) {
  100. // indices
  101. var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );
  102. var b = ( radialSegments + 1 ) * j + ( i - 1 );
  103. var c = ( radialSegments + 1 ) * j + i;
  104. var d = ( radialSegments + 1 ) * ( j - 1 ) + i;
  105. // faces
  106. indices.push( a, b, d );
  107. indices.push( b, c, d );
  108. }
  109. }
  110. // build geometry
  111. this.setIndex( indices );
  112. this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
  113. this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
  114. this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  115. // this function calculates the current position on the torus curve
  116. function calculatePositionOnCurve( u, p, q, radius, position ) {
  117. var cu = Math.cos( u );
  118. var su = Math.sin( u );
  119. var quOverP = q / p * u;
  120. var cs = Math.cos( quOverP );
  121. position.x = radius * ( 2 + cs ) * 0.5 * cu;
  122. position.y = radius * ( 2 + cs ) * su * 0.5;
  123. position.z = radius * Math.sin( quOverP ) * 0.5;
  124. }
  125. }
  126. TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
  127. TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry;
  128. export { TorusKnotGeometry, TorusKnotBufferGeometry };