<> <> << Dobkin August 8, 1986 2:12:03 pm PDT>> <> <> <> <<>> <> <<>> <> <<>> <> << 2. The code should allow it's output to be generated to either the current format file or a file that fits into Frank Crow's ThreeDWorld. This is straightforward. In Retrow, when points are generated, they need to be numbered. Then, PutTriangle would be modified to print out triples of integers rather than triples of triples of floating point numbers. This would cause the inner loop of RowofTriangles to Change it's form to something which PutTriangles by vertex indices. It is actually the case that PR[J] is expressible as (I-1)*M + J and TR[J] is expressible as I*M + J for suitable index variable I.>> << 3. FInally, there is a problem in closing the knots. Having taken a sample and the slope of the curve at that point, a plane perpendicular to the slope vector is given x and y axes (Fx and Fy in the code) which are used to generate the M-gonal cross-section. In all planes after the first, the x and y axes are selected to line up with those in the preceeding one. However, this cannot occur when matching together the last and first cross-sections. Thus, when these two M-gons are triangulated together, a twisted ruled cylinder can result. This needs to be fixed by introducing another subroutine to do this patching. To see this effect, view the default knot at the default parameters of hidesweep with a 135 degree rotation about the y-axis. >> <> <<>> <<>> DIRECTORY FS USING [StreamOpen], IO USING [Close, PutF, real, STREAM], Real USING [Float,Fix], RealFns USING [SinDeg, CosDeg,SqRt], Random USING [RandomStream,Create,NextInt], Rope USING [ROPE]; KnotPicture: CEDAR PROGRAM IMPORTS FS, IO, Real, RealFns, Random = BEGIN OPEN FS, IO, Real, RealFns, Random, Rope; KnotDescriptorRec: TYPE = RECORD [ N : INTEGER _ 600, -- Number of samples taken on the curve M : INTEGER _ 24, -- Number of samples taken at each cross-section Radius : REAL _ .4, -- Radius of tube passed along the curve (or M-gon at cross-sections). A1 : INTEGER _ 4, -- Number of times the knot goes around the center of the torus A2 : INTEGER _ 7 , -- Number of times the knot goes around the circumference of the torus A3 : REAL _ 2.5 , -- Central radius of the tours (average of inner and outer radii) FileOutName: Rope.ROPE _ "trefoil.1400.txt", -- Name of file where triangles are to be written FileAppend: BOOLEAN _ FALSE, -- Flag tells whether to append to file or create it. Partial: BOOLEAN _ FALSE, -- This allows only part of the knot to be generated StartAt: BOOLEAN _ TRUE, -- set this flag if initial cross-sections will NOT be generated StartAtPoint: INTEGER _ 2, -- First cross-section to be generated StopAt: BOOLEAN _ TRUE, -- set this flag if final cross-sections will NOT be generated StopAtPoint: INTEGER _ 4 -- Last cross-section to be generated ]; KnotDescriptor: TYPE = REF KnotDescriptorRec; KnotCreate: PROC [ KD : KnotDescriptor ] ~ { Debug: BOOLEAN _ FALSE; FileDebugOut: Rope.ROPE _ "debug.txt"; EPSILON : REAL _ .0001; TWOPI : REAL _ 6.283185 ; rs : RandomStream _ Create[range:1000000]; Point3: TYPE = ARRAY [1..3] OF REAL; RowofPoint3s: TYPE = ARRAY [0..100] OF Point3; FirstRow, PrevRow, ThisRow: RowofPoint3s; MySinCos: PROC [angle, period : INTEGER] RETURNS [s,c: REAL] ~ { a: REAL _ Real.Float[angle]/Real.Float[period]; b: INTEGER _ Real.Fix[a]; ang:REAL _ 360 * (a - Real.Float[b]); s _ RealFns.SinDeg[ang]; c _ RealFns.CosDeg[ang]; }; GetVals : PROC [i : INTEGER] RETURNS [v,d : Point3] ~ { -- get values of a point on the curve and the derivative there SinTheta1, CosTheta1, SinTheta2, CosTheta2, Deriv1, Deriv2 : REAL; [SinTheta1 , CosTheta1 ] _ MySinCos[KD.A1*i,KD.N]; [SinTheta2 , CosTheta2 ] _ MySinCos[KD.A2*i,KD.N]; Deriv1 _ Real.Float[KD.A1]*TWOPI/Real.Float[KD.N]; Deriv2 _ Real.Float[KD.A2]*TWOPI/Real.Float[KD.N]; v[1] _ SinTheta1 * (KD.A3 + CosTheta2); v[2] _ CosTheta1 * (KD.A3 + CosTheta2); v[3] _ SinTheta2; d[1] _ Deriv1*CosTheta1*(KD.A3 + CosTheta2) - SinTheta1*Deriv2*SinTheta2; d[2] _ -(Deriv1*SinTheta1*(KD.A3 + CosTheta2) + CosTheta1*Deriv2*SinTheta2); d[3] _ Deriv2*CosTheta2; }; GetFirstFrame : PROC [Dir : Point3] RETURNS [Xd, Yd : Point3] ~ { -- find axes in first frames, other axes derive from this one. Dir _ Normalize[Dir]; IF (ABS[Dir[3]] > (1.0 - EPSILON)) THEN Xd _ ZCross[Dir] ELSE Xd _ YCross[Dir]; Xd _ Normalize[Xd]; Yd _ Cross[Dir,Xd]; Yd _ Normalize[Yd]; }; GetFrame : PROC [Dir, Pos, OldPos, OldXd : Point3] RETURNS [Xd, Yd : Point3] ~ { -- in all succeeding frames, axes are aligned to those in the first frame. DelFc : Point3 _ Subtract[Pos , OldPos] ; Xd _ Subtract[ OldXd , Scalar[Dot[Dir,OldFx]/Dot[Dir,DelFc] , DelFc] ]; Xd _ Normalize[Xd]; Yd _ Cross[Dir,Xd]; Yd _ Normalize[Yd]; }; MakeRow: PROC [Center, Xd, Yd : Point3] RETURNS [ Retrow : RowofPoint3s] ~ { -- creates a row of triangles from two M-gonal cross-sections. CosJ, SinJ : REAL; FOR J: INTEGER IN [0..KD.M-1] DO [SinJ, CosJ] _ MySinCos[J, KD.M]; Retrow[J] _ Add[Center, Add[ Scalar[ KD.Radius*CosJ, Xd],Scalar[KD.Radius*SinJ, Yd] ] ]; <> --At this point, vertices should be output if the ThreeDWorldformat is being used for output. ENDLOOP; }; CopyRow: PROC [OldRow : RowofPoint3s] RETURNS [NewRow : RowofPoint3s] ~ { FOR J: INTEGER IN [0..KD.M-1] DO NewRow[J] _ OldRow[J]; ENDLOOP; }; PutTriangle: PROC [p,q,r: Point3,lastone:BOOLEAN] ~ { File.PutF["%g %g %g,", IO.real[p[1]], IO.real[p[2]], IO.real[p[3]]]; File.PutF[" %g %g %g,", IO.real[q[1]], IO.real[q[2]], IO.real[q[3]]]; File.PutF[" %g %g %g/", IO.real[r[1]], IO.real[r[2]], IO.real[r[3]]]; File.PutF[" %g %g %g", IO.real[Real.Float[Random.NextInt[rs]]/1000000.], IO.real[Real.Float[Random.NextInt[rs]]/1000000.], IO.real[Real.Float[Random.NextInt[rs]]/1000000.]]; IF (NOT lastone ) THEN File.PutF[" "]; }; PutThisRow: PROC [ Center: Point3 , Currow : RowofPoint3s] ~ { FileDebug.PutF[" These are the J's for Center of (%g , %g , %g)", IO.real[Center[1]],IO.real[Center[2]],IO.real[Center[3]]]; FileDebug.PutF[" "]; FOR J : INTEGER IN [0..KD.M-1] DO FileDebug.PutF[" ( %g , %g , %g )" ,IO.real[Currow[J][1]],IO.real[Currow[J][2]],IO.real[Currow[J][3]]]; FileDebug.PutF[" "]; ENDLOOP; }; RowofTriangles: PROC [ PR, TR : RowofPoint3s, lastone: BOOLEAN] ~ { FOR JJ:INTEGER IN [0..KD.M-2] DO PutTriangle[PR[JJ],PR[JJ+1],TR[JJ+1],FALSE]; PutTriangle[PR[JJ],TR[JJ],TR[JJ+1],FALSE]; ENDLOOP; PutTriangle[PR[KD.M-1],PR[0],TR[0],FALSE]; PutTriangle[PR[KD.M-1],TR[KD.M-1],TR[0],lastone]; }; ZCross: PROC [p: Point3] RETURNS [r: Point3] ~ {r[1] _ p[2]; r[2] _ -p[1];}; YCross: PROC [p: Point3] RETURNS [r: Point3] ~ {r[3] _ p[1]; r[1] _ -p[3];}; Scalar: PROC [s:REAL, p: Point3] RETURNS [q: Point3] ~ { FOR i: CARDINAL IN [1..3] DO q[i] _ s * p[i] ; ENDLOOP; }; Dot: PROC [p, q: Point3] RETURNS [s: REAL _ 0.0] ~ { FOR i: CARDINAL IN [1..3] DO s _ s + p[i] * q[i]; ENDLOOP; }; Cross: PROC [p, q: Point3] RETURNS [r: Point3] ~ { FOR i: CARDINAL IN [1..3] DO n: CARDINAL _ (i MOD 3) + 1; nn: CARDINAL _ (n MOD 3) + 1; r[i] _ p[n]*q[nn] - p[nn]*q[n]; ENDLOOP; }; Normalize: PROC [p : Point3] RETURNS [q: Point3] ~ {q _ Scalar[(1.0/RealFns.SqRt[Dot[p,p]]), p]; }; Add: PROC [p, q: Point3] RETURNS [r: Point3] ~ { FOR i: CARDINAL IN [1..3] DO r[i] _ p[i] + q[i]; ENDLOOP; }; Subtract: PROC [p, q: Point3] RETURNS [r: Point3] ~ { FOR i: CARDINAL IN [1..3] DO r[i] _ p[i] - q[i]; ENDLOOP; }; Fp,Fc,Fx,Fy, OldFx, OldFc, FirstFc : Point3; ulimit,llimit: INTEGER; File: IO.STREAM ; FileDebug:IO.STREAM ; IF (KD.FileAppend) THEN File _ FS.StreamOpen[KD.FileOutName, $append] ELSE File _ FS.StreamOpen[KD.FileOutName, $create]; IF (Debug) THEN FileDebug _ FS.StreamOpen[FileDebugOut, $create]; IF (KD.Partial AND KD.StartAt) THEN llimit _ KD.StartAtPoint ELSE llimit _ 0; [Fc , Fp ] _ GetVals[llimit]; [Fx , Fy ] _ GetFirstFrame[Fp]; FirstRow _ MakeRow[Fc,Fx,Fy]; IF (Debug) THEN PutThisRow[Fc,FirstRow]; PrevRow _ CopyRow[FirstRow]; OldFx _ Fx; FirstFc _ OldFc _ Fc; IF (KD.Partial AND KD.StopAt) THEN ulimit _ KD.StopAtPoint ELSE ulimit _ KD.N-1; FOR I : INTEGER IN [llimit+1..ulimit-1] DO [Fc , Fp ] _ GetVals[I]; [Fx , Fy ] _ GetFrame[Fp, Fc, OldFc, OldFx]; ThisRow _ MakeRow[Fc,Fx,Fy]; RowofTriangles[PrevRow, ThisRow,FALSE]; PrevRow _ CopyRow[ThisRow]; IF (Debug) THEN PutThisRow[Fc,ThisRow]; OldFx _ Fx; OldFc _ Fc; ENDLOOP; [Fc, Fp] _ GetVals[ulimit]; [Fx, Fy] _ GetFrame[Fp, Fc, OldFc, OldFx]; ThisRow _ MakeRow[Fc,Fx,Fy]; RowofTriangles[PrevRow, ThisRow,(KD.Partial AND (KD.StartAt OR KD.StopAt))]; IF (Debug) THEN PutThisRow[Fc,ThisRow]; IF (NOT (KD.Partial AND (KD.StopAt OR KD.StartAt))) THEN RowofTriangles[ThisRow,FirstRow,TRUE]; File.Close[]; IF (Debug) THEN FileDebug.Close[]; }; Kd : KnotDescriptor _ NEW[KnotDescriptorRec]; Kd.FileOutName _ "big.txt"; KnotCreate[ Kd ] ; END.