using System; using System.Diagnostics; using OpenTK; namespace Examples.Shapes { public sealed class TorusKnot: DrawableShape { #region Constants // hard minimums to make sure the created Torusknot is 3D private const int MINShapeVertices = 3; private const int MINPathSteps = 32; private const double TwoPi = ( 2.0 * System.Math.PI ); #endregion Constants public TorusKnot( int pathsteps, int shapevertices, double radius, int p, int q, int TexCount, bool useDL ) : base( useDL ) { Trace.Assert( pathsteps >= MINPathSteps, "A Path must have at least " + MINPathSteps + " Steps to form a volume." ); Trace.Assert( shapevertices >= MINShapeVertices, "A Shape must contain at least " + MINShapeVertices + " Vertices to be considered valid and create a volume." ); Trace.Assert( TexCount >= 1, "at least 1 Texture set is required." ); PrimitiveMode = OpenTK.Graphics.OpenGL.PrimitiveType.TriangleStrip; Vector3d[] PathPositions = new Vector3d[pathsteps]; #region Find the center Points for each step on the path for ( int i = 0; i < pathsteps; i++ ) { double Angle = ( i / (double)pathsteps ) * TwoPi; double AngleTimesP = Angle * p; double AngleTimesQ = Angle * q; double r = ( 0.5 * ( 2.0 + System.Math.Sin( AngleTimesQ ) ) ); PathPositions[i] = new Vector3d( ( r * System.Math.Cos( AngleTimesP ) ), ( r * System.Math.Cos( AngleTimesQ ) ), ( r * System.Math.Sin( AngleTimesP ) ) ); } #endregion Find the center Points for each step on the path #region Find the Torus length Vector3d result; double[] Lengths = new double[pathsteps]; Vector3d.Subtract( ref PathPositions[pathsteps - 1], ref PathPositions[0], out result ); Lengths[0] = result.Length; double TotalLength = result.Length; for ( int i = 1; i < pathsteps; i++ ) // skipping { Vector3d.Subtract( ref PathPositions[i - 1], ref PathPositions[i], out result ); Lengths[i] = result.Length; TotalLength += result.Length; } Trace.WriteLine( "the TorusKnot's length is: " + TotalLength + " " ); #endregion Find the Torus length VertexArray = new VertexT2dN3dV3d[pathsteps * shapevertices]; #region Loft a circle Shape along the path double TwoPiThroughVert = TwoPi / shapevertices; // precalc for reuse for ( uint i = 0; i < pathsteps; i++ ) { Vector3d last, next, normal, tangent; if ( i == pathsteps - 1 ) next = PathPositions[0]; else next = PathPositions[i + 1]; if ( i == 0 ) last = PathPositions[pathsteps - 1]; else last = PathPositions[i - 1]; Vector3d.Subtract( ref next, ref last, out tangent ); // Guesstimate tangent tangent.Normalize(); Vector3d.Add( ref next, ref last, out normal ); // Approximate N normal.Normalize(); Vector3d.Multiply( ref normal, radius, out normal );// scale the shape to desired radius for ( uint j = 0; j < shapevertices; j++ ) { uint index = i * (uint)shapevertices + j; // Create a point on the plane and rotate it Matrix4d RotationMatrix = Matrix4d.Rotate( tangent, -( j * TwoPiThroughVert ) ); Vector3d point = Vector3d.TransformVector( normal, RotationMatrix ); Vector3d.Add( ref PathPositions[i], ref point, out VertexArray[index].Position ); // Since the used shape is a circle, the Vertex normal's heading is easy to find Vector3d.Subtract( ref VertexArray[index].Position, ref PathPositions[i], out VertexArray[index].Normal ); VertexArray[index].Normal.Normalize(); // just generate some semi-useful UVs to fill blanks VertexArray[index].TexCoord = new Vector2d( (double)( i / TotalLength/ TexCount ), j / ( shapevertices - 1.0 ) ); } } #endregion Loft a circle Shape along the path PathPositions = null; // not needed anymore uint currentindex = 0; #region Build a Triangle strip from the Vertices IndexArray = new uint[pathsteps * ( shapevertices * 2 + 2 )]; // 2 triangles per vertex, +2 due to added degenerate triangles for ( uint i = 0; i < pathsteps; i++ ) { uint RowCurrent = i * (uint)shapevertices; uint RowBelow; if ( i == pathsteps - 1 ) RowBelow = 0; // for the last row, the first row is the following else RowBelow = ( i + 1 ) * (uint)shapevertices; // new ring begins here for ( uint j = 0; j < shapevertices; j++ ) { IndexArray[currentindex++] = RowCurrent + j; IndexArray[currentindex++] = RowBelow + j; } // ring ends here, repeat first 2 vertices to insert 2 degenerate triangles to reach following ring IndexArray[currentindex++] = RowCurrent; IndexArray[currentindex++] = RowBelow; } #endregion Build a Triangle strip from the Vertices } } }