povray/distribution/include/shapes.inc
2013-11-06 13:07:19 -05:00

1041 lines
31 KiB
PHP

// This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
// To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a
// letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
// Persistence of Vision Ray Tracer version 3.5 Include File
// File: shapes.inc
// Last updated: April-2013
// 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) //----------------------------------------
object{ Circle_Text_Valigned( F, // Font, i.e.: "arial.ttf",
T, // Text, i.e.: "POVRay",
S, // LetterSize, i.e.: 0.75,
Sp,// LetterSpacing, i.e.: 0.025,
Th,// Deepth, i.e.: 15.00,
R, // Radius, i.e.: 1.25
I, // Inverted, 0 or 1
J, // Justification: Align_Left, Align_Right, or Align_Center
A, // Circle angle
0 // Valign: Rotates for vertical objects.
// -90 = right side up, 90 = upside-down, 0 = horzontal.
) } //--------------------------------------------------------
#end //-------------------------------------------------------------- end macro Circle_Text
// Cicle_Text macro expanded by rotating the letters:
#macro Circle_Text_Valigned( F, // Font, i.e.: "arial.ttf",
T, // Text, i.e.: "POVRay",
S, // LetterSize, i.e.: 0.75,
Sp,// LetterSpacing, i.e.: 0.025,
Th,// Deepth, i.e.: 15.00,
R, // Radius, i.e.: 1.25
I, // Inverted, 0 or 1
J, // Justification: Align_Left, Align_Right, or Align_Center
A, // Circle angle
Valign// Valign: Rotates the letters. -90 = right side up, 90 = upside-down, 0 = horzontal.
) //----------------------------------------------------------------------------------------------
#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> rotate x*Valign}
#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// of macro --------------------------------------------- end of macro Circle_Text_Valigned
#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