1//    Persistence of Vision Ray Tracer version 3.5 Include File
2//    File: shapes.inc
3//    Last updated: 2001.8.13
4//    Description: This file contains macros for working with objects, as well
5//    as macros for creating special objects, such as bevelled text,
6//    height fields, and rounded shapes.
7
8#ifndef(SHAPES_INC_TEMP)
9#declare SHAPES_INC_TEMP = version;
10#version 3.5;
11
12#ifdef(View_POV_Include_Stack)
13   #debug "including shapes.inc\n"
14#end
15
16#include "shapes_old.inc"
17#include "consts.inc"
18#include "transforms.inc"
19#include "strings.inc"
20#include "math.inc"
21
22// These macros are just interfaces to the trace() function.
23// They return values through their parameters:
24// If an intersection is found, they return true and set
25// OPt to the intersection point, and ONorm to the normal.
26// Otherwise they return false, and do not modify OPt or ONorm.
27#macro Isect(Pt, Dir, Obj, OPt)
28   #local Norm = <0,0,0>;
29   #local IPt = trace(Obj, Pt, Dir, Norm);
30   #if (vlength(Norm) > 0)
31      #declare OPt = IPt;
32      #local Return=true;
33   #else
34      #local Return=false;
35   #end
36   (Return)
37#end
38#macro IsectN(Pt, Dir, Obj, OPt, ONorm)
39   #local Norm = <0,0,0>;
40   #local IPt = trace(Obj, Pt, Dir, Norm);
41   #if (vlength(Norm) > 0)
42      #declare OPt = IPt;
43      #declare ONorm = Norm;
44      #local Return=true;
45   #else
46      #local Return=false;
47   #end
48   (Return)
49#end
50
51
52// A shortcut for getting both min and max extents of an object
53#macro Extents(Obj, Min, Max)
54   #declare Min = min_extent(Obj);
55   #declare Max = max_extent(Obj);
56#end
57
58
59// shortcuts for using the CenterTrans and AlignTrans
60// macros with objects.
61#macro Center_Object(Object, Axis)
62   object {Object Center_Trans(Object, Axis)}
63#end
64
65#macro Align_Object(Object, Axis, Pt)
66   object {Object Align_Trans(Object, Axis, Pt)}
67#end
68
69
70// A simple beveled text macro. The parameters are:
71// Font: the name of the font file.
72// String: the text string the text object is composed of.
73// Cuts: the number of times excess material is cut off, to form the bevel.
74//       More cuts will give smoother results, but take longer to render.
75// BevelAng: the angle of the bevel.
76// BevelDepth: the depth of the bevelled portion of the text.
77// Depth: the total depth of the text object.
78// Offset: the offset value for the text object.  Since the front faces of each
79//         letter need to be in the same plane, z values are ignored.
80#macro Bevelled_Text(Font, String, Cuts, BevelAng, BevelDepth, Depth, Offset, UseMerge)
81   #local BBox = text {ttf Font, String Depth, Offset*(x+y)}
82   #if(UseMerge)
83      merge {
84   #else
85      union {
86   #end
87      text {ttf Font, String Depth-BevelDepth, Offset*(x+y)}
88      intersection {
89         #local J=0;
90         #while(J<Cuts)
91            #local A = 2*pi*J/(Cuts);
92            #local CA = cos(radians(BevelAng));
93            #local SA = sin(radians(BevelAng));
94            text {ttf Font, String BevelDepth, Offset*(x+y)
95               translate -z*(BevelDepth+J*0.0001)
96               Shear_Trans(x, y, < cos(A)*SA, sin(A)*SA, CA>/CA)
97            }
98            #local J=J+1;
99         #end
100      }
101      translate z*BevelDepth
102      bounded_by {box {min_extent(BBox), max_extent(BBox)}}
103   }
104#end
105
106
107// Constants used for the text macros
108#declare Align_Left = 1;
109#declare Align_Right = 2;
110#declare Align_Center = 3;
111
112/* Text_Space( Font, String, Size, Spacing )
113Computes the width of a text string, including "white space". It
114returns the advance widths of all n letters. Text_Space gives the
115space a text or a glyph occupies in regard to its surroundings.
116
117Font:    The font to use (see the documentation for the text object)
118String:  The text for which we want to know the width
119Size:    The size to which the text should be scaled
120Spacing: The amount of space to add between the letters. */
121
122#macro Text_Space(Font, String, Size, Spacing)
123   #local TO = text {ttf Font concat("|",String,"|") 1 Spacing*x scale <Size,Size,1>}
124   #local SO = text {ttf Font "||"                   1 Spacing*x scale <Size,Size,1>}
125   ((max_extent(TO).x-min_extent(TO).x)-(max_extent(SO).x-min_extent(SO).x))
126#end
127
128/* Text_Width( Font, String, Size, Spacing )
129Computes the width of a text string. It returns the advance widths
130of the first n-1 letters, plus the glyph width of the last letter.
131Text_Width gives the "fysical" width of the text and if you use
132only one letter the "fysical" width of one glyph.
133
134Font:    The font to use (see the documentation for the text object)
135String:  The text for which we want to know the width
136Size:    The size to which the text should be scaled
137Spacing: The amount of space to add between the letters. */
138
139#macro Text_Width(Font, String, Size, Spacing)
140   #local TO = text {ttf Font String 1 Spacing*x scale <Size,Size,1>}
141   (max_extent(TO).x-min_extent(TO).x)
142#end
143
144// Circle_Text author: Ron Parker
145/* Circle_Text( Font, Text, Size, Spacing, Thickness, Radius, Inverted,
146                Justification, Angle )
147Creates a text object with the bottom (or top) of the character cells aligned
148with all or part of a circle.  This macro should be used inside an object{...}
149block.
150
151         Font: The font to use (see the documentation for the text object)
152         Text: The text string to be created
153         Size: The height of the text string, as you would use to scale a
154               standard text object
155      Spacing: The amount of space to add between the letters.
156    Thickness: The thickness of the letters (see the documentation for the
157               text object)
158       Radius: The radius of the circle along which the letters are aligned
159     Inverted: If this parameter is nonzero, the tops of the letters will
160               point toward the center of the circle.  Otherwise, the bottoms
161               of the letters will do so.
162Justification: One of the constants Align_Left, Align_Right, or Align_Center
163        Angle: The point on the circle from which rendering will begin.  The
164               +x direction is 0 and the +y direction is 90 (i.e. the angle
165               increases anti-clockwise. */
166
167#macro Circle_Text(F, T, S, Sp, Th, R, I, J, A)
168   #local FW = Text_Width(F, T, S, Sp);
169   #local TO = text {ttf F T 1 0 scale<S, S, 1>}
170   #local TH = max_extent(TO).y;
171   #local C = array[strlen(T)]
172   #if(FW > 2*pi*R)
173      #error concat("\n\n**** Text string \"", T, "\" is too long for a circle of the specified radius.\n\n\n")
174   #end
175   #local AW = -FW*180/pi/R;
176   #local SA = A;
177   #local EA = A + AW;
178   #if(((J = Align_Right) & !I)|((J = Align_Left) & I))
179      #local SA = A - AW;
180      #local EA = A;
181   #else
182      #if(J = Align_Center)
183         #local SA = A - AW/2;
184         #local EA = A + AW/2;
185      #end
186   #end
187
188   #local CI = 1;
189   #while(CI <= strlen(T))
190      #local OE = Text_Width(F, substr(T,CI,1), S, Sp);
191      #local LW = Text_Width(F, substr(T,1,CI), S, Sp) - OE;
192      #local LA = SA + AW*LW/FW + OE/2/FW*AW;
193      #if(I)
194         #local LA = EA - (LA - SA);
195      #end
196      #local TO = text {ttf F substr(T, CI, 1) Th 0 scale<S,S,1>}
197      #if(I)
198         #local C[CI-1] =
199         object {TO
200            rotate 180*z
201            translate <OE/2, TH, 0>
202            rotate -90*z
203            translate R*x
204            rotate LA*z
205         }
206      #else
207         #local C[CI-1] =
208         object {TO
209            translate -OE/2*x
210            rotate -90*z
211            translate R*x
212            rotate LA*z
213         }
214      #end
215      #local CI = CI + 1;
216   #end
217
218   // Create the final object, a union of individual text object letters.
219   union {
220      #local CI=0;
221      #while(CI < strlen(T))
222         object {C[CI]}
223         #local CI = CI + 1;
224      #end
225   }
226#end
227
228
229#macro Wedge(Angle)
230   #local A = clamp(Angle, 0, 360);
231   #if(A < 180)
232      difference {
233         plane {-x, 0}
234         plane {-x, 0 rotate y*A}
235      }
236   #else
237      #if(A = 180)
238         plane {-x, 0}
239      #else
240         intersection {
241            plane {x, 0}
242            plane {-x, 0 rotate y*A}
243            inverse
244         }
245      #end
246   #end
247#end
248
249
250#macro Spheroid(Center, Radius)
251   sphere { 0, 1 scale Radius translate Center }
252#end
253
254
255#macro Supertorus(RMj, RMn, MajorControl, MinorControl, Accuracy, MaxGradient)
256   #local CP = 2/MinorControl;
257   #local RP = 2/MajorControl;
258   isosurface {
259      function { pow( pow(abs(pow(pow(abs(x),RP) + pow(abs(z),RP), 1/RP) - RMj),CP) + pow(abs(y),CP) ,1/CP) - RMn }
260      threshold 0
261      contained_by {box {<-RMj-RMn,-RMn,-RMj-RMn>, < RMj+RMn, RMn, RMj+RMn>}}
262      #if(MaxGradient >= 1)
263         max_gradient MaxGradient
264      #else
265         evaluate 1, 10, 0.1
266      #end
267      accuracy Accuracy
268   }
269#end
270
271
272// Supercone author: Juha Nieminen
273// A cone object where each end is an ellipse, you specify two radii
274// for each end.
275// SuperCone function: (x^2/a^2+y^2/b^2-1)*(1-z) + (x^2/c^2+y^2/d^2-1)*z = 0
276//
277// camera { location <6,5,-10> look_at 0 angle 35 }
278// light_source { <100,100,-20>,1 }
279// plane { y,-1.5 pigment { checker rgb 1, rgb .5 } }
280// object { SuperCone(<0,-1.5,0>,1,2, <0,1.5,0>,1,.5)
281//     pigment { rgb x } finish { specular .5 }
282// }
283#macro Supercone(PtA, A, B, PtB, C, D)
284   intersection {
285      quartic {
286         <0, 0,  0,  0,  0,  0,  0,  B*B-2*B*D+D*D, 2*(B*D-B*B), B*B,
287         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
288         0,  0,  0, A*A-2*A*C+C*C, 2*(A*C-A*A), A*A, 0,  0,  0,  0,
289         -(A*A-2*A*C+C*C)*(B*B-2*B*D+D*D),
290         -(2*((B*D-B*B)*(A*A-2*A*C+C*C)+(A*C-A*A)*(B*B-2*B*D+D*D))),
291         -(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)),
292         -(2*(B*B*(A*C-A*A)+A*A*(B*D-B*B))), -A*A*B*B>
293         sturm
294      }
295      cylinder {0, z, max(max(abs(A), abs(B)), max(abs(C), abs(D)))}
296
297      bounded_by {cone {0, max(abs(A), abs(B)), z, max(abs(C), abs(D))}}
298
299      #local Dirv = PtB - PtA;
300      scale <1,1,vlength(Dirv)>
301      #local Dirv = vnormalize(Dirv);
302      #if(vlength(Dirv-<0,0,-1>)=0) scale <1,1,-1>
303      #else Reorient_Trans(z, Dirv)
304      #end
305      translate PtA
306   }
307#end
308
309
310// Connect two spheres with a cylinder.
311// Derived from Connect() macro by John VanSickle
312#macro Connect_Spheres(PtA, RadiusA, PtB, RadiusB)
313   #local Axis = PtB - PtA;
314   #local RadDif = RadiusA - RadiusB;
315   #local Len = VDist(PtA, PtB);
316   #local D2 = sqrt(f_sqr(Len) - f_sqr(RadDif));
317
318   cone {
319      PtA + Axis/Len*RadDif*RadiusA/Len, RadiusA*D2/Len,
320      PtB + Axis/Len*RadDif*RadiusB/Len, RadiusB*D2/Len
321   }
322#end
323
324
325#macro Wire_Box_Union(A, B, WireRadius)
326   Wire_Box(A, B, WireRadius, no)
327#end
328#macro Wire_Box_Merge(A, B, WireRadius)
329   Wire_Box(A, B, WireRadius, yes)
330#end
331#macro Wire_Box(A, B, WireRadius, UseMerge)
332   #local AA = <min(A.x, B.x), min(A.y, B.y), min(A.z, B.z)>;
333   #local BB = <max(A.x, B.x), max(A.y, B.y), max(A.z, B.z)>;
334
335   #local Delta=abs(BB.x-AA.x)/2;
336   #if (Delta<WireRadius)
337      #warning "\nWire_Box() macro called with x-size < Radius,\nresults may not be as expected\n"
338
339      #local AA = <AA.x+Delta, AA.y, AA.z>;
340      #local BB = <BB.x-Delta, BB.y, BB.z>;
341   #else
342      #local AA = <AA.x+WireRadius, AA.y, AA.z>;
343      #local BB = <BB.x-WireRadius, BB.y, BB.z>;
344   #end
345
346   #local Delta=abs(BB.y-AA.y)/2;
347   #if (Delta<WireRadius)
348      #warning "\nWire_Box() macro called with y-size < Radius,\nresults may not be as expected\n"
349
350      #local AA = <AA.x, AA.y+Delta, AA.z>;
351      #local BB = <BB.x, BB.y-Delta, BB.z>;
352   #else
353      #local AA = <AA.x, AA.y+WireRadius, AA.z>;
354      #local BB = <BB.x, BB.y-WireRadius, BB.z>;
355   #end
356
357   #local Delta=abs(BB.z-AA.z)/2;
358   #if (Delta<WireRadius)
359      #warning "\nWire_Box() macro called with z-size < Radius,\nresults may not be as expected\n"
360
361      #local AA = <AA.x, AA.y, AA.z+Delta>;
362      #local BB = <BB.x, BB.y, BB.z-Delta>;
363   #else
364      #local AA = <AA.x, AA.y, AA.z+WireRadius>;
365      #local BB = <BB.x, BB.y, BB.z-WireRadius>;
366   #end
367
368   #local LBF = AA;
369   #local RBF = < BB.x, AA.y, AA.z>;
370   #local RBB = < BB.x, AA.y, BB.z>;
371   #local LBB = < AA.x, AA.y, BB.z>;
372   #local LTF = < AA.x, BB.y, AA.z>;
373   #local RTF = < BB.x, BB.y, AA.z>;
374   #local RTB = BB;
375   #local LTB = < AA.x, BB.y, BB.z>;
376
377   #if(UseMerge)
378      merge {
379   #else
380      union {
381   #end
382      sphere {LBF, WireRadius}
383
384      #if (AA.x != BB.x)
385         sphere {RBF, WireRadius}
386      #end
387      #if ((AA.x != BB.x) & (AA.z != BB.z))
388         sphere {RBB, WireRadius}
389      #end
390      #if (AA.z != BB.z)
391         sphere {LBB, WireRadius}
392      #end
393
394      #if (AA.y != BB.y)
395         sphere {LTF, WireRadius}
396      #end
397      #if ((AA.x != BB.x) & (AA.y != BB.y))
398         sphere {RTF, WireRadius}
399      #end
400      #if ((AA.x != BB.x) & (AA.y != BB.y) & (AA.z != BB.z))
401         sphere {RTB, WireRadius}
402      #end
403      #if ((AA.y != BB.y) & (AA.z != BB.z))
404         sphere {LTB, WireRadius}
405      #end
406
407      #if (AA.x != BB.x)
408         cylinder {LBF, RBF, WireRadius}
409         cylinder {LBB, RBB, WireRadius}
410         cylinder {LTB, RTB, WireRadius}
411         cylinder {LTF, RTF, WireRadius}
412      #end
413
414      #if (AA.y != BB.y)
415         cylinder {LBF, LTF, WireRadius}
416         cylinder {RBF, RTF, WireRadius}
417         cylinder {RBB, RTB, WireRadius}
418         cylinder {LBB, LTB, WireRadius}
419      #end
420
421      #if (AA.z != BB.z)
422         cylinder {LTB, LTF, WireRadius}
423         cylinder {LBB, LBF, WireRadius}
424         cylinder {RTB, RTF, WireRadius}
425         cylinder {RBB, RBF, WireRadius}
426      #end
427   }
428#end
429
430#macro Round_Box_Union(A, B, EdgeRadius)
431   Round_Box(A, B, EdgeRadius, no)
432#end
433#macro Round_Box_Merge(A, B, EdgeRadius)
434   Round_Box(A, B, EdgeRadius, yes)
435#end
436#macro Round_Box(A, B, EdgeRadius, UseMerge)
437   #local AA = <min(A.x, B.x), min(A.y, B.y), min(A.z, B.z)>;
438   #local BB = <max(A.x, B.x), max(A.y, B.y), max(A.z, B.z)>;
439
440   #local Delta=abs(BB.x-AA.x)/2;
441   #if (Delta<EdgeRadius)
442      #warning "\nRound_Box() macro called with x-size < Radius,\nresults may not be as expected\n"
443
444      #local AA = <AA.x+Delta, AA.y, AA.z>;
445      #local BB = <BB.x-Delta, BB.y, BB.z>;
446   #else
447      #local AA = <AA.x+EdgeRadius, AA.y, AA.z>;
448      #local BB = <BB.x-EdgeRadius, BB.y, BB.z>;
449   #end
450
451   #local Delta=abs(BB.y-AA.y)/2;
452   #if (Delta<EdgeRadius)
453      #warning "\nRound_Box() macro called with y-size < Radius,\nresults may not be as expected\n"
454
455      #local AA = <AA.x, AA.y+Delta, AA.z>;
456      #local BB = <BB.x, BB.y-Delta, BB.z>;
457   #else
458      #local AA = <AA.x, AA.y+EdgeRadius, AA.z>;
459      #local BB = <BB.x, BB.y-EdgeRadius, BB.z>;
460   #end
461
462   #local Delta=abs(BB.z-AA.z)/2;
463   #if (Delta<EdgeRadius)
464      #warning "\nRound_Box() macro called with z-size < Radius,\nresults may not be as expected\n"
465
466      #local AA = <AA.x, AA.y, AA.z+Delta>;
467      #local BB = <BB.x, BB.y, BB.z-Delta>;
468   #else
469      #local AA = <AA.x, AA.y, AA.z+EdgeRadius>;
470      #local BB = <BB.x, BB.y, BB.z-EdgeRadius>;
471   #end
472
473   #local LBF = AA;
474   #local RBF = < BB.x, AA.y, AA.z>;
475   #local RBB = < BB.x, AA.y, BB.z>;
476   #local LBB = < AA.x, AA.y, BB.z>;
477   #local LTF = < AA.x, BB.y, AA.z>;
478   #local RTF = < BB.x, BB.y, AA.z>;
479   #local RTB = BB;
480   #local LTB = < AA.x, BB.y, BB.z>;
481
482   #if(UseMerge)
483      merge {
484   #else
485      union {
486   #end
487      sphere {LBF, EdgeRadius}
488
489      #if (AA.x != BB.x)
490         sphere {RBF, EdgeRadius}
491      #end
492      #if ((AA.x != BB.x) & (AA.z != BB.z))
493         sphere {RBB, EdgeRadius}
494      #end
495      #if (AA.z != BB.z)
496         sphere {LBB, EdgeRadius}
497      #end
498
499      #if (AA.y != BB.y)
500         sphere {LTF, EdgeRadius}
501      #end
502      #if ((AA.x != BB.x) & (AA.y != BB.y))
503         sphere {RTF, EdgeRadius}
504      #end
505      #if ((AA.x != BB.x) & (AA.y != BB.y) & (AA.z != BB.z))
506         sphere {RTB, EdgeRadius}
507      #end
508      #if ((AA.y != BB.y) & (AA.z != BB.z))
509         sphere {LTB, EdgeRadius}
510      #end
511
512      #if (AA.x != BB.x)
513         cylinder {LBF, RBF, EdgeRadius}
514         cylinder {LBB, RBB, EdgeRadius}
515         cylinder {LTB, RTB, EdgeRadius}
516         cylinder {LTF, RTF, EdgeRadius}
517      #end
518
519      #if (AA.y != BB.y)
520         cylinder {LBF, LTF, EdgeRadius}
521         cylinder {RBF, RTF, EdgeRadius}
522         cylinder {RBB, RTB, EdgeRadius}
523         cylinder {LBB, LTB, EdgeRadius}
524      #end
525
526      #if (AA.z != BB.z)
527         cylinder {LTB, LTF, EdgeRadius}
528         cylinder {LBB, LBF, EdgeRadius}
529         cylinder {RTB, RTF, EdgeRadius}
530         cylinder {RBB, RBF, EdgeRadius}
531      #end
532
533      box {AA-EdgeRadius*x, BB+EdgeRadius*x}
534      box {AA-EdgeRadius*y, BB+EdgeRadius*y}
535      box {AA-EdgeRadius*z, BB+EdgeRadius*z}
536   }
537#end
538
539#macro Round_Cylinder_Union(A, B, Radius, EdgeRadius)
540   Round_Cylinder(A, B, Radius, EdgeRadius, no)
541#end
542#macro Round_Cylinder_Merge(A, B, Radius, EdgeRadius)
543   Round_Cylinder(A, B, Radius, EdgeRadius, yes)
544#end
545#macro Round_Cylinder(A, B, Radius, EdgeRadius, UseMerge)
546
547   #if(UseMerge)
548      merge {
549   #else
550      union {
551   #end
552
553      #if(Radius<EdgeRadius)
554         #warning "\nRound_Cylinder() macro called with Radius < EdgeRadius,\nresults may not be as expected\n"
555
556         #local AA = A + vnormalize(B - A)*Radius;
557         #local BB = B + vnormalize(A - B)*Radius;
558
559         cylinder {AA, BB, Radius}
560         sphere {0, Radius translate AA }
561         sphere {0, Radius translate BB }
562
563      #else
564
565         #local AA = A + vnormalize(B - A)*EdgeRadius;
566         #local BB = B + vnormalize(A - B)*EdgeRadius;
567
568         cylinder {A, B, Radius - EdgeRadius}
569         cylinder {AA, BB, Radius}
570         torus {Radius - EdgeRadius, EdgeRadius translate y*EdgeRadius
571            Point_At_Trans(B - A)
572            translate A
573         }
574         torus {Radius - EdgeRadius, EdgeRadius translate y*(vlength(A - B) - EdgeRadius)
575            Point_At_Trans(B - A)
576            translate A
577         }
578
579      #end
580   }
581#end
582
583
584// Rounded cone with torus edges
585// This shape will fit entirely within a cone given the same parameters.
586#macro Round_Cone_Union(PtA, RadiusA, PtB, RadiusB, EdgeRadius)
587   Round_Cone(PtA, RadiusA, PtB, RadiusB, EdgeRadius, no)
588#end
589#macro Round_Cone_Merge(PtA, RadiusA, PtB, RadiusB, EdgeRadius)
590   Round_Cone(PtA, RadiusA, PtB, RadiusB, EdgeRadius, yes)
591#end
592#macro Round_Cone(PtA, RadiusA, PtB, RadiusB, EdgeRadius, UseMerge)
593   #if(min(RadiusA, RadiusB) < EdgeRadius)
594     #warning "\nRound_Cone() macro called with Radius < EdgeRadius,\nresults may not be as expected\n"
595   #end
596
597   #if(RadiusA > RadiusB)
598      #local RA = RadiusB;
599      #local RB = RadiusA;
600      #local PA = PtB;
601      #local PB = PtA;
602   #else
603      #local RA = RadiusA;
604      #local RB = RadiusB;
605      #local PA = PtA;
606      #local PB = PtB;
607   #end
608
609   #local Axis = vnormalize(PB - PA);
610   #local Len = VDist(PA, PB);
611   #local SA = atan2(RB - RA, Len);
612
613   #if(UseMerge)
614      merge {
615   #else
616      union {
617   #end
618      #local R1 = RA - EdgeRadius*tan(pi/4 - SA/2);
619      #local R2 = RB - EdgeRadius/tan(pi/4 - SA/2);
620
621      torus {R1, EdgeRadius
622         Point_At_Trans(Axis) translate PA + Axis*EdgeRadius
623      }
624      torus {R2, EdgeRadius
625         Point_At_Trans(Axis) translate PB - Axis*EdgeRadius
626      }
627
628      #local D1 = EdgeRadius - EdgeRadius*sin(SA);
629      #local D2 = EdgeRadius + EdgeRadius*sin(SA);
630
631      cone {
632         PA + Axis*D1, R1 + EdgeRadius*cos(SA),
633         PB - Axis*D2, R2 + EdgeRadius*cos(SA)
634      }
635
636      cone {PA, R1, PB, R2}
637   }
638#end
639
640
641// Cones with spherical caps
642// Sphere-capped cone object with spheres centered on end points.
643// Derived from Connect() macro by John VanSickle
644#macro Round_Cone2_Union(PtA, RadiusA, PtB, RadiusB)
645   Round_Cone2(PtA, RadiusA, PtB, RadiusB, no)
646#end
647#macro Round_Cone2_Merge(PtA, RadiusA, PtB, RadiusB)
648   Round_Cone2(PtA, RadiusA, PtB, RadiusB, yes)
649#end
650#macro Round_Cone2(PtA, RadiusA, PtB, RadiusB, UseMerge)
651   #local Axis = PtB - PtA;
652   #local RadDif = RadiusA - RadiusB;
653   #local Len = VDist(PtA, PtB);
654
655   #local D2 = f_sqr(Len) - f_sqr(RadDif);
656   #if(D2<0)
657     #error "Round_Cone2() macro called with parameters that can't be handled correctly"
658   #end
659   #local D2 = sqrt(D2);
660
661   #if(UseMerge)
662      merge {
663   #else
664      union {
665   #end
666      sphere {PtA, RadiusA}
667      sphere {PtB, RadiusB}
668
669      cone {
670         PtA + Axis/Len*RadDif*RadiusA/Len, RadiusA*D2/Len,
671         PtB + Axis/Len*RadDif*RadiusB/Len, RadiusB*D2/Len
672      }
673   }
674#end
675
676// Sphere-capped cone object with spheres moved and resized
677// to fit ends of cone.
678// The cone portion is identical to what you would get using
679// a cone object with the same parameters, but the spheres are
680// not centered on the endpoints of the cone, but are moved
681// to give a smooth transition with the surface
682#macro Round_Cone3_Union(PtA, RadiusA, PtB, RadiusB)
683   Round_Cone3(PtA, RadiusA, PtB, RadiusB, no)
684#end
685#macro Round_Cone3_Merge(PtA, RadiusA, PtB, RadiusB)
686   Round_Cone3(PtA, RadiusA, PtB, RadiusB, yes)
687#end
688#macro Round_Cone3(PtA, RadiusA, PtB, RadiusB, UseMerge)
689   #local Axis = vnormalize(PtB - PtA);
690   #local Len = VDist(PtA, PtB);
691   #local SA = atan2(RadiusB - RadiusA, Len);
692
693   #if(UseMerge)
694      merge {
695   #else
696      union {
697   #end
698      cone {PtA, RadiusA, PtB, RadiusB}
699      sphere {PtA + Axis*tan(SA)*RadiusA, RadiusA/cos(SA)}
700      sphere {PtB + Axis*tan(SA)*RadiusB, RadiusB/cos(SA)}
701   }
702#end
703
704// Two-triangle quad
705//  A---B
706//  |\  |
707//  | \ |
708//  |  \|
709//  D---C
710#macro Quad(A, B, C, D)
711   triangle {A, B, C}
712   triangle {A, C, D}
713#end
714#macro Smooth_Quad(A, NA, B, NB, C, NC, D, ND)
715   smooth_triangle {A, NA, B, NB, C, NC}
716   smooth_triangle {A, NA, C, NC, D, ND}
717#end
718
719
720// HF Macros author: Rune S. Johansen
721// Optimizations by: Wlodzimierz ABX Skiba
722// There are several HF macros in shapes.inc, which generate meshes in various shapes.
723// See more information in the help file.
724
725#macro HF_Square (Function,UseUVheight,UseUVtexture,Res,Smooth,FileName,MnExt,MxExt)
726   #local WriteFile = (strlen(FileName) > 0);
727   #local xRes = (< 1, 1>*Res).x;
728   #local zRes = (< 1, 1>*Res).y;
729   #local UVheight  = (UseUVheight=1);
730   #local UVtex = (UseUVtexture=1);
731   #local Smooth = (Smooth=1);
732
733   #local Ext = MxExt-MnExt;
734
735   // CALCULTION OF POINT GRID
736   // Note that the grid extents one element further in all directions
737   // if a smooth heightfield is calculated. This is to ensure correct
738   // normal calculation later on.
739   #local PArr = array[xRes+1+Smooth][zRes+1+Smooth]
740   #local J = 1-Smooth;
741   #while (J<xRes+1+Smooth)
742      #local K = 1-Smooth;
743      #while (K<zRes+1+Smooth)
744
745         #local UV = <(J-1)/(xRes-1),0,(K-1)/(zRes-1)>;
746
747         #local P  = (UV*Ext*<1,0,1> + MnExt);
748
749         #if (UVheight)
750            #local H = Function(UV.x, UV.z, 0);
751         #else
752            #local H = Function(P.x, P.y, P.z);
753         #end
754
755         #declare PArr[J][K] = P + H*Ext*y;
756
757         #declare K = K+1;
758      #end
759      #declare J = J+1;
760   #end
761
762   HFCreate_()
763#end
764
765#macro HF_Sphere (Function,UseUVheight,UseUVtexture,Res,Smooth,FileName,Center,Radius,Depth)
766   #local WriteFile = (strlen(FileName) > 0);
767   #local xRes = (< 1, 1>*Res).x;
768   #local zRes = (< 1, 1>*Res).y;
769   #local UVheight  = (UseUVheight=1);
770   #local UVtex = (UseUVtexture=1);
771   #local Smooth = (Smooth=1);
772
773   // CALCULTION OF POINT GRID
774   // Note that the grid extents one element further in all directions
775   // if a smooth heightfield is calculated. This is to ensure correct
776   // normal calculation later on.
777   #local PArr = array[xRes+1+Smooth][zRes+1+Smooth]
778   #local J = 1-Smooth;
779   #while (J<xRes+1+Smooth)
780      #local K = 1-Smooth;
781      #while (K<zRes+1+Smooth)
782
783         #local UV = <(J-1)/(xRes-1),0,(K-1)/(zRes-1)>;
784
785         #local Dir = vrotate( vrotate(x,(-89.9999+179.9998*UV.z)*z), -360*UV.x*y );
786         #local P  = Center + Dir * Radius;
787
788         #if (UVheight)
789            #local H = Function(UV.x, UV.z, 0);
790         #else
791            #local H = Function(P.x, P.y, P.z);
792         #end
793
794         #declare PArr[J][K] = P + H*Dir*Depth;
795
796         #declare K = K+1;
797      #end
798      #declare J = J+1;
799   #end
800
801   HFCreate_()
802#end
803
804#macro HF_Cylinder (Function,UseUVheight,UseUVtexture,Res,Smooth,FileName,EndA,EndB,Radius,Depth)
805   #local WriteFile = (strlen(FileName) > 0);
806   #local xRes = (< 1, 1>*Res).x;
807   #local zRes = (< 1, 1>*Res).y;
808   #local UVheight  = (UseUVheight=1);
809   #local UVtex = (UseUVtexture=1);
810   #local Smooth = (Smooth=1);
811
812   #local Axis = EndB-EndA;
813   #local Base = VPerp_To_Vector(Axis);
814
815   // CALCULTION OF POINT GRID
816   // Note that the grid extents one element further in all directions
817   // if a smooth heightfield is calculated. This is to ensure correct
818   // normal calculation later on.
819   #local PArr = array[xRes+1+Smooth][zRes+1+Smooth]
820   #local J = 1-Smooth;
821   #while (J<xRes+1+Smooth)
822      #local K = 1-Smooth;
823      #while (K<zRes+1+Smooth)
824
825         #local UV = <(J-1)/(xRes-1),0,(K-1)/(zRes-1)>;
826
827         #local Dir = vaxis_rotate(Base,Axis,-360*UV.x-90);
828         #local P  = EndA+Axis*UV.z+Dir*Radius;
829
830         #if (UVheight)
831            #local H = Function(UV.x, UV.z, 0);
832         #else
833            #local H = Function(P.x, P.y, P.z);
834         #end
835
836         #declare PArr[J][K] = P + H*Dir*Depth;
837
838         #declare K = K+1;
839      #end
840      #declare J = J+1;
841   #end
842
843   HFCreate_()
844#end
845
846#macro HF_Torus (Function,UseUVheight,UseUVtexture,Res,Smooth,FileName,Major,Minor,Depth)
847   #local WriteFile = (strlen(FileName) > 0);
848   #local xRes = (< 1, 1>*Res).x;
849   #local zRes = (< 1, 1>*Res).y;
850   #local UVheight  = (UseUVheight=1);
851   #local UVtex = (UseUVtexture=1);
852   #local Smooth = (Smooth=1);
853
854   // CALCULTION OF POINT GRID
855   // Note that the grid extents one element further in all directions
856   // if a smooth heightfield is calculated. This is to ensure correct
857   // normal calculation later on.
858   #local PArr = array[xRes+1+Smooth][zRes+1+Smooth]
859   #local J = 1-Smooth;
860   #while (J<xRes+1+Smooth)
861      #local K = 1-Smooth;
862      #while (K<zRes+1+Smooth)
863
864         #local UV = <(J-1)/(xRes-1),0,(K-1)/(zRes-1)>;
865
866         #local Dir = vrotate(vrotate(-x,360*UV.z*z),-360*UV.x*y);
867         #local P  = vrotate(Major*x,-360*UV.x*y)+Dir*Minor;
868
869         #if (UVheight)
870            #local H = Function(UV.x, UV.z, 0);
871         #else
872            #local H = Function(P.x, P.y, P.z);
873         #end
874
875         #declare PArr[J][K] = P + H*Dir*Depth;
876
877         #declare K = K+1;
878      #end
879      #declare J = J+1;
880   #end
881
882   HFCreate_()
883#end
884
885// Internal macro - not intended to be called by user.
886#macro HFCreate_ ()
887
888   #if(WriteFile)
889      #fopen _HFMACRO_OUTPUT_FILE FileName write
890      #write(_HFMACRO_OUTPUT_FILE,"mesh2 {\nvertex_vectors {",xRes*zRes,",\n",
891   #else
892      mesh2 {vertex_vectors{xRes*zRes,
893   #end
894
895   #local J = 1;
896   #while (J<=xRes)
897      #local K = 1;
898      #while (K<=zRes)
899         PArr[J][K],
900         #declare K = K+1;
901      #end
902      #declare J = J+1;
903   #end
904
905   #if(WriteFile)
906      "}\n")
907   #else
908      }
909   #end
910
911   #if (Smooth)
912      #if(WriteFile)
913         #write(_HFMACRO_OUTPUT_FILE,"normal_vectors {",xRes*zRes,",\n",
914      #else
915         normal_vectors{xRes*zRes,
916      #end
917
918      // CALCULATION OF NORMAL VECTOR
919      // We don't vnormalize the vectors from the current center point
920      // to its neightbor points because we want a weighted average
921      // where bigger areas contribute more. This also means that the
922      // center point can be left out completely of the calculations:
923      #local J = 1;
924      #while (J<=xRes)
925         #local K = 1;
926         #while (K<=zRes)
927            vnormalize(vcross(
928               PArr[J][K+1]-PArr[J][K-1],
929               PArr[J+1][K]-PArr[J-1][K]
930            )),
931            #declare K = K+1;
932         #end
933         #declare J = J+1;
934      #end
935      #if(WriteFile)
936         "}\n")
937      #else
938         }
939      #end
940   #end
941
942   #if (UVtex)
943      #if(WriteFile)
944         #write(_HFMACRO_OUTPUT_FILE,"uv_vectors {",xRes*zRes,",\n",
945      #else
946         uv_vectors{xRes*zRes,
947      #end
948      #local J = 1;
949      #while (J<=xRes)
950         #local K = 1;
951         #while (K<=zRes)
952            <(J-1)/(xRes-1),(K-1)/(zRes-1)>,
953            #declare K = K+1;
954         #end
955         #declare J = J+1;
956      #end
957      #if(WriteFile)
958         "}\n")
959      #else
960         }
961      #end
962   #end
963
964   #if(WriteFile)
965      #write(_HFMACRO_OUTPUT_FILE,"face_indices {",(xRes-1)*(zRes-1)*2,",\n",
966   #else
967      face_indices{(xRes-1)*(zRes-1)*2,
968   #end
969   #local F1 = <0,zRes,zRes+1>;
970   #local F2 = <0,zRes+1,1>;
971   #local J = 0;
972   #while (J<xRes-1)
973      #local A = J*zRes;
974      #while (mod(A+1,zRes))
975         F1+A, F2+A,
976         #local A = A+1;
977      #end
978      #local J = J+1;
979   #end
980   #if (UVtex)
981      #if(WriteFile)
982         "}\nuv_mapping\n}")
983         #fclose _HFMACRO_OUTPUT_FILE
984      #else
985         } uv_mapping}
986      #end
987   #else
988      #if(WriteFile)
989         "}\n}")
990         #fclose _HFMACRO_OUTPUT_FILE
991      #else
992         }}
993      #end
994   #end
995
996#end
997
998#version SHAPES_INC_TEMP;
999#end//shapes.inc
1000