// Persistence of Vision Ray Tracer Scene Description File
// File: grasspatch.inc
// Vers: 1.0 of grasspatch.inc for POV-Ray 3.1
// Desc: Draws a patch of grass based on a few simple variables
//       Include at the beginning of the file
// Date: 01/18/2000
// Auth: Josh English

#declare HelloGrassPatch = true;

#ifndef(HelloBezier) #include "bezier.inc" #end

#macro DrawBlade(s1,s2,s3)
        ConnectSmooth(s1,s2)
        ConnectSmooth(s1,s3)
#end

#macro RangedRand(mn,mx,sv)
   mn + rand(sv)*(mx-mn)
#end

#declare Blade_Tex = texture { pigment { rgb <0,0.3,0> } finish { phong 0.25 ambient 0.2 } }

#declare Blade_Height_Minimum = 1.75;
#declare Blade_Height_Maximum = 3;
// New on 1-22
#declare Blade_Detail = 10;
#declare Max_Blade_Angle = 45;
#declare Min_Blade_Angle = 10;
// until here
#declare someseed = seed(2345);
#declare xseed = seed(42);
#declare zseed = seed(369);

#declare Blade_Density = 2; // per unit

#declare Patch_Rotation = 0;
#declare Patch_Translation = <0,0,0>;

#declare Patch_Shape = 1; // 0 for circle, 1 for square, 2 for polygon
// used for PatchShape 0 (Circle)
#declare Clump_Radius_Minimum = 0;
#declare Clump_Radius_Maximum = 2;

// used for PatchShape 1 (Square)
#declare Patch_X_Minimum = -5;
#declare Patch_X_Maximum = 5;

#declare Patch_Z_Minimum = -5;
#declare Patch_Z_Maximum = 5;

// used for PatchShape 2 (Polygon)
#declare Patch_Sides = 3;
#declare Poly_Radius_Range = 0.333;
#declare RadialOffset = 0;

// used for PatchShape 3 (Curve)
#declare Patch_Path = array[4] { <-3,0,0>, <-1,0,5>, <1,0,-5> <3,0,0> }
#declare Path_Density = 6;
#declare Path_Points = 20;

#declare PlotPoints = false;

#declare Spread_Correction = 1;

#ifndef(NumberOfBlades) #declare NumberOfBlades = 100; #end

// New on 3-2-00
// This should allow us to make the blades further away less
// detailed, thus saving parse and render times
#declare Blade_Scale = 1;
#declare Blade_Width = 0.075;
#declare Use_Blade_Distance = false;
#declare Camera_Position = <0,0,-4>;
#declare Max_Blade_Detail = 20;
#declare Min_Blade_Detail = 5;
#declare Max_Detail_Distance = 1;
#declare Min_Detail_Distance = 10;

#macro FillOutBlade(A)
  #local cpnt = vrotate(<Blade_Width,0,0>,<0,45,0>);
  #local ccpnt = vrotate(<Blade_Width,0,0>,<0,-45,0>);
  #declare ClockwiseSpline = array[4] { A[0] + cpnt,
                                   A[1] + cpnt,
                                   A[2] + <-0.1,0,-.1>,
                                   A[3] + <0,0,-0.01> }
        
  #declare CounterSpline = array[4] { A[0] + ccpnt,
                                   A[1] + ccpnt,
                                   A[2] + <-0.1,0,0.1>,
                                   A[3] + <0,0,0.01> }
#end

// From MyMaxs
#macro Return(sd,ed,sr,er,rd,dg)
  #local a = (rd - sd)/(ed-sd);
  #local a = pow(a,dg);
  #local b = sr + a*(er-sr);
  max(min(b,er),sr)
#end

// the big macro
#macro PlantPatch()
  #declare Temp_Number = NumberofPoints;
  #declare NumberofPoints = Blade_Detail;
  #declare Blade_Count = 0;
  #declare Clump_Radius_Range = Clump_Radius_Maximum - Clump_Radius_Minimum;
  #declare Blade_X_Range = Patch_X_Maximum - Patch_X_Minimum;
  #declare Blade_Z_Range = Patch_Z_Maximum - Patch_Z_Minimum;

  #declare Blade_Distance = 1/Blade_Density;
  #declare X_Start = Patch_X_Minimum + 0.5*Blade_Distance;
  #declare Z_Start = Patch_Z_Minimum + 0.5*Blade_Distance; 

// Find how many blades to use
  #switch(Patch_Shape)
    #case(1) // Square
      #declare NumberOfBlades = Blade_Density * Blade_X_Range * Blade_Density * Blade_Z_Range;
    #break
    #case(3) // Curve
      #declare NumberOfBlades = Path_Points * Path_Density;
    #break
    #else
      #declare NumberOfBlades = pow(Blade_Density,2) * int(pi*(pow(Clump_Radius_Maximum,2) - pow(Clump_Radius_Minimum,2)));
    #end

  #declare X_Count = X_Start;
  #declare Z_Count = Z_Start;
  #declare thisStop = 0;
  #declare thisCycle = 1;
  #declare BezTex = Blade_Tex
//  #render concat("\nX_range  is " + str(Blade_X_Range,3,3))
//  #render concat("\nZ_range  is " + str(Blade_Z_Range,3,3))
  #debug concat("\nTotal number of dots is ", str(NumberOfBlades,3,0))

  #while (Blade_Count < NumberOfBlades)
    // Find the placement of the blade
    #switch(Patch_Shape)
      #case (1) // Square shape
        #declare px = X_Count + rand(xseed)*2*Blade_Distance - Blade_Distance;
        #declare pz = Z_Count + rand(zseed)*2*Blade_Distance - Blade_Distance;
        #if ( px > Patch_X_Maximum ) #declare px = Patch_X_Maximum; #end
        #if ( px < Patch_X_Minimum ) #declare px = Patch_X_Minimum; #end
        #if ( pz > Patch_Z_Maximum ) #declare pz = Patch_Z_Maximum; #end
        #if ( pz < Patch_Z_Minimum ) #declare pz = Patch_Z_Minimum; #end
        #declare X_Count = X_Count + Blade_Distance;
      #break
      #case(2) // Polygon
        // Step one find a random angle
        #declare thisAngle = rand(someseed) *360;
        // Step two find the maximum radius
        #declare tempRadRAnge = Poly_Radius_Range*Clump_Radius_Maximum;
        #declare thisMaxRadius = Clump_Radius_Maximum - abs(sin(thisAngle/360*pi*Patch_Sides))*tempRadRAnge;
        // Step three find a random radius
        #declare thisRadius = pow(rand(someseed),Spread_Correction)*thisMaxRadius;
        #declare thisPoint = vrotate(<thisRadius,0,0>,<0,(thisAngle+RadialOffset),0>);
        #declare px = thisPoint.x;
        #declare pz = thisPoint.z;
      #break
      #case(3) //Curve
        #declare thisCenter = ReturnPosition(Patch_Path,thisStop);
        #declare thisRadius = pow(rand(someseed),Spread_Correction);
        #declare thisPoint = <thisRadius*Clump_Radius_Maximum,0,0>;
        #declare thisPoint = vrotate(thisPoint,<0,rand(someseed)*360,0>);
        #declare thisPoint = thisPoint + thisCenter;
        #declare px = thisPoint.x;
        #declare pz = thisPoint.z;
        #declare thisCycle = thisCycle + 1;
      #break
      #else // circular
        #declare subr = pow(rand(someseed),Spread_Correction);
        #declare subr = Clump_Radius_Minimum + subr*Clump_Radius_Range;
        #declare thispoint = <subr,0,0>;
        #declare thispoint = vrotate(thispoint,<0,rand(someseed)*360,0>);
        #declare px = thispoint.x;
        #declare pz = thispoint.z;
      #end

      // Create the Blade
      #local h4 = RangedRand(Blade_Height_Minimum,Blade_Height_Maximum,someseed);
      #local r4 = RangedRand(tan(radians(Min_Blade_Angle))*h4,tan(radians(Max_Blade_Angle))*h4,someseed);
      #local curve = RangedRand(0.667,1.333,someseed);
      #declare CentralSpline = array[4] { <0,0,0> <0,h4/3,0> <0,curve*h4,0> <r4,h4,0> }
      FillOutBlade(CentralSpline)
      #if (PlotPoints)
         sphere { <px,0,pz> 0.25 texture {BezTex}
                  scale Blade_Scale translate Patch_Translation}
      #else
         // calculate detail (if necessary)
         #if (Use_Blade_Distance)
          #local Distance = vlength((<px,0,pz>+Patch_Translation)-Camera_Position);
          #declare NumberofPoints = Return(Max_Detail_Distance,Min_Detail_Distance,Max_Blade_Detail,Min_Blade_Detail,Distance,1);
//          #render concat("\nDistance is " + str(Distance,3,3))
//          #render concat(" Number of Points is " + str(NumberofPoints,3,3))
         #end
         union { DrawBlade(CentralSpline,ClockwiseSpline,CounterSpline)
                 rotate rand(someseed)*360*y
                 scale Blade_Scale
                 translate<px,0,pz>+Patch_Translation }
      #end
    // Reset X_Count and Z_Count if necessary
    #if (X_Count > Patch_X_Maximum)
       #declare Z_Count = Z_Count + Blade_Distance;
       #declare X_Count = X_Start;
    #end
    // Reset thisStop and thisCycle if necessary
    #if ( thisCycle > Path_Density )
       #declare thisStop = thisStop + (1/Path_Points);
       #declare thisCycle = 1;
    #end

    #declare Blade_Count = Blade_Count + 1;
    #end
    #declare NumberofPoints = Temp_Number;
#end