//Rick's Chain Maker
//Copyright 2008 Richard Swika
//http://home.comcast.net/~chainmaker
//email:chainmaker@comcast.net
//Revision: 1.3.1

//Link Properties; adjust these to vary the size and proportions of each link to suit your needs
//#declare LinkLength = 1.45;          //The length of a single link (outside diameter)
//#declare LinkRadius = 0.5;          //The outside radius of the link's curved section
//#declare WireRadius = 0.15;         //The radius of the wire used to make each ink

//Chain Frame Properties; adjust these to control the number of corners and number of links in the final chain frame
//#declare CornerCount = 4;           //How many corners to put in the frame; 3 or more please!!!
//#declare CornerLinkCount = 3;       //How many links to put on the corners of the frame (0 for pointy corners)
//#declare StraightLinkCountEven = 4; //How many links to put in the even straight sections of the frame
//#declare StraightLinkCountOdd  = 4; //How many links to put in the odd straight sections of the frame

//Special effects
//#declare InitialTwist = 0;          //The starting twist angle of the chain;
                                      //most useful at 90, 45 and -45 (all angles are in degrees)
//#declare TwistDelta   = 90;         //How much each link twists when added to the chain
                                      //most useful at 0 and 90
                                      //very interesting effects at 30, 45, 60 and others
//#declare Zoom = 1;                  //In rare cases you may want to change the zoom level
//#declare Exposure = 1;              //You can also play with the exposure level to bring out more detail
//#declare TotalInterior = 360;       //Total Interior Angle of all corners; set to 720 and CornerCount to 5 for a star


///Don't change anything below this line unless you know what you are doing!!
#include "transforms.inc"
#include "math.inc"
                                        
//Calcuated constants
#declare CylL = LinkLength-(LinkRadius*2);
#declare LinkCount = CornerCount*CornerLinkCount+int((1+CornerCount)/2)*StraightLinkCountEven+int(CornerCount/2)*StraightLinkCountOdd;

#if ((StraightLinkCountOdd>0) & (StraightLinkCountEven>0))
#declare CornerRotation = TotalInterior/(CornerCount*(CornerLinkCount+1.0));
#else
#if (StraightLinkCountOdd+StraightLinkCountEven)
#declare CornerRotation = TotalInterior/(CornerCount*(CornerLinkCount+0.5));
#else
#declare CornerRotation = TotalInterior/(CornerCount*(CornerLinkCount));
#end
#end

#declare LinkCurve = difference {
     torus {LinkRadius-WireRadius,WireRadius sturm}
     plane {-x,0}
     rotate 90*x
  }
 
 
#declare LinkSeg = cylinder {
    <0, 0, 0>, <CylL, 0, 0>, WireRadius
  }


#declare TestLink = union {
    object {
      cylinder {<-LinkRadius+WireRadius, 0, 0>, <CylL+LinkRadius-WireRadius, 0, 0>, WireRadius}
      translate z*(LinkRadius-WireRadius)
    }
    object {
      cylinder {<-LinkRadius+WireRadius, 0, 0>, <CylL+LinkRadius-WireRadius, 0, 0>, WireRadius}
      translate -z*(LinkRadius-WireRadius)
    }
  }
#declare Link = union {
    object {LinkCurve}
    object {
      LinkCurve
      rotate y*180
      translate x*CylL
    }
    object {
      LinkSeg
      translate y*(LinkRadius-WireRadius)
    }
    object {
      LinkSeg
      translate -y*(LinkRadius-WireRadius)
    }
  }

//Functions to determine which feature of the frame is being generated as a function of the Section number
#declare OnCorner=function(Section) {odd(Section)};
#declare OnOddStraight=function(Section) {odd(int(Section/2))*even(Section)};
#declare OnEvenStraight=function(Section) {even(int(Section/2))*even(Section)};

//Macro to steer chain growth by moduling DeltaRotation; modify this to generate different shapes
#macro SteerChain(Section)
    #debug concat(" Section=",str(Section,3,0)," SubIndex=",str(SubIndex,3,0),
                  "OnEvenStraight",str(OnEvenStraight(Section),3,0),
                  " OnOddStraight=",str(OnOddStraight(Section),3,0),"\n")
    #declare DeltaRotation = 0;  
    #if (OnEvenStraight(Section))
      #if (SubIndex >= StraightLinkCountEven)
        #declare Section = Section + 1;
        #declare DeltaRotation = CornerRotation;
        #declare SubIndex = 0;
        #if (CornerLinkCount=0)
          #declare Section = Section + 1;
         #end      
      #end
    #else #if (OnOddStraight(Section))
      #if (SubIndex >= StraightLinkCountOdd)
        #declare Section = Section + 1;
        #declare DeltaRotation = CornerRotation;
        #declare SubIndex = 0;
        #if (CornerLinkCount=0)
          #declare Section = Section + 1;
         #end      
      #end
    #else //Am on corner
      #declare DeltaRotation = CornerRotation; //Make corner
      #if (SubIndex >= CornerLinkCount)
        #declare Section = Section + 1;
        #declare SubIndex = 0;
        #if (OnEvenStraight(Section))
          #if (StraightLinkCountEven=0)
            #declare Section = Section + 1;
          #end      
        #else #if (OnOddStraight(Section))
          #if (StraightLinkCountOdd=0)
            #declare Section = Section + 1;
          #end  
        #end  
        #end
      #end  
    #end
    #end
#end

#declare ChainFrame = union {  
  #declare Index = 0;
  #if (StraightLinkCountEven=0)
    #declare Section = 1;
  #else
    #declare Section = 0;
  #end
  #declare SubIndex = 0;
  #declare AttachPt = <0,0,0>;
  #declare Rotation = 0;
  #declare DeltaRotation = 0;
  #declare MinExtent = <0,0,0>;
  #declare MaxExtent = <0,0,0>;

  //Generate each link of the chain
  #while(Index < LinkCount)
    #declare Twist = InitialTwist + TwistDelta*Index; //Amount of Twist is directly related to Link Index
   
    //Adjust the pivot point of the link for a natural appearance, such that
    //vertical links rotate about torus minor radius center, and
    //horizontal links rotate about the torus major radius center
    #declare PivotAdjust =(LinkRadius-WireRadius)*abs(sin(radians(Twist)));  
   
//   #debug concat("Twist=",str(InitialTwist+TwistDelta*Index,5,0)," PivotAdjust=",str(PivotAdjust,5,3),"\n")
//    cylinder {AttachPt,AttachPt-z*LinkRadius*.99, LinkRadius-2*WireRadius} //Identifies attachment point location during development

    //Generate the link, orient and position it
    object {Link rotate x*(InitialTwist + TwistDelta*Index)   //Apply twist
                 translate x*PivotAdjust                      //Apply pivot point adjustment
                 rotate z*Rotation                            //Curve around frame
                 translate AttachPt                           //Move to Attachment Point
                 }
    #declare Index = Index + 1;
    #declare SubIndex = SubIndex + 1;

    //Calculate where the attachment point has moved as a result of the above transformation
    #declare AttachPt=AttachPt+vtransform(x*(CylL+2*PivotAdjust),transform{rotate z*Rotation});

    SteerChain(Section) //Steer chain by modulating DeltaRotation of next link to be generated; 0 for straight section, etc.

    //Advance Attachment Point
    #declare Rotation = Rotation + DeltaRotation; //This will be the total rotation of the next link to be generated  

//    #debug concat("DeltaRotation=",str(DeltaRotation,4,0)," Index=",str(Index,3,0)," SubIndex=",str(SubIndex,3,0),"\n")
    
    //Find extent, for autotracking and autozoom
    #declare MinExtent = <min(MinExtent.x,AttachPt.x),min(MinExtent.y,AttachPt.y),0>;
    #declare MaxExtent = <max(MaxExtent.x,AttachPt.x),max(MaxExtent.y,AttachPt.y),max(MaxExtent.z,AttachPt.y)>;
  #end
 // rotate x*45  //For debugging, useful to see frame from a different angle
  }  
   
 
#declare DepthRig = union {
   plane {z, -0.000001} //Nudge toward black
   ChainFrame
   pigment {
      gradient z
      color_map {
         [0 rgb 0]
         [0.5 rgb 1]
         [1 rgb 0]
      }
      scale <1,1,2*LinkRadius/Exposure>
   }
   finish {ambient 1 diffuse 0}
  }


#declare CLOC = <0.0, 0.0, -10.0>;
camera {                   
  orthographic
  location  CLOC
  direction 1*z
  right     x*image_width/image_height
  look_at   <0.0, 0.0,  0.0>
}


object { // scene
  #debug concat("MinExtent = <",str(MinExtent.x,2,0),",",str(MinExtent.y,2,0),",",str(MinExtent.z,2,0),">","\n")
  #debug concat("MaxExtent = <",str(MaxExtent.x,2,0),",",str(MaxExtent.y,2,0),",",str(MaxExtent.z,2,0),">","\n")
 
  #declare ScreenScale = Zoom/(1.1+max(MaxExtent.x-MinExtent.x,MaxExtent.y-MinExtent.y));
  //Apply autotracking and autozooming
  DepthRig translate <-(MaxExtent.x+MinExtent.x)/2,-(MaxExtent.y+MinExtent.y)/2,0> scale <ScreenScale,ScreenScale,ScreenScale>
}

//Rick's Chain Maker
//Copyright 2008 Richard Swika
//http://home.comcast.net/~chainmaker