//    Persistence of Vision Ray Tracer version 3.5 Include File
//    File: shapes.inc
//    Last updated: 2001.8.13
//    Description: This file contains macros for working with objects, as well
//    as macros for creating special objects, such as bevelled text,
//    height fields, and rounded shapes.

#ifndef(SHAPES_INC_TEMP)
#declare SHAPES_INC_TEMP = version;
#version 3.5;

#ifdef(View_POV_Include_Stack)
   #debug "including shapes.inc\n"
#end

#include "shapes_old.inc"
#include "consts.inc"
#include "transforms.inc"
#include "strings.inc"
#include "math.inc"

// These macros are just interfaces to the trace() function.
// They return values through their parameters:
// If an intersection is found, they return true and set
// OPt to the intersection point, and ONorm to the normal.
// Otherwise they return false, and do not modify OPt or ONorm.
#macro Isect(Pt, Dir, Obj, OPt)
   #local Norm = <0,0,0>;
   #local IPt = trace(Obj, Pt, Dir, Norm);
   #if (vlength(Norm) > 0)
      #declare OPt = IPt;
      #local Return=true;
   #else
      #local Return=false;
   #end
   (Return)
#end
#macro IsectN(Pt, Dir, Obj, OPt, ONorm)
   #local Norm = <0,0,0>;
   #local IPt = trace(Obj, Pt, Dir, Norm);
   #if (vlength(Norm) > 0)
      #declare OPt = IPt;
      #declare ONorm = Norm;
      #local Return=true;
   #else
      #local Return=false;
   #end
   (Return)
#end


// A shortcut for getting both min and max extents of an object
#macro Extents(Obj, Min, Max)
   #declare Min = min_extent(Obj);
   #declare Max = max_extent(Obj);
#end


// shortcuts for using the CenterTrans and AlignTrans
// macros with objects.
#macro Center_Object(Object, Axis)
   object {Object Center_Trans(Object, Axis)}
#end

#macro Align_Object(Object, Axis, Pt)
   object {Object Align_Trans(Object, Axis, Pt)}
#end


// A simple beveled text macro. The parameters are:
// Font: the name of the font file.
// String: the text string the text object is composed of.
// Cuts: the number of times excess material is cut off, to form the bevel.
//       More cuts will give smoother results, but take longer to render.
// BevelAng: the angle of the bevel.
// BevelDepth: the depth of the bevelled portion of the text.
// Depth: the total depth of the text object.
// Offset: the offset value for the text object.  Since the front faces of each
//         letter need to be in the same plane, z values are ignored.
#macro Bevelled_Text(Font, String, Cuts, BevelAng, BevelDepth, Depth, Offset, UseMerge)
   #if(UseMerge)
      merge {
   #else
      union {
   #end
      text {ttf Font, String Depth-BevelDepth, Offset*(x+y)}
      intersection {
         #local J=0;
         #while(J<Cuts)
            #local A = 2*pi*J/(Cuts);
            #local CA = cos(radians(BevelAng));
            #local SA = sin(radians(BevelAng));
            text {ttf Font, String BevelDepth, Offset*(x+y)
               translate -z*(BevelDepth+J*0.0001)
               Shear_Trans(x, y, < cos(A)*SA, sin(A)*SA, CA>/CA)
            }
            #local J=J+1;
         #end
      }
      translate z*BevelDepth
   }
#end


// Constants used for the text macros
#declare Align_Left = 1;
#declare Align_Right = 2;
#declare Align_Center = 3;

/* Text_Space( Font, String, Size, Spacing )
Computes the width of a text string, including "white space". It
returns the advance widths of all n letters. Text_Space gives the
space a text or a glyph occupies in regard to its surroundings.

Font:    The font to use (see the documentation for the text object)
String:  The text for which we want to know the width
Size:    The size to which the text should be scaled
Spacing: The amount of space to add between the letters. */

#macro Text_Space(Font, String, Size, Spacing)
   #local TO = text {ttf Font concat("|",String,"|") 1 Spacing*x scale <Size,Size,1>}
   #local SO = text {ttf Font "||"                   1 Spacing*x scale <Size,Size,1>}
   ((max_extent(TO).x-min_extent(TO).x)-(max_extent(SO).x-min_extent(SO).x))
#end

/* Text_Width( Font, String, Size, Spacing )
Computes the width of a text string. It returns the advance widths
of the first n-1 letters, plus the glyph width of the last letter.
Text_Width gives the "fysical" width of the text and if you use
only one letter the "fysical" width of one glyph.

Font:    The font to use (see the documentation for the text object)
String:  The text for which we want to know the width
Size:    The size to which the text should be scaled
Spacing: The amount of space to add between the letters. */

#macro Text_Width(Font, String, Size, Spacing)
   #local TO = text {ttf Font String 1 Spacing*x scale <Size,Size,1>}
   (max_extent(TO).x-min_extent(TO).x)
#end

// Circle_Text author: Ron Parker
/* Circle_Text( Font, Text, Size, Spacing, Thickness, Radius, Inverted,
                Justification, Angle )
Creates a text object with the bottom (or top) of the character cells aligned
with all or part of a circle.  This macro should be used inside an object{...}
block.

         Font: The font to use (see the documentation for the text object)
         Text: The text string to be created
         Size: The height of the text string, as you would use to scale a
               standard text object
      Spacing: The amount of space to add between the letters.
    Thickness: The thickness of the letters (see the documentation for the
               text object)
       Radius: The radius of the circle along which the letters are aligned
     Inverted: If this parameter is nonzero, the tops of the letters will
               point toward the center of the circle.  Otherwise, the bottoms
               of the letters will do so.
Justification: One of the constants Align_Left, Align_Right, or Align_Center
        Angle: The point on the circle from which rendering will begin.  The
               +x direction is 0 and the +y direction is 90 (i.e. the angle
               increases anti-clockwise. */

#macro Circle_Text(F, T, S, Sp, Th, R, I, J, A)
   #local FW = Text_Width(F, T, S, Sp);
   #local TO = text {ttf F T 1 0 scale<S, S, 1>}
   #local TH = max_extent(TO).y;
   #local C = array[strlen(T)]
   #if(FW > 2*pi*R)
      #error concat("\n\n**** Text string \"", T, "\" is too long for a circle of the specified radius.\n\n\n")
   #end
   #local AW = -FW*180/pi/R;
   #local SA = A;
   #local EA = A + AW;
   #if(((J = Align_Right) & !I)|((J = Align_Left) & I))
      #local SA = A - AW;
      #local EA = A;
   #else
      #if(J = Align_Center)
         #local SA = A - AW/2;
         #local EA = A + AW/2;
      #end
   #end

   #local CI = 1;
   #while(CI <= strlen(T))
      #local OE = Text_Width(F, substr(T,CI,1), S, Sp);
      #local LW = Text_Width(F, substr(T,1,CI), S, Sp) - OE;
      #local LA = SA + AW*LW/FW + OE/2/FW*AW;
      #if(I)
         #local LA = EA - (LA - SA);
      #end
      #local TO = text {ttf F substr(T, CI, 1) Th 0 scale<S,S,1>}
      #if(I)
         #local C[CI-1] =
         object {TO
            rotate 180*z
            translate <OE/2, TH, 0>
            rotate -90*z
            translate R*x
            rotate LA*z
         }
      #else
         #local C[CI-1] =
         object {TO
            translate -OE/2*x
            rotate -90*z
            translate R*x
            rotate LA*z
         }
      #end
      #local CI = CI + 1;
   #end

   // Create the final object, a union of individual text object letters.
   union {
      #local CI=0;
      #while(CI < strlen(T))
         object {C[CI]}
         #local CI = CI + 1;
      #end
   }
#end


#macro Wedge(Angle)
   #local A = clamp(Angle, 0, 360);
   #if(A < 180)
      difference {
         plane {-x, 0}
         plane {-x, 0 rotate y*A}
      }
   #else
      #if(A = 180)
         plane {-x, 0}
      #else
         intersection {
            plane {x, 0}
            plane {-x, 0 rotate y*A}
            inverse
         }
      #end
   #end
#end


#macro Spheroid(Center, Radius)
   sphere { 0, 1 scale Radius translate Center }
#end


#macro Supertorus(RMj, RMn, MajorControl, MinorControl, Accuracy, MaxGradient)
   #local CP = 2/MinorControl;
   #local RP = 2/MajorControl;
   isosurface {
      function { pow( pow(abs(pow(pow(abs(x),RP) + pow(abs(z),RP), 1/RP) - RMj),CP) + pow(abs(y),CP) ,1/CP) - RMn }
      threshold 0
      contained_by {box {<-RMj-RMn,-RMn,-RMj-RMn>, < RMj+RMn, RMn, RMj+RMn>}}
      #if(MaxGradient >= 1)
         max_gradient MaxGradient
      #else
         evaluate 1, 10, 0.1
      #end
      accuracy Accuracy
   }
#end


// Supercone author: Juha Nieminen
// A cone object where each end is an ellipse, you specify two radii
// for each end.
// SuperCone function: (x^2/a^2+y^2/b^2-1)*(1-z) + (x^2/c^2+y^2/d^2-1)*z = 0
//
// camera { location <6,5,-10> look_at 0 angle 35 }
// light_source { <100,100,-20>,1 }
// plane { y,-1.5 pigment { checker rgb 1, rgb .5 } }
// object { SuperCone(<0,-1.5,0>,1,2, <0,1.5,0>,1,.5)
//     pigment { rgb x } finish { specular .5 }
// }
#macro Supercone(PtA, A, B, PtB, C, D)
   intersection {
      quartic {
         <0, 0,  0,  0,  0,  0,  0,  B*B-2*B*D+D*D, 2*(B*D-B*B), B*B,
         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
         0,  0,  0, A*A-2*A*C+C*C, 2*(A*C-A*A), A*A, 0,  0,  0,  0,
         -(A*A-2*A*C+C*C)*(B*B-2*B*D+D*D),
         -(2*((B*D-B*B)*(A*A-2*A*C+C*C)+(A*C-A*A)*(B*B-2*B*D+D*D))),
         -(B*B*(A*A-2*A*C+C*C)+4*(A*C-A*A)*(B*D-B*B)+A*A*(B*B-2*B*D+D*D)),
         -(2*(B*B*(A*C-A*A)+A*A*(B*D-B*B))), -A*A*B*B>
         sturm
      }
      cylinder {0, z, max(max(abs(A), abs(B)), max(abs(C), abs(D)))}

      bounded_by {cone {0, max(abs(A), abs(B)), z, max(abs(C), abs(D))}}

      #local Dirv = PtB - PtA;
      scale <1,1,vlength(Dirv)>
      #local Dirv = vnormalize(Dirv);
      #if(vlength(Dirv-<0,0,-1>)=0) scale <1,1,-1>
      #else Reorient_Trans(z, Dirv)
      #end
      translate PtA
   }
#end


// Connect two spheres with a cylinder.
// Derived from Connect() macro by John VanSickle
#macro Connect_Spheres(PtA, RadiusA, PtB, RadiusB)
   #local Axis = PtB - PtA;
   #local RadDif = RadiusA - RadiusB;
   #local Len = VDist(PtA, PtB);
   #local D2 = sqrt(f_sqr(Len) - f_sqr(RadDif));

   cone {
      PtA + Axis/Len*RadDif*RadiusA/Len, RadiusA*D2/Len,
      PtB + Axis/Len*RadDif*RadiusB/Len, RadiusB*D2/Len
   }
#end


#macro Wire_Box_Union(A, B, WireRadius)
   Wire_Box(A, B, WireRadius, no)
#end
#macro Wire_Box_Merge(A, B, WireRadius)
   Wire_Box(A, B, WireRadius, yes)
#end
#macro Wire_Box(A, B, WireRadius, UseMerge)
   #local AA = <min(A.x, B.x), min(A.y, B.y), min(A.z, B.z)>;
   #local BB = <max(A.x, B.x), max(A.y, B.y), max(A.z, B.z)>;

   #local Delta=abs(BB.x-AA.x)/2;
   #if (Delta<WireRadius)
      #warning "\nWire_Box() macro called with x-size < Radius,\nresults may not be as expected\n"

      #local AA = <AA.x+Delta, AA.y, AA.z>;
      #local BB = <BB.x-Delta, BB.y, BB.z>;
   #else
      #local AA = <AA.x+WireRadius, AA.y, AA.z>;
      #local BB = <BB.x-WireRadius, BB.y, BB.z>;
   #end

   #local Delta=abs(BB.y-AA.y)/2;
   #if (Delta<WireRadius)
      #warning "\nWire_Box() macro called with y-size < Radius,\nresults may not be as expected\n"

      #local AA = <AA.x, AA.y+Delta, AA.z>;
      #local BB = <BB.x, BB.y-Delta, BB.z>;
   #else
      #local AA = <AA.x, AA.y+WireRadius, AA.z>;
      #local BB = <BB.x, BB.y-WireRadius, BB.z>;
   #end

   #local Delta=abs(BB.z-AA.z)/2;
   #if (Delta<WireRadius)
      #warning "\nWire_Box() macro called with z-size < Radius,\nresults may not be as expected\n"

      #local AA = <AA.x, AA.y, AA.z+Delta>;
      #local BB = <BB.x, BB.y, BB.z-Delta>;
   #else
      #local AA = <AA.x, AA.y, AA.z+WireRadius>;
      #local BB = <BB.x, BB.y, BB.z-WireRadius>;
   #end

   #local LBF = AA;
   #local RBF = < BB.x, AA.y, AA.z>;
   #local RBB = < BB.x, AA.y, BB.z>;
   #local LBB = < AA.x, AA.y, BB.z>;
   #local LTF = < AA.x, BB.y, AA.z>;
   #local RTF = < BB.x, BB.y, AA.z>;
   #local RTB = BB;
   #local LTB = < AA.x, BB.y, BB.z>;

   #if(UseMerge)
      merge {
   #else
      union {
   #end
      sphere {LBF, WireRadius}

      #if (AA.x != BB.x)
         sphere {RBF, WireRadius}
      #end
      #if ((AA.x != BB.x) & (AA.z != BB.z))
         sphere {RBB, WireRadius}
      #end
      #if (AA.z != BB.z)
         sphere {LBB, WireRadius}
      #end

      #if (AA.y != BB.y)
         sphere {LTF, WireRadius}
      #end
      #if ((AA.x != BB.x) & (AA.y != BB.y))
         sphere {RTF, WireRadius}
      #end
      #if ((AA.x != BB.x) & (AA.y != BB.y) & (AA.z != BB.z))
         sphere {RTB, WireRadius}
      #end
      #if ((AA.y != BB.y) & (AA.z != BB.z))
         sphere {LTB, WireRadius}
      #end

      #if (AA.x != BB.x)
         cylinder {LBF, RBF, WireRadius}
         cylinder {LBB, RBB, WireRadius}
         cylinder {LTB, RTB, WireRadius}
         cylinder {LTF, RTF, WireRadius}
      #end

      #if (AA.y != BB.y)
         cylinder {LBF, LTF, WireRadius}
         cylinder {RBF, RTF, WireRadius}
         cylinder {RBB, RTB, WireRadius}
         cylinder {LBB, LTB, WireRadius}
      #end

      #if (AA.z != BB.z)
         cylinder {LTB, LTF, WireRadius}
         cylinder {LBB, LBF, WireRadius}
         cylinder {RTB, RTF, WireRadius}
         cylinder {RBB, RBF, WireRadius}
      #end
   }
#end

#macro Round_Box_Union(A, B, EdgeRadius)
   Round_Box(A, B, EdgeRadius, no)
#end
#macro Round_Box_Merge(A, B, EdgeRadius)
   Round_Box(A, B, EdgeRadius, yes)
#end
#macro Round_Box(A, B, EdgeRadius, UseMerge)
   #local AA = <min(A.x, B.x), min(A.y, B.y), min(A.z, B.z)>;
   #local BB = <max(A.x, B.x), max(A.y, B.y), max(A.z, B.z)>;

   #local Delta=abs(BB.x-AA.x)/2;
   #if (Delta<EdgeRadius)
      #warning "\nRound_Box() macro called with x-size < Radius,\nresults may not be as expected\n"

      #local AA = <AA.x+Delta, AA.y, AA.z>;
      #local BB = <BB.x-Delta, BB.y, BB.z>;
   #else
      #local AA = <AA.x+EdgeRadius, AA.y, AA.z>;
      #local BB = <BB.x-EdgeRadius, BB.y, BB.z>;
   #end

   #local Delta=abs(BB.y-AA.y)/2;
   #if (Delta<EdgeRadius)
      #warning "\nRound_Box() macro called with y-size < Radius,\nresults may not be as expected\n"

      #local AA = <AA.x, AA.y+Delta, AA.z>;
      #local BB = <BB.x, BB.y-Delta, BB.z>;
   #else
      #local AA = <AA.x, AA.y+EdgeRadius, AA.z>;
      #local BB = <BB.x, BB.y-EdgeRadius, BB.z>;
   #end

   #local Delta=abs(BB.z-AA.z)/2;
   #if (Delta<EdgeRadius)
      #warning "\nRound_Box() macro called with z-size < Radius,\nresults may not be as expected\n"

      #local AA = <AA.x, AA.y, AA.z+Delta>;
      #local BB = <BB.x, BB.y, BB.z-Delta>;
   #else
      #local AA = <AA.x, AA.y, AA.z+EdgeRadius>;
      #local BB = <BB.x, BB.y, BB.z-EdgeRadius>;
   #end

   #local LBF = AA;
   #local RBF = < BB.x, AA.y, AA.z>;
   #local RBB = < BB.x, AA.y, BB.z>;
   #local LBB = < AA.x, AA.y, BB.z>;
   #local LTF = < AA.x, BB.y, AA.z>;
   #local RTF = < BB.x, BB.y, AA.z>;
   #local RTB = BB;
   #local LTB = < AA.x, BB.y, BB.z>;

   #if(UseMerge)
      merge {
   #else
      union {
   #end
      sphere {LBF, EdgeRadius}

      #if (AA.x != BB.x)
         sphere {RBF, EdgeRadius}
      #end
      #if ((AA.x != BB.x) & (AA.z != BB.z))
         sphere {RBB, EdgeRadius}
      #end
      #if (AA.z != BB.z)
         sphere {LBB, EdgeRadius}
      #end

      #if (AA.y != BB.y)
         sphere {LTF, EdgeRadius}
      #end
      #if ((AA.x != BB.x) & (AA.y != BB.y))
         sphere {RTF, EdgeRadius}
      #end
      #if ((AA.x != BB.x) & (AA.y != BB.y) & (AA.z != BB.z))
         sphere {RTB, EdgeRadius}
      #end
      #if ((AA.y != BB.y) & (AA.z != BB.z))
         sphere {LTB, EdgeRadius}
      #end

      #if (AA.x != BB.x)
         cylinder {LBF, RBF, EdgeRadius}
         cylinder {LBB, RBB, EdgeRadius}
         cylinder {LTB, RTB, EdgeRadius}
         cylinder {LTF, RTF, EdgeRadius}
      #end

      #if (AA.y != BB.y)
         cylinder {LBF, LTF, EdgeRadius}
         cylinder {RBF, RTF, EdgeRadius}
         cylinder {RBB, RTB, EdgeRadius}
         cylinder {LBB, LTB, EdgeRadius}
      #end

      #if (AA.z != BB.z)
         cylinder {LTB, LTF, EdgeRadius}
         cylinder {LBB, LBF, EdgeRadius}
         cylinder {RTB, RTF, EdgeRadius}
         cylinder {RBB, RBF, EdgeRadius}
      #end

      box {AA-EdgeRadius*x, BB+EdgeRadius*x}
      box {AA-EdgeRadius*y, BB+EdgeRadius*y}
      box {AA-EdgeRadius*z, BB+EdgeRadius*z}
   }
#end

#macro Round_Cylinder_Union(A, B, Radius, EdgeRadius)
   Round_Cylinder(A, B, Radius, EdgeRadius, no)
#end
#macro Round_Cylinder_Merge(A, B, Radius, EdgeRadius)
   Round_Cylinder(A, B, Radius, EdgeRadius, yes)
#end
#macro Round_Cylinder(A, B, Radius, EdgeRadius, UseMerge)

   #if(UseMerge)
      merge {
   #else
      union {
   #end

      #if(Radius<EdgeRadius)
         #warning "\nRound_Cylinder() macro called with Radius < EdgeRadius,\nresults may not be as expected\n"

         #local AA = A + vnormalize(B - A)*Radius;
         #local BB = B + vnormalize(A - B)*Radius;

         cylinder {AA, BB, Radius}
         sphere {0, Radius translate AA }
         sphere {0, Radius translate BB }

      #else

         #local AA = A + vnormalize(B - A)*EdgeRadius;
         #local BB = B + vnormalize(A - B)*EdgeRadius;

         cylinder {A, B, Radius - EdgeRadius}
         cylinder {AA, BB, Radius}
         torus {Radius - EdgeRadius, EdgeRadius translate y*EdgeRadius
            Point_At_Trans(B - A)
            translate A
         }
         torus {Radius - EdgeRadius, EdgeRadius translate y*(vlength(A - B) - EdgeRadius)
            Point_At_Trans(B - A)
            translate A
         }

      #end
   }
#end


// Rounded cone with torus edges
// This shape will fit entirely within a cone given the same parameters.
#macro Round_Cone_Union(PtA, RadiusA, PtB, RadiusB, EdgeRadius)
   Round_Cone(PtA, RadiusA, PtB, RadiusB, EdgeRadius, no)
#end
#macro Round_Cone_Merge(PtA, RadiusA, PtB, RadiusB, EdgeRadius)
   Round_Cone(PtA, RadiusA, PtB, RadiusB, EdgeRadius, yes)
#end
#macro Round_Cone(PtA, RadiusA, PtB, RadiusB, EdgeRadius, UseMerge)
   #if(min(RadiusA, RadiusB) < EdgeRadius)
     #warning "\nRound_Cone() macro called with Radius < EdgeRadius,\nresults may not be as expected\n"
   #end

   #if(RadiusA > RadiusB)
      #local RA = RadiusB;
      #local RB = RadiusA;
      #local PA = PtB;
      #local PB = PtA;
   #else
      #local RA = RadiusA;
      #local RB = RadiusB;
      #local PA = PtA;
      #local PB = PtB;
   #end

   #local Axis = vnormalize(PB - PA);
   #local Len = VDist(PA, PB);
   #local SA = atan2(RB - RA, Len);

   #if(UseMerge)
      merge {
   #else
      union {
   #end
      #local R1 = RA - EdgeRadius*tan(pi/4 - SA/2);
      #local R2 = RB - EdgeRadius/tan(pi/4 - SA/2);

      torus {R1, EdgeRadius
         Point_At_Trans(Axis) translate PA + Axis*EdgeRadius
      }
      torus {R2, EdgeRadius
         Point_At_Trans(Axis) translate PB - Axis*EdgeRadius
      }

      #local D1 = EdgeRadius - EdgeRadius*sin(SA);
      #local D2 = EdgeRadius + EdgeRadius*sin(SA);

      cone {
         PA + Axis*D1, R1 + EdgeRadius*cos(SA),
         PB - Axis*D2, R2 + EdgeRadius*cos(SA)
      }

      cone {PA, R1, PB, R2}
   }
#end


// Cones with spherical caps
// Sphere-capped cone object with spheres centered on end points.
// Derived from Connect() macro by John VanSickle
#macro Round_Cone2_Union(PtA, RadiusA, PtB, RadiusB)
   Round_Cone2(PtA, RadiusA, PtB, RadiusB, no)
#end
#macro Round_Cone2_Merge(PtA, RadiusA, PtB, RadiusB)
   Round_Cone2(PtA, RadiusA, PtB, RadiusB, yes)
#end
#macro Round_Cone2(PtA, RadiusA, PtB, RadiusB, UseMerge)
   #local Axis = PtB - PtA;
   #local RadDif = RadiusA - RadiusB;
   #local Len = VDist(PtA, PtB);

   #local D2 = f_sqr(Len) - f_sqr(RadDif);
   #if(D2<0)
     #error "Round_Cone2() macro called with parameters that can't be handled correctly"
   #end
   #local D2 = sqrt(D2);

   #if(UseMerge)
      merge {
   #else
      union {
   #end
      sphere {PtA, RadiusA}
      sphere {PtB, RadiusB}

      cone {
         PtA + Axis/Len*RadDif*RadiusA/Len, RadiusA*D2/Len,
         PtB + Axis/Len*RadDif*RadiusB/Len, RadiusB*D2/Len
      }
   }
#end

// Sphere-capped cone object with spheres moved and resized
// to fit ends of cone.
// The cone portion is identical to what you would get using
// a cone object with the same parameters, but the spheres are
// not centered on the endpoints of the cone, but are moved
// to give a smooth transition with the surface
#macro Round_Cone3_Union(PtA, RadiusA, PtB, RadiusB)
   Round_Cone3(PtA, RadiusA, PtB, RadiusB, no)
#end
#macro Round_Cone3_Merge(PtA, RadiusA, PtB, RadiusB)
   Round_Cone3(PtA, RadiusA, PtB, RadiusB, yes)
#end
#macro Round_Cone3(PtA, RadiusA, PtB, RadiusB, UseMerge)
   #local Axis = vnormalize(PtB - PtA);
   #local Len = VDist(PtA, PtB);
   #local SA = atan2(RadiusB - RadiusA, Len);

   #if(UseMerge)
      merge {
   #else
      union {
   #end
      cone {PtA, RadiusA, PtB, RadiusB}
      sphere {PtA + Axis*tan(SA)*RadiusA, RadiusA/cos(SA)}
      sphere {PtB + Axis*tan(SA)*RadiusB, RadiusB/cos(SA)}
   }
#end

// Two-triangle quad
//  A---B
//  |\  |
//  | \ |
//  |  \|
//  D---C
#macro Quad(A, B, C, D)
   triangle {A, B, C}
   triangle {A, C, D}
#end
#macro Smooth_Quad(A, NA, B, NB, C, NC, D, ND)
   smooth_triangle {A, NA, B, NB, C, NC}
   smooth_triangle {A, NA, C, NC, D, ND}
#end


// HF Macros author: Rune S. Johansen
// Optimizations by: Wlodzimierz ABX Skiba
// There are several HF macros in shapes.inc, which generate meshes in various shapes.
// See more information in the help file.

#macro HF_Square (Function,UseUVheight,UseUVtexture,Res,Smooth,FileName,MnExt,MxExt)
   #local WriteFile = (strlen(FileName) > 0);
   #local xRes = (< 1, 1>*Res).x;
   #local zRes = (< 1, 1>*Res).y;
   #local UVheight  = (UseUVheight=1);
   #local UVtex = (UseUVtexture=1);
   #local Smooth = (Smooth=1);

   #local Ext = MxExt-MnExt;

   // CALCULTION OF POINT GRID
   // Note that the grid extents one element further in all directions
   // if a smooth heightfield is calculated. This is to ensure correct
   // normal calculation later on.
   #local PArr = array[xRes+1+Smooth][zRes+1+Smooth]
   #local J = 1-Smooth;
   #while (J<xRes+1+Smooth)
      #local K = 1-Smooth;
      #while (K<zRes+1+Smooth)

         #local UV = <(J-1)/(xRes-1),0,(K-1)/(zRes-1)>;

         #local P  = (UV*Ext*<1,0,1> + MnExt);

         #if (UVheight)
            #local H = Function(UV.x, UV.z, 0);
         #else
            #local H = Function(P.x, P.y, P.z);
         #end

         #declare PArr[J][K] = P + H*Ext*y;

         #declare K = K+1;
      #end
      #declare J = J+1;
   #end

   HFCreate_()
#end

#macro HF_Sphere (Function,UseUVheight,UseUVtexture,Res,Smooth,FileName,Center,Radius,Depth)
   #local WriteFile = (strlen(FileName) > 0);
   #local xRes = (< 1, 1>*Res).x;
   #local zRes = (< 1, 1>*Res).y;
   #local UVheight  = (UseUVheight=1);
   #local UVtex = (UseUVtexture=1);
   #local Smooth = (Smooth=1);

   // CALCULTION OF POINT GRID
   // Note that the grid extents one element further in all directions
   // if a smooth heightfield is calculated. This is to ensure correct
   // normal calculation later on.
   #local PArr = array[xRes+1+Smooth][zRes+1+Smooth]
   #local J = 1-Smooth;
   #while (J<xRes+1+Smooth)
      #local K = 1-Smooth;
      #while (K<zRes+1+Smooth)

         #local UV = <(J-1)/(xRes-1),0,(K-1)/(zRes-1)>;

         #local Dir = vrotate( vrotate(x,(-89.9999+179.9998*UV.z)*z), -360*UV.x*y );
         #local P  = Center + Dir * Radius;

         #if (UVheight)
            #local H = Function(UV.x, UV.z, 0);
         #else
            #local H = Function(P.x, P.y, P.z);
         #end

         #declare PArr[J][K] = P + H*Dir*Depth;

         #declare K = K+1;
      #end
      #declare J = J+1;
   #end

   HFCreate_()
#end

#macro HF_Cylinder (Function,UseUVheight,UseUVtexture,Res,Smooth,FileName,EndA,EndB,Radius,Depth)
   #local WriteFile = (strlen(FileName) > 0);
   #local xRes = (< 1, 1>*Res).x;
   #local zRes = (< 1, 1>*Res).y;
   #local UVheight  = (UseUVheight=1);
   #local UVtex = (UseUVtexture=1);
   #local Smooth = (Smooth=1);

   #local Axis = EndB-EndA;
   #local Base = VPerp_To_Vector(Axis);

   // CALCULTION OF POINT GRID
   // Note that the grid extents one element further in all directions
   // if a smooth heightfield is calculated. This is to ensure correct
   // normal calculation later on.
   #local PArr = array[xRes+1+Smooth][zRes+1+Smooth]
   #local J = 1-Smooth;
   #while (J<xRes+1+Smooth)
      #local K = 1-Smooth;
      #while (K<zRes+1+Smooth)

         #local UV = <(J-1)/(xRes-1),0,(K-1)/(zRes-1)>;

         #local Dir = vaxis_rotate(Base,Axis,-360*UV.x-90);
         #local P  = EndA+Axis*UV.z+Dir*Radius;

         #if (UVheight)
            #local H = Function(UV.x, UV.z, 0);
         #else
            #local H = Function(P.x, P.y, P.z);
         #end

         #declare PArr[J][K] = P + H*Dir*Depth;

         #declare K = K+1;
      #end
      #declare J = J+1;
   #end

   HFCreate_()
#end

#macro HF_Torus (Function,UseUVheight,UseUVtexture,Res,Smooth,FileName,Major,Minor,Depth)
   #local WriteFile = (strlen(FileName) > 0);
   #local xRes = (< 1, 1>*Res).x;
   #local zRes = (< 1, 1>*Res).y;
   #local UVheight  = (UseUVheight=1);
   #local UVtex = (UseUVtexture=1);
   #local Smooth = (Smooth=1);

   // CALCULTION OF POINT GRID
   // Note that the grid extents one element further in all directions
   // if a smooth heightfield is calculated. This is to ensure correct
   // normal calculation later on.
   #local PArr = array[xRes+1+Smooth][zRes+1+Smooth]
   #local J = 1-Smooth;
   #while (J<xRes+1+Smooth)
      #local K = 1-Smooth;
      #while (K<zRes+1+Smooth)

         #local UV = <(J-1)/(xRes-1),0,(K-1)/(zRes-1)>;

         #local Dir = vrotate(vrotate(-x,360*UV.z*z),-360*UV.x*y);
         #local P  = vrotate(Major*x,-360*UV.x*y)+Dir*Minor;

         #if (UVheight)
            #local H = Function(UV.x, UV.z, 0);
         #else
            #local H = Function(P.x, P.y, P.z);
         #end

         #declare PArr[J][K] = P + H*Dir*Depth;

         #declare K = K+1;
      #end
      #declare J = J+1;
   #end

   HFCreate_()
#end

// Internal macro - not intended to be called by user.
#macro HFCreate_ ()

   #if(WriteFile)
      #fopen _HFMACRO_OUTPUT_FILE FileName write
      #write(_HFMACRO_OUTPUT_FILE,"mesh2 {\nvertex_vectors {\n",xRes*zRes,
   #else
      mesh2 {vertex_vectors{xRes*zRes,
   #end

   #local J = 1;
   #while (J<=xRes)
      #local K = 1;
      #while (K<=zRes)
        #if(WriteFile)
          ",\n",PArr[J][K],
        #else
          PArr[J][K],
        #end
         #declare K = K+1;
      #end
      #declare J = J+1;
   #end

   #if(WriteFile)
      "\n}\n")
   #else
      }
   #end

   #if (Smooth)
      #if(WriteFile)
         #write(_HFMACRO_OUTPUT_FILE,"normal_vectors {\n",xRes*zRes,
      #else
         normal_vectors{xRes*zRes,
      #end

      // CALCULATION OF NORMAL VECTOR
      // We don't vnormalize the vectors from the current center point
      // to its neightbor points because we want a weighted average
      // where bigger areas contribute more. This also means that the
      // center point can be left out completely of the calculations:
      #local J = 1;
      #while (J<=xRes)
         #local K = 1;
         #while (K<=zRes)
           #if(WriteFile)
             ",\n",vnormalize(vcross(PArr[J][K+1]-PArr[J][K-1], PArr[J+1][K]-PArr[J-1][K])),
           #else
             vnormalize(vcross(PArr[J][K+1]-PArr[J][K-1], PArr[J+1][K]-PArr[J-1][K])),
           #end
            #declare K = K+1;
         #end
         #declare J = J+1;
      #end
      #if(WriteFile)
         "\n}\n")
      #else
         }
      #end
   #end

   #if (UVtex)
      #if(WriteFile)
         #write(_HFMACRO_OUTPUT_FILE,"uv_vectors {\n",xRes*zRes,
      #else
         uv_vectors{xRes*zRes,
      #end
      #local J = 1;
      #while (J<=xRes)
         #local K = 1;
         #while (K<=zRes)
           #if(WriteFile)
             ",\n",<(J-1)/(xRes-1),(K-1)/(zRes-1)>,
           #else
             <(J-1)/(xRes-1),(K-1)/(zRes-1)>,
           #end
            #declare K = K+1;
         #end
         #declare J = J+1;
      #end
      #if(WriteFile)
         "\n}\n")
      #else
         }
      #end
   #end

   #if(WriteFile)
      #write(_HFMACRO_OUTPUT_FILE,"face_indices {\n",(xRes-1)*(zRes-1)*2,
   #else
      face_indices{(xRes-1)*(zRes-1)*2,
   #end
   #local F1 = <0,zRes,zRes+1>;
   #local F2 = <0,zRes+1,1>;
   #local J = 0;
   #while (J<xRes-1)
      #local A = J*zRes;
      #while (mod(A+1,zRes))
        #if(WriteFile)
          ",\n",F1+A,",\n",F2+A,
        #else
          F1+A, F2+A,
        #end
         #local A = A+1;
      #end
      #local J = J+1;
   #end
   #if (UVtex)
      #if(WriteFile)
         "\n}\nuv_mapping\n}")
         #fclose _HFMACRO_OUTPUT_FILE
      #else
         } uv_mapping}
      #end
   #else
      #if(WriteFile)
         "\n}\n}")
         #fclose _HFMACRO_OUTPUT_FILE
      #else
         }}
      #end
   #end

#end

#version SHAPES_INC_TEMP;
#end//shapes.inc
