Povray code-macros.inc

```
/\* This program is free software: you can redistribute it and/or modify it
 \* under the terms of the GNU General Public License as published by the Free
 \* Software Foundation, either version 3 of the License, or (at your option)
 \* any later version.
 \*
 \* This program is distributed in the hope that it will be useful, but WITHOUT
 \* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 \* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 \* more details.
 \*
 \* You should have received a copy of the GNU General Public License along with
 \* this program. If not, see <http://www.gnu.org/licenses/>
 \*/

/\* Useful macros \*/
#ifndef (MACROS\_INC)
#declare MACROS\_INC = 1;

// Avoid any errors due to undefined variables
#local FullFaces = array[1][1];
#local Faces = array[1][1];
#local FaceTextureIndex = array[1][1];
#local Phi = (1 + sqrt(5)) / 2;
#declare Phi2 = Phi \* Phi;
#declare Phi3 = Phi \* Phi2;

// Needed to triangulate the star polygons
#declare NextUnusedVertex = NumVertices;

/\* Triangulate convex polygons
 \* Dest: The array to split into
 \* DestOffset: The first slot to be used
 \* SrcOffset: The offset into FullFaces[][] where the polygons can be found
 \* NumPolygons: The number of polygons to triangulate
 \* NumSides: The order of the polygon
 \*/
#macro TriangulateConvex(Dest, DestOffset, SrcOffset, NumPolygons, NumSides)
 #for (F, 0, NumPolygons - 1, 1)
 #local Ofs = DestOffset + (NumSides - 2) \* F;
 #for (Slice, 0, NumSides - 3, 1)
 #declare Dest[Ofs + Slice][0] = FullFaces[SrcOffset + F][0];
 #declare Dest[Ofs + Slice][1] = FullFaces[SrcOffset + F][Slice + 1];
 #declare Dest[Ofs + Slice][2] = FullFaces[SrcOffset + F][Slice + 2];
 #end
 #end
#end

/\* Create a string from a vertex
 \* Vertex: The veertex to stringify
 \*/
#macro VStr(Vertex)
 concat("<", str(Vertex.x, 12, 9), ", ", str(Vertex.y, 12, 9), ", ",
 str(Vertex.z, 12, 9), ">")
#end

/\* Set the textures for a range of faces
 \* Offset: First face to texturise
 \* NumTriangles: Number of triangles to texturise
 \* Texture: Texture of triangles
 \*/
#macro SetTextures(Offset, NumTriangles, Texture)
 #for (F, 0, NumTriangles - 1, 1)
 #declare FaceTextureIndex[Offset + F] = Texture;
 #end
#end

/\* Copy triangles from one array to Faces
 \* Source: The array to copy from
 \* Offset: The offset into Faces[] to place the triangles
 \* NumTriangles: The number of triangles to copy
 \*/
#macro CopyFaces(Source, Offset, NumTriangles)
 #for (F, 0, NumTriangles - 1, 1)
 #declare Faces[Offset + F][0] = Source[F][0];
 #declare Faces[Offset + F][1] = Source[F][1];
 #declare Faces[Offset + F][2] = Source[F][2];
 #end
#end

/\* Copy triangles from one array to Faces, and adds texture
 \* Source: The array to copy from
 \* Offset: The offset into Faces[] to place the triangles
 \* NumTriangles: The number of triangles to copy
 \* Texture: Texture to add
 \*/
#macro CopyWithTexture(Source, Offset, NumTriangles, Texture)
 CopyFaces(Source, Offset, NumTriangles)
 SetTextures(Offset, NumTriangles, Texture)
#end

// Global variable NumPieces gives the number of slices to triangulate a
// pentagram into
#ifdef (EvenOdd)
 #declare NumPieces5\_2 = 5;
#else
 #declare NumPieces5\_2 = 4;
#end

/\* Triangulate pentagrams
 \* Dest: The array to split the pentagrams into
 \* DeestOffset: The first slot to be used
 \* SrcOffset: The offset into FullFaces[][] where the pentagrams can be
 \* found
 \* NumPolygons: The number of pentagrams to split
 \*
 \* Global variable EvenOdd:
 \* Defined: Fill according to even-odd winding rule
 \* Undefined: Fill according to non-zero winding rule
 \*/
#macro Triangulate5\_2(Dest, DestOffset, SrcOffset, NumPolygons)
 #for (F, 0, NumPolygons - 1, 1)
 #local From = SrcOffset + F;
 #for (V, 0, 4, 1)
 #declare Vertices[NextUnusedVertex + V] =
 (Vertices[FullFaces[From][V]] +
 Vertices[FullFaces[From][mod(V + 1, 5)]] / Phi) / Phi;
 #end
 #local To = DestOffset + NumPieces5\_2 \* F;
 #ifdef (EvenOdd)
 #for (Split, 0, 4, 1)
 #declare Dest[To + Split][0] = nextUnusedVertex + mod(Split + 2, 5);
 #declare Dest[To + Split][1] = FullFaces[From][Split];
 #declare Dest[To + Split][2] = NextUnusedVertex + Split;
 #end
 #else
 #declare Dest[To][0] = FullFaces[From][0];
 #declare Dest[To][1] = FullFaces[From][1];
 #declare Dest[To][2] = NextUnusedVertex + 4;
 #for (Split, 1, 3, 1)
 #declare Dest[To + Split][0] = NextUnusedVertex + mod(Split + 3, 5);
 #declare Dest[To + Split][1] = FullFaces[From][Split + 1];
 #declare Dest[To + Split][2] = NextUnusedVertex + Split + 1;
 #end
 #end // ifdef EvenOdd
 #declare NextUnusedVertex = NextUnusedVertex + 5;
 #end
#end

#macro RegisterTri(Dest, Offset, P, Q, R)
 #declare Dest[Offset][0] = P;
 #declare Dest[Offset][1] = Q;
 #declare Dest[Offset][2] = R;
#end

#macro RegisterQuad(Dest, Offset, P, Q, R, S)
 RegisterTri(Dest, Offset , P, Q, R)
 RegisterTri(Dest, Offset + 1, P, S, R)
#end

// Global variable NumPieces gives the number of slices to triangulate an
// octagram into
#ifdef (EvenOdd)
 #declare NumPieces8\_3 = 22;
#else
 #declare NumPieces8\_3 = 10;
#end

/\* Triangulate octagrams
 \* Dest: The array to split the octagrams into
 \* DestOffset: The first slot to be used
 \* SrcOffset: The offset into FullFaces[][] where the octagrams can be
 \* found
 \* NumPolygons: The number of octagrams to split
 \*
 \* Global variable EvenOdd:
 \* Defined: Fill according to even-odd winding rule
 \* Undefined: Fill according to non-zero winding rule
 \*/
#macro Triangulate8\_3(Dest, DestOffset, SrcOffset, NumPolygons)
 #for (F, 0, NumPolygons - 1, 1)
 #local From = SrcOffset + F;
 #for (V, 0, 7, 1)
 #local V1 = Vertices[FullFaces[From][V]];
 #local V2 = Vertices[FullFaces[From][mod(V + 1, 8)]];
 #declare Vertices[NextUnusedVertex + 2 \* V] =
 V1 \* sqrt(2) / 2 + V2 \* (1 - sqrt(2) / 2);
 #declare Vertices[NextUnusedVertex + 2 \* V + 1] =
 V1 \* (2 - sqrt(2)) + V2 \* (sqrt(2) - 1);
 #end
 #local To = DestOffset + F \* NumPieces8\_3;
 #ifdef (EvenOdd)
 #for (V, 0, 7, 1)
 RegisterQuad(Dest, To + 2 \* V, FullFaces[From][V],
 NextUnusedVertex + 2 \* V,
 NextUnusedVertex + mod(2 \* V + 11, 16),
 NextUnusedVertex + mod(2 \* V + 10, 16))
 #end
 #for (V, 1, 6, 1)
 RegisterTri(Dest, To + 16 + V, NextUnusedVertex + 1,
 NextUnusedVertex + mod(6 \* V + 1, 16),
 NextUnusedVertex + mod(6 \* V + 7, 16))
 #end
 #else
 RegisterTri (Dest, To , FullFaces[From][0],
 FullFaces[From][1],
 NextUnusedVertex + 14)
 RegisterTri (Dest, To + 1, NextUnusedVertex + 2,
 FullFaces[From][4],
 NextUnusedVertex + 9)
 RegisterQuad(Dest, To + 2, FullFaces[From][7],
 NextUnusedVertex + 8,
 NextUnusedVertex + 9,
 NextUnusedVertex + 14)
 RegisterTri (Dest, To + 4, NextUnusedVertex + 14,
 FullFaces[From][2],
 NextUnusedVertex + 5)
 RegisterQuad(Dest, To + 5, FullFaces[From][5],
 NextUnusedVertex + 4,
 NextUnusedVertex + 5,
 NextUnusedVertex + 10)
 RegisterTri (Dest, To + 7, FullFaces[From][3],
 NextUnusedVertex,
 NextUnusedVertex + 7)
 RegisterQuad(Dest, To + 8, FullFaces[From][6],
 NextUnusedVertex + 6,
 NextUnusedVertex + 7,
 NextUnusedVertex + 12)
 #end // ifdef EvenOdd
 #declare NextUnusedVertex = NextUnusedVertex + 16;
 #end
#end

// Global variable NumPieces gives the number of slices to triangulate a
// decagram into
#ifdef (EvenOdd)
 #declare NumPieces10\_3 = 28;
#else
 #declare NumPieces10\_3 = 12;
#end

/\* Triangulate decagrams
 \* Dest: The array to split the decagrams into
 \* DestOffset: The first slot to be used
 \* SrcOffset: The offset into FullFaces[][] where the decagrams can be
 \* found
 \* NumPolygons: The number of pentagrams to split
 \*
 \* Global variable EvenOdd:
 \* Defined: Fill according to even-odd winding rule
 \* Undefined: Fill according to non-zero winding rule
 \*/
#macro Triangulate10\_3(Dest, DestOffset, SrcOffset, NumPolygons)
 #for (F, 0, NumPolygons - 1, 1)
 #local From = SrcOffset + F;
 #for (V, 0, 9, 1)
 #local V1 = Vertices[FullFaces[From][V]];
 #local V2 = Vertices[FullFaces[From][mod(V + 1, 10)]];
 #declare Vertices[NextUnusedVertex + 2 \* V] = 2 \* V1 / Phi2 + V2 / Phi3;
 #declare Vertices[NextUnusedVertex + 2 \* V + 1] = V1 / Phi + V2 / Phi2;
 #end
 #local To = DestOffset + F \* NumPieces10\_3;
 #ifdef (EvenOdd)
 #for (V, 0, 9, 1)
 RegisterQuad(Dest, To + 2 \* V, FullFaces[From][V],
 NextUnusedVertex + 2 \* V,
 NextUnusedVertex + mod(2 \* V + 7, 20),
 NextUnusedVertex + mod(2 \* V + 6, 16))
 #end
 #for (V, 1, 8, 1)
 #local Ofs = 14 \* V;
 RegisterTri(Dest, To + 20 + V, NextUnusedVertex + 1,
 NextUnusedVertex + mod(Ofs + 15, 20),
 NextUnusedVertex + mod(Ofs + 9, 20))
 #end
 #else
 RegisterQuad(Dest, To , FullFaces[From][0],
 FullFaces[From][1],
 FullFaces[From][2],
 NextUnusedVertex + 19)
 RegisterTri (Dest, To + 2, NextUnusedVertex + 2,
 FullFaces[From][8],
 NextUnusedVertex + 17)
 RegisterQuad(Dest, To + 3, FullFaces[From][5],
 NextUnusedVertex + 16,
 NextUnusedVertex + 17,
 NextUnusedVertex + 10)
 RegisterTri (Dest, To + 5, NextUnusedVertex + 4,
 FullFaces[From][9],
 NextUnusedVertex + 19)
 RegisterTri (Dest, To + 6, NextUnusedVertex + 18,
 FullFaces[From][6],
 NextUnusedVertex + 13)
 RegisterQuad(Dest, To + 7, FullFaces[From][3],
 NextUnusedVertex + 12,
 NextUnusedVertex + 13,
 NextUnusedVertex + 6)
 RegisterTri (Dest, To + 9, FullFaces[From][7],
 NextUnusedVertex,
 NextUnusedVertex + 15)
 RegisterQuad(Dest, To + 10, FullFaces[From][4],
 NextUnusedVertex + 14,
 NextUnusedVertex + 15,
 NextUnusedVertex + 8)
 #end // ifdef EvenOdd
 #declare NextUnusedVertex = NextUnusedVertex + 20;
 #end
#end

/\* Swap pairs of elements to do a basic sort
 \*
 \* Given an array with an even number of elements, this macro will try to
 \* get all the edge lengths equal to 2 by selectively swapping pairs of
 \* adjacent elements.
 \*
 \* Array: The array containing the elements
 \* Size: The number of elements in the array, assumed even
 \*/
#macro SortPairs(Array, Size)
 #local V0 = Array[0];
 vlength(Vertices[V0] - Vertices[Array[3]]) = 2)
 #declare Array[0] = Array[1];
 #declare Array[1] = V0;
 #end
 #local I = 0;
 #for (I, 2, Size - 2, 2)
 #if (vlength(Vertices[Array[I]] - Vertices[Array[I - 1]]) != 2)
 #local Temp = Array[I];
 #declare Array[I] = Array[I + 1];
 #declare Array[I + 1] = Temp;
 #end
 #end
#end

/\* Find the medial faces in P- & Q-type polyhedra
 \*
 \* Start: The first index into FullFaces to fill
 \* Quantity: The number of faces to find.
 \* Size: The number of sides of the polygon.
 \*/
#macro GetMedialPolygons(Start, Quantity, Size)
 #local UsedEdges = array[NumEdges];
 #for (E, 0, NumEdges - 1, 1)
 #declare UsedEdges[E] = false;
 #end

 #local LastUsedEdge = -1;
 #for (F, 0, Quantity - 1, 1)
 #local To = F + Start;
 #declare LastUsedEdge = LastUsedEdge + 1;
 #while (UsedEdges[LastUsedEdge])
 #declare LastUsedEdge = LastUsedEdge + 1;
 #end
 #local ThisEdge = LastUsedEdge;
 #declare UsedEdges[ThisEdge] = true;
 #local FirstVertex = Edges[ThisEdge][0];
 #local ThisVertex = Edges[ThisEdge][1];
 #local Dir = 1;
 #declare FullFaces[To][0] = ThisVertex;
 #for (V, 1, Size - 1, 1)
 #local NextEdge = -1;
 #local NextVertex = -1;
 #declare Dir = 1 - Dir;
 #for (E, LastUsedEdge + 1, NumEdges - 1, 1)
 #if (!UsedEdges[E] & Edges[E][1 - Dir] = ThisVertex)
 #declare NextEdge = E;
 #declare NextVertex = Edges[E][Dir];
 #end
 #end
 #declare ThisEdge = NextEdge;
 #declare UsedEdges[ThisEdge] = true;
 #declare ThisVertex = NextVertex;
 #declare FullFaces[To][V] = ThisVertex;
 #end
 #if (ThisVertex != FirstVertex)
 #error "Polyhedron does not close."
 #end
 #declare FullFaceSizes[To] = Size;
 #end
#end

#end // ifndef (MACROS\_INC)

```