///////////////////////////////////////////////// ////////// ////////// Cheap_Sweep macro ////////// creates a csg which resembles a sphere_sweep ////////// ///////////////////////////////////////////////// // This macro was written for readability, NOT speed, so // you may want to optimise it for your own personal needs // There is an example scene commented out at the bottom // of this file. // I hope someone finds this useful. -Shay // CONTROL_POINTS [array] 1 dimensional array of control points // RADIUS [float] radius of sweep // FILE_NAME [string] file name into which Sweep_Data macro will be written // this string requires a file name complete with extension ex: "Test_Out.inc" // CLOSE [bool] closes the shape for you. Do not close the shape yourself. // a square(ish) shape for example would only require an input of four control points // My reason for closing the shape within the macro is that I suspect many will want // to input points on a declared spline. This presents a problem because of the necessity // of a completely closed declared spline and the possible presence of floating point errors #macro Cheap_Sweep (CONTROL_POINTS, RADIUS, FILE_NAME, CLOSE) // Define constants #local Number_Of_Points = dimension_size (CONTROL_POINTS, 1); // number of control points // end Define constants // Create an include file #fopen Sweep_Out FILE_NAME write #write (Sweep_Out, "#macro Sweep_Data ()\n") // end Create an include file // Add necessary iterations to loop if CLOSE is true #if (CLOSE) #local Points_Used = Number_Of_Points + 2; #else // #if (CLOSE) #local Points_Used = Number_Of_Points; #end // #if (CLOSE) // close Add necessary iterations to loop if CLOSE is true // Start loop for forming segments #local C = 1; // counter #while (C < Points_Used - 1) // first and last points are only used for calculation // end Start loop for forming segments // Find array indices for segment #local I = C; // index is equal to counter if counter is within range of array #local Prev_I = I - 1; // previous control point if I - 1 is within range of array #if (I = Number_Of_Points) // will never happen unless CLOSE is true #local I = 0; // center control point is equal to first control point #local Prev_I = Number_Of_Points - 1; // previous control point is last control point #end // #if (I = Number_Of_Points) #local Next_I = I + 1; // next control point if I + 1 is within range of array #if (I = Number_Of_Points - 1) // will never happen unless CLOSE is true #local Next_I = 0; // next control point is equal to first control point #end // #if (I = Number_Of_Points - 1) // end Find array indices for segment // Vertex variables #local Center_Vx = CONTROL_POINTS[I]; // named only for clarity #local Prev_Vx = (CONTROL_POINTS[Prev_I] + Center_Vx)/2; // end of segment is mid between previous and center vertex #local Next_Vx = (CONTROL_POINTS[Next_I] + Center_Vx)/2; // end of segment is mid between next and center vertex // end Vertex variables // Vector variables #local Prev_Vr = vnormalize (Prev_Vx - Center_Vx); #local Next_Vr = vnormalize (Next_Vx - Center_Vx); #local Average_Vr = vnormalize((Prev_Vr + Next_Vr)/2); // used to place torus center // end Vector variables // Skip segment and insert cylinder if angle between vectors is 0 or pi #local Vector_Angle = acos(min(1, vdot(Prev_Vr, -Next_Vr))); // angle spanned by torus #if (Vector_Angle = 0 | Vector_Angle = pi) #if (vlength (Prev_Vx - Next_Vx) != 0) // check that previous and next are not equal cylinder {Prev_Vx, Next_Vx, RADIUS} // replace segment with cylinder and skip to end of loop #write (Sweep_Out, " cylinder {", Prev_Vx, ",", Next_Vx, ",", RADIUS, "}\n") #end // #if (vlength (Prev_Vx - Next_Vx) != 0) #else // #if (Vector_Angle = 0 | Vector_Angle = pi) // continue with loop // end Skip segment and insert cylinder if angle between vectors is 0 or pi // Length variables #local Prev_L = vlength (Prev_Vx - Center_Vx); #local Next_L = vlength (Next_Vx - Center_Vx); // end Length variables // Rename closest vertex to Center_Vx as Start // and find End along opposite vector the same distance // from Center_Vx #if (Prev_L < Next_L) #local Start = Prev_Vx; // start of torus segment #local Start_L = Prev_L; #local End = Center_Vx + Next_Vr * Start_L; // end of torus segment and end of cylinder #local Cylinder_Start = Next_Vx; // other end of cylinder #else // #if (Prev_L < Next_L) #local Start = Next_Vx; // start of torus segment #local Start_L = Next_L; #local End = Center_Vx + Prev_Vr * Start_L; // end of torus segment and end of cylinder #local Cylinder_Start = Prev_Vx; // other end of cylinder #end // #if (Prev_L < Next_L) // end Rename closest vertex to Center_Vx as Start // Add cylinder between Cylinder_Start and End if necessary #if (vlength (Cylinder_Start - End) != 0) // checks is gap exists for cylinder cylinder {Cylinder_Start, End, RADIUS} #write (Sweep_Out, " cylinder {", Cylinder_Start, ",", End, "," RADIUS, "}\n") #end // #if (Cylinder_Start != End) // end Add cylinder between Cylinder_Start and End if necessary // Calculate torus properties #local Torus_Span = vlength (End - Start); // distance spanned by torus segment #local Torus_Radius = (sin((pi - Vector_Angle)/2) * Torus_Span) / sin (Vector_Angle); // major radius (Law of Sines) #local Torus_Normal = vnormalize(vcross(Prev_Vr, Next_Vr)); // normal of plane on which torus will lie #local Transform_Length = sin(pi/2-Vector_Angle/2)*Torus_Radius; // to find center #local Torus_Center = (Start - End)/2 + End + Average_Vr * Transform_Length; // center of torus // end Calculate torus properties // Create torus torus {Torus_Radius, RADIUS #write (Sweep_Out, " torus {", Torus_Radius, "," RADIUS) // end Create torus // Transform torus #local New_Y = Torus_Normal; // Make a plane out of Torus_Normal and one other vector, then use vcross to find a vector // perpendicular to both #local New_X = vnormalize(vcross(New_Y, vrotate(New_Y, <90, 90, 0>))); // use vcross to find a third vector perpendicular to both of the first two #local New_Z = (vnormalize(vcross(New_Y, New_X))); // use a matrix to redefine the x, y, and z vectors and translate the torus to Torus_Center matrix // translate values #write (Sweep_Out, " matrix <", New_X.x, ",", New_X.y, ",", New_X.z, ",", New_Y.x, ",", New_Y.y, ",", New_Y.z, ",", New_Z.x, ",", New_Z.y, ",", New_Z.z, ",", Torus_Center.x, ",", Torus_Center.y, ",", Torus_Center.z">\n") // clip with planes perpendicular to segment vectors clipped_by {plane {-(Center_Vx - Start), 0 translate Start}} // clip at Start #write (Sweep_Out, " clipped_by {plane {", -(Center_Vx - Start), ", 0 ", "matrix <1,0,0,0,1,0,0,0,1," Start.x, ",", Start.y, ",", Start.z, ">}}\n") clipped_by {plane {-(Center_Vx - End), 0 translate End}} // clip at End #write (Sweep_Out, " clipped_by {plane {", -(Center_Vx - End), ", 0 ", "matrix <1,0,0,0,1,0,0,0,1," End.x, ",", End.y, ",", End.z, ">}}}\n") } // end torus // end Transform torus // Skip to here if angle between vectors is o or 180 #end // #if (Vector_Angle = 0 | Vector_Angle = 180) // end Skip to here if angle between vectors is o or 180 // End loop for forming segments #local C = C + 1; #end // #while (C < Points_Used - 1) // end End loop for forming segments // Close and include the *.inc file #write (Sweep_Out, "#end // #macro Sweep_Data ()") #fclose Sweep_Out #include FILE_NAME // end Close and include the *.inc file #end // #macro Cheap_Sweep (CONTROL_POINTS, RADIUS, FILE_NAME, CLOSE) /* // Example scene camera {location <0,1,-10> look_at <0,0,0>} light_source {<25,25,-25> color rgb .7} background {rgb 1} global_settings {assumed_gamma 1} #local Trefoil_x = function(F) {cos(F)-2*cos(2*F)}; #local Trefoil_y = function(F) {sin(F)+2*sin(2*F)}; #local Trefoil_z = function(F) {sin(3*F)}; //Sin[3t],Sin[t]+2Sin[2t],Cos[t]-2Cos[2t]} #local Macro_Array = array[20] #local C = 0; #while (C<20) #local Macro_Array[C] = ; #local C = C+1; #end // First Render union {Cheap_Sweep (Macro_Array, .7, "Test_Out.inc", yes) pigment {rgb .5} finish { reflection {.7 metallic}}} // Subsequent Renders //#include "Test_Out.inc" //union {Sweep_Data () pigment {rgb .5} finish { reflection {.7 metallic}}} */