// Persistence of Vision Ray Tracer Scene Description File
// File: Bezier.inc
// Vers: 3.1
// Desc:  Bezier Spline Macro Include File
// Date:  11-11-99 First made into .inc file
//        11-15-99 Started the smooth triangle connection bewtween two splines
//        11-15-99 Started the Radius Change feature
//        11-17-99 Managed to fix problems with ConnectSmooth, so all the
//                 Triangles should show up now
//        12-28-99 Made DrawStrand Macro
//        01-17-00 Made ConnectStrandToSpline Macro
//        01-19-00 Deleted ConnectStrandToSpline Macro due to memory issues
//        02-11-00 Added MoveSpline, ReverseSpline, RotateSpline, and ScaleSpline
 

// Auth: Josh English

#declare HelloBezier = true;

// Set Defaults
 #declare ShowTangents = false;
#declare FullCurve = false;
#declare NumberofPoints = 20;
#declare AccelerationDegree = 1;
#declare BezTex = texture { pigment { red 1 } finish { phong 1 } }
#declare BezRad = 0.1;
#declare BezThing = sphere { <0,0,0> BezRad texture { BezTex }  }
#declare NoShadow = true; 


// Autopoint takes a point and it's contol, and creates a new control point
// in the opposite direction, the lenght is scaled by a factor of d
#macro Autopoint(a,b,d)
  #local newpoint = a - d*(b - a);
  newpoint;
#end

// Square creates two triangles that connect four points
#macro Square(A,B,C,D)
  union {
  triangle { A, B, C }
  triangle { D, C, A } texture { BezTex } }
#end


// CheckDefaults will fill out anything not specified by the user
#macro CheckDefaults()
   #ifndef (ShowTangents)
      #declare ShowTangents = false;
   #end
   #ifndef (FullCurve)
      #declare FullCurve = false;
   #end
   #ifndef (NumberofPoints)
      #declare NumberofPoints = 20;
   #end
   #ifndef (AccelerationDegree)
      #declare AccelerationDegree = 1;
   #end
   #ifndef (BezTex)
      #declare BezTex = texture { pigment { red 1 } finish { phong 1 } }
   #end
   #ifndef (BezRad)
      #declare BezRad = 0.1;
   #end
   #ifndef (BezThing)
      #declare BezThing = sphere { <0,0,0> BezRad texture { BezTex }  }
   #end
   #ifndef (NoShadow) #declare NoShadow = true; #end
#end

// ShowBezier () relies on predefined identifiers to be in place
#macro ShowBezier()
   CheckDefaults()
	#local c = 0;
	#while ( c < 1 )
	  #local d = pow(c,AccelerationDegree);
	  #local m = 1 - d;
	  #local temp = FirstPoint*pow(m,3) + 
	                FirstControl*d*3*pow(m,2) + 
	                SecondControl*3*pow(d,2)*m + 
	                SecondPoint*pow(d,3);
     // Find the Next Point on the spline
	  #local n = pow(c + (1/NumberofPoints),AccelerationDegree);
	  #local o = 1 - n;
	  #local next = FirstPoint*pow(o,3) + 
	                FirstControl*n*3*pow(o,2) + 
	                SecondControl*3*pow(n,2)*o + 
	                SecondPoint*pow(n,3);
	 #if (FullCurve)
	       cylinder { temp, next BezRad texture { BezTex } 
	                  #if(NoShadow) no_shadow #end }
     #end	
	   // Find the proper transformation for the thing
     #local nx = vnormalize(next - temp);
     #local nz = vnormalize(vcross(nx,y));
     #local ny = vcross(nz,nx);
	  object{ BezThing 
	          #if(NoShadow) no_shadow #end  
            matrix <nx.x,nx.y,nx.z,ny.x,ny.y,ny.z,nz.x,nz.y,nz.z,temp.x,temp.y,temp.z> }

	  #declare c = c + (1/NumberofPoints);
	#end
	#if (ShowTangents)
     cylinder { FirstPoint,FirstControl 0.1 pigment { rgb 1 } no_shadow }
     cylinder { SecondControl,SecondPoint 0.1 pigment { rgb 1 } no_shadow}
   #end
#end

// Also need a method of taking a spline and a normalized number and return
// a place on the spline
#macro ReturnPosition(id,num)
 // CheckDefaults()
  #local d = pow(num,AccelerationDegree);
  #local m = 1 - d;
  #local this = id[0]*pow(m,3) + 
                id[1]*d*3*pow(m,2) + 
                id[2]*3*pow(d,2)*m + 
                id[3]*pow(d,3);
  this
#end

// DrawSpline takes an array identifier and
#macro DrawSpline(id)
   #declare BezThing = sphere { <0,0,0> BezRad texture { BezTex }  }
	#local c = 0;
	#while ( c < 1 )
	  #local temp = ReturnPosition(id,c);
     // Find the Next Point on the spline
     #local next = ReturnPosition(id,(c+1/NumberofPoints));
     #if (FullCurve)
	      #if ( c < 1 ) cylinder { temp, next BezRad texture { BezTex }
	                               #if(NoShadow) no_shadow #end  } #end
     #end	
	   // Find the proper transformation for the thing
     #local nx = vnormalize(next - temp);
     #local nz = vnormalize(vcross(nx,y));
     #local ny = vcross(nz,nx);
	  object{ BezThing  texture { BezTex }
	          #if(NoShadow) no_shadow #end 
            matrix <nx.x,nx.y,nx.z,ny.x,ny.y,ny.z,nz.x,nz.y,nz.z,temp.x,temp.y,temp.z> }

	  #declare c = c + (1/NumberofPoints);
	#end
	#if (ShowTangents)
     cylinder { id[0],id[1] BezRad*0.75 pigment { rgb 1 } no_shadow}
     cylinder { id[2],id[3] BezRad*0.75 pigment { rgb 1 } no_shadow}
   #end
#end

// DrawPartialSpline takes an array identifier and
#macro DrawPartialSpline(id,lim)
   CheckDefaults()
	#local c = 0;
	#while ( c < lim )
	  #local temp = ReturnPosition(id,c);
     // Find the Next Point on the spline
     #local next = ReturnPosition(id,(c+1/NumberofPoints));
     #if (FullCurve)
	      #if ( c < 1 ) cylinder { temp, next BezRad texture { BezTex } 
	                               #if(NoShadow) no_shadow #end  } #end
     #end	
	   // Find the proper transformation for the thing
     #local nx = vnormalize(next - temp);
     #local nz = vnormalize(vcross(nx,y));
     #local ny = vcross(nz,nx);
	  object{ BezThing 
	          #if(NoShadow) no_shadow #end  
             matrix <nx.x,nx.y,nx.z,ny.x,ny.y,ny.z,nz.x,nz.y,nz.z,temp.x,temp.y,temp.z> }

	  #declare c = c + (1/NumberofPoints);
	#end
	#if (ShowTangents)
     cylinder { id[0],id[1] BezRad*0.75 pigment { rgb 1 } no_shadow}
     cylinder { id[2],id[3] BezRad*0.75 pigment { rgb 1 } no_shadow}
   #end
#end

// SplineLength returns a rough estimate of the length of the spline
#macro SplineLength(id)
  #local c = 0;
  #declare len = 0;
  #while  ( c < 1 )
    #local temp = ReturnPosition(id,c);
    #local next = ReturnPosition(id,(c + 0.05));
    #declare len = len + vlength(next-temp);
    #declare c = c + 0.05;
  #end
  len
#end

// BridgeSpline takes an array identifier and
#macro BridgeSplines(id1,id2)
   CheckDefaults()
	#local c = 0;
	#while ( c < 1 )
	  #local t1 = ReturnPosition(id1,c);
	  #local t2 = ReturnPosition(id2,c);
     // Find the Next Point on the spline
     #local n1 = ReturnPosition(id1,(c+1/NumberofPoints));
     #local n2 = ReturnPosition(id2,(c+1/NumberofPoints));     
     #if (FullCurve)
	      #if ( (c+1/NumberofPoints) < 1 ) 
	          cylinder { t1, n1 BezRad texture { BezTex } #if(NoShadow) no_shadow #end  }
	          cylinder { t2, n2 BezRad texture { BezTex } #if(NoShadow) no_shadow #end  }
	      #end
     #end	
	   // Find the proper transformation for the thing
     #local nx = vnormalize(next - temp);
     #local nz = vnormalize(vcross(nx,y));
     #local ny = vcross(nz,nx);
	  object{ BezThing 
	          #if(NoShadow) no_shadow #end  
            matrix <nx.x,nx.y,nx.z,ny.x,ny.y,ny.z,nz.x,nz.y,nz.z,temp.x,temp.y,temp.z> }
     cylinder ( t1,t2 BezRad*0.75 texture { BezTex ) no_shadow }
	  #declare c = c + (1/NumberofPoints);
	#end
	#if (ShowTangents)
     cylinder { id[0],id[1] BezRad pigment { rgb 1 } no_shadow}
     cylinder { id[2],id[3] BezRad pigment { rgb 1 } no_shadow}
   #end
#end

#macro Connect(SP,SR,EP,ER)
  #local D=vlength(EP-SP);
  #local Rd=SR-ER;
  #local D2=sqrt(abs(D*D-Rd*Rd));
  cone { SP+(EP-SP)/D*Rd*(SR)/D,(SR)*D2/D,EP+(EP-SP)/D*Rd*(ER)/D,(ER)*D2/D }
#end

// DrawTail deals with  a start radius and an end radius
#macro DrawTail(id)
   CheckDefaults()
   #ifndef (StartRadius) #declare StartRadius = 1; #end
   #ifndef (EndRadius) #declare EndRadius = 0.1; #end
	#local c = 0;
	#while ( c < 1 )
	  #local temp = ReturnPosition(id,c);
	  #local t_rad = StartRadius + (c * (EndRadius - StartRadius));
     // Find the Next Point on the spline
      #local next = ReturnPosition(id,(c+1/NumberofPoints));
      #local n_rad = StartRadius + ((c+1/NumberofPoints) * (EndRadius - StartRadius));
      #if (FullCurve)
	      #if ( (c+1/NumberofPoints) < 1 ) 
	          object { Connect(temp,t_rad,next,n_rad)
	                   texture { BezTex } }
	      #end
     #end	
	   // Find the proper transformation for the thing
     #local nx = vnormalize(next - temp);
     #local nz = vnormalize(vcross(nx,y));
     #local ny = vcross(nz,nx);
	  sphere { 0,1 
	          scale t_rad
	          texture { BezTex }
              matrix <nx.x,nx.y,nx.z,ny.x,ny.y,ny.z,nz.x,nz.y,nz.z,temp.x,temp.y,temp.z> }

	  #declare c = c + (1/NumberofPoints);
	#end
	#if (ShowTangents)
     cylinder { id[0],id[1] BezRad pigment { rgb 1 } no_shadow}
     cylinder { id[2],id[3] BezRad pigment { rgb 1 } no_shadow}
   #end
#end


// Need a way to automatically crate a new spline from an old one with 
// a smooth connection
// new[0] = old[3]
// new[1] = autopoint(old[3],old[2],1)
// new[2] and new[3] are customizable

// Method for connecting two splines with triangles, kind of a bicubic patch
// Thing without the control points
#macro ConnectSplines(id1,id2)
   CheckDefaults()
   mesh {
	#local c = 0;
	#while ( c < 1 )
	  #local p1 = ReturnPosition(id1,c);
	  #local p2 = ReturnPosition(id1,(c+1/NumberofPoints));
	  #local p3 = ReturnPosition(id2,c);
	  #local p4 = ReturnPosition(id2,(c+1/NumberofPoints));
	  //Square (p1,p2,p4,p3)
	  triangle { p1,p2,p3 }
	  triangle { p2,p4,p3 }
     #declare c = c + (1/NumberofPoints);
	#end
	texture { BezTex }
	} // end mesh
#end
//#include "MyMacs.inc"

#macro ConnectSmooth(id1,id2)
  CheckDefaults()
  mesh {
  #local c = 0;
  #while (c < 1)
   // Get Positions for First Spline
    #local ac = ReturnPosition(id1,c);
    #local an = ReturnPosition(id1,(c+(1/NumberofPoints)));
    #local aq = ReturnPosition(id1,(c+(2/NumberofPoints)));
    #local ap = ReturnPosition(id1,(c-(1/NumberofPoints)));
   // Get Position for Second Spline
    #local bc = ReturnPosition(id2,c);
    #local bn = ReturnPosition(id2,(c+(1/NumberofPoints)));
    #local bq = ReturnPosition(id2,(c+(2/NumberofPoints)));
    #local bp = ReturnPosition(id2,(c-(1/NumberofPoints)));
   // Find six normals
    #declare n1 = vnormalize(vcross((bc-bp),(ap-bp)));
    #declare n2 = vnormalize(vcross((bn-bc),(ac-bc)));
    #declare n3 = vnormalize(vcross((ac-an),(bn-an)));
    #declare n4 = vnormalize(vcross((ac-an),(bn-an)));
    #declare n5 = vnormalize(vcross((bq-bn),(an-bn)));
    #declare n6 = vnormalize(vcross((an-aq),(bq-aq)));
   // Now find the four local normals that are the average of three
   // triangle normals each
    #declare acn = vnormalize((n2 + n3 + n4)/3);
    #declare ann = vnormalize((n4 + n5 + n6)/3);
    #declare bcn = vnormalize((n1 + n2 + n3)/3);
    #declare bnn = vnormalize((n3 + n4 + n5)/3);
   // Draw the two smooth triangles
    smooth_triangle { ac acn bc bcn bn bnn }
    smooth_triangle { ac acn an ann bn bnn }
   #declare c = c + (1/NumberofPoints);
  #end
  texture { BezTex }
  }
#end

#macro CheckStrandDefaults()
  #ifndef (NumberCorrection) #declare NumberCorrection = 2; #end
  #ifndef (NoRotations) #declare NoRotations = 1; #end
  #ifndef (OutRadius) #declare OutRadius = 0.5; #end
  #ifndef (RotationOffset) #declare RotationOffset = 0; #end
  #ifndef (RadMin) #declare RadMin = 0; #end
  #ifndef (RadMax) #declare RadMax = 1; #end
  #ifndef (RadialMethod) #declare RadialMethod = 0; #end
#end
// Drawstrand
#macro DrawStrand(id)
   CheckDefaults()
   CheckStrandDefaults()
	#declare NumberofPoints = int(NumberofPoints * NumberCorrection);
	#local c = 0;

	#while ( c < 1 )
		#local c1 = c + 1/NumberofPoints;
	  // Find the Current Point and the Next Point
	  #local temp = ReturnPosition(id,c);
     #local next = ReturnPosition(id,c1);
     
     // Find the rotation around the spline of the current and next points
     #local currentRotation = (360*c*NoRotations) + RotationOffset;
     #local nextRotation = (360*c1*NoRotations) + RotationOffset;
     
     // Find the current radius and next radius based on the Method
     #switch(RadialMethod)
       #case(1) // Linear Method
         #local RadRange = RadMax - RadMin;
         #local currentRadius = RadMin + (c*RadRange);
         #local nextRadius = RadMin + (c1*RadRange);
         #break;
       #case(2) // Sine Method
         #local RadRange = RadMax - RadMin;
         #local currentRadius = ( sin(c * pi)*RadRange) + RadMin;
         #local nextRadius = ( sin(c1 * pi)*RadRange) + RadMin;
         #break;
       #else // constant method
         #local currentRadius = OutRadius;
         #local nextRadius = OutRadius;
     #end
     #local FirstPos = vrotate(<0,0,currentRadius>,<currentRotation,0,0>);
     #local SecondPos = vrotate(<vlength(next-temp),0,nextRadius>,<nextRotation,0,0>);

	
	   // Find the proper transformation for the thing
     #local nx = vnormalize(next - temp);
     #local nz = vnormalize(vcross(nx,y));
     #local ny = vcross(nz,nx);
	  object{ merge { sphere { FirstPos, BezRad } 
	                  cylinder { FirstPos, SecondPos, BezRad }
	                  texture { BezTex } }
	          #if(NoShadow) no_shadow #end  
             matrix <nx.x,nx.y,nx.z,ny.x,ny.y,ny.z,nz.x,nz.y,nz.z,temp.x,temp.y,temp.z> }
            
	  #declare c = c1;
	#end
#end

// Added 2-11-2000
#macro MoveSpline(Spl,vct)
  array [4] { 
    Spl[0] + vct,
    Spl[1] + vct,
    Spl[2] + vct,
    Spl[3] + vct }
#end

#macro ReverseSpline(Spl)
  array [4] { Spl[3] Spl[2] Spl[1] Spl[0] }
#end

#macro RotateSpline (Spl,vct)
  array [4] {
    vrotate(Spl[0],vct),
    vrotate(Spl[1],vct),
    vrotate(Spl[2],vct),
    vrotate(Spl[3],vct) }
#end

#macro ScaleSpline(Spl,vct)
  array [4] {
    Spl[0]*vct,
    Spl[1]*vct,
    Spl[2]*vct,
    Spl[3]*vct }
#end

// 2-14-2000
#macro BezPatch(S1,C1,C2,S2)
#declare cnt = 0;
#declare stp = 0.05;
#declare NumberofPoints = 20;

#while(cnt < 1 )
  #local nxt = cnt + 1/NumberofPoints;
  #local t0 = ReturnPosition(S1,cnt);
  #local t1 = ReturnPosition(C1,cnt);
  #local t2 = ReturnPosition(C2,cnt);
  #local t3 = ReturnPosition(S2,cnt);

  #local n0 = ReturnPosition(S1,nxt);
  #local n1 = ReturnPosition(C1,nxt);
  #local n2 = ReturnPosition(C2,nxt);
  #local n3 = ReturnPosition(S2,nxt);
  #declare Temp = array [4] { t0, t1, t2, t3 }
  #declare Next = array [4] { n0, n1, n2, n3 }
  ConnectSmooth(Temp,Next)
  #declare cnt = nxt;
#end
#end

#macro BezFrame(S1,C1,C2,S2)
  #local tempCurve = FullCurve;
  #local FullCurve = true;
  DrawSpline(S1)
 // DrawSpline(C1)
 // DrawSpline(C2)
  DrawSpline(S2)
#declare cnt = 0;
#declare stp = 0.05;

#while(cnt <= 1 )
  #local nxt = cnt + 0.333;
  #local t0 = ReturnPosition(S1,cnt);
  #local t1 = ReturnPosition(C1,cnt);
  #local t2 = ReturnPosition(C2,cnt);
  #local t3 = ReturnPosition(S2,cnt);
  #declare Temp = array [4] { t0, t1, t2, t3 }
  DrawSpline(Temp)
  #declare cnt = nxt;
#end
#local FullCurve = tempCurve;
#end

