// $Id: Penrose.inc,v 1.25 2006/07/06 06:13:04 jon Exp $ // Penrose tiling macros // ---------- Constants ---------- #declare phi = (sqrt (5) + 1) / 2; // ~= 1.61803 // Important identities: // phi * phi = phi + 1 // phi = 2 * cos (pi / 5) // 1 / phi = phi - 1 = 2 * sin (pi / 10) // 1 / (phi + 1) = 2 - phi #declare PENROSE_obj_ace = 0; #declare PENROSE_obj_batman = 1; #declare PENROSE_obj_dart = 2; #declare PENROSE_obj_deuce = 3; #declare PENROSE_obj_jack = 4; #declare PENROSE_obj_king = 5; #declare PENROSE_obj_kite = 6; #declare PENROSE_obj_long_bowtie = 7; #declare PENROSE_obj_queen = 8; #declare PENROSE_obj_rthick = 9; #declare PENROSE_obj_rthin = 10; #declare PENROSE_obj_short_bowtie = 11; #declare PENROSE_obj_slim1 = 12; #declare PENROSE_obj_slim2 = 13; #declare PENROSE_obj_star = 14; #declare PENROSE_obj_sun = 15; #declare PENROSE_obj_thick = 16; #declare PENROSE_obj_thin = 17; #declare PENROSE_obj_vert_a = 18; #declare PENROSE_obj_vert_b = 19; #declare PENROSE_obj_vert_c = 20; #declare PENROSE_obj_vert_d = 21; #declare PENROSE_obj_vert_e = 22; #declare PENROSE_obj_vert_f = 23; #declare PENROSE_obj_vert_g = 24; #declare PENROSE_obj_vert_h = 25; #declare PENROSE_obj_wide1 = 26; #declare PENROSE_obj_wide2 = 27; // ---------- Triangle Macros ---------- // These triangles are building blocks for the more traditional penrose // tiles. The reason to use these primitives instead is because the // inflation rules for the other systems overlap their neighbors. // This means that some duplicates will be generated. In many cases, // *a lot* of duplicates will be created. This wastes time and memory, // either in duplicate objects, or in code to find and eliminate the // duplicates. // There are two systems here that are duals of each other. The first // will break a slim triangle into 2 triangles, and a wide one into 3 // parts. These triangles should have have two equal length edges, the // third being shorter for slim triangles and longer for wide ones. The // triangles will both have the same area. // The triangle "edge pair" extend from the location "pos" in directions // of "ang + dir * x" and "ang - dir * x". (The value of x will be 18 // for a slim triangle, and 54 for a wide one.) An edge of positive // (or negative) dir will always be shared by a similar edge on the // adjcent triangle. // The macros call PENROSE_slim1 and PENROSE_wide1 to do the actual // dirty work of creating objects to render. They are provided // with the location, angle, and direction of the object to generate. // (The direction is for telling the left side of the triangle apart // from the right, it is either +1, or -1.) #macro slim1 (pos, ang, dir, level, context) #if (level < 1) PENROSE_slim1 (pos, ang, dir, context) #else #local new = phi * (pos + vrotate (x, (ang - dir * 18) * y)); #local ctx = PENROSE_context (pos, ang, dir, level, PENROSE_obj_slim1, 0, context); slim1 (new, ang + dir * 108, dir, level - 1, ctx) #local new = phi * pos + vrotate (x, (ang + dir * 18) * y); #local ctx = PENROSE_context (pos, ang, dir, level, PENROSE_obj_slim1, 1, context); wide1 (new, ang - dir * 108, dir, level - 1, ctx) #end #end #macro wide1 (pos, ang, dir, level, context) #if (level < 1) PENROSE_wide1 (pos, ang, dir, context) #else #local new = phi * pos + (phi - 1) * vrotate (x, (ang + dir * 54) * y); #local ctx = PENROSE_context (pos, ang, dir, level, PENROSE_obj_wide1, 0, context); wide1 (new, ang, -dir, level - 1, ctx) #local new = new + vrotate (x, (ang - dir * 54) * y); #local ctx = PENROSE_context (pos, ang, dir, level, PENROSE_obj_wide1, 1, context); wide1 (new, ang - dir * 144, dir, level - 1, ctx) #local ctx = PENROSE_context (pos, ang, dir, level, PENROSE_obj_wide1, 2, context); slim1 (new, ang + dir * 144, -dir, level - 1, ctx) #end #end // The second system will do splitting in the reverse manner of the first, // breaking slim triangles into 3 parts and wide ones into 2. For this // system, the longest edges of the triangles will be the same length, // meaning that the long side of a wide triangle will be the same length // as the two long sides of a slim one. Similarly, the two short sides // of the wide will be the same length as the short side of the slim. // This system does not share the property with the other system that // every edge has two triangles that label the edge the same way. #macro slim2 (pos, ang, dir, level, context) #if (level < 1) PENROSE_slim2 (pos, ang, dir, context) #else #local new = phi * pos + (phi - 1) * vrotate (x, (ang + dir * 18) * y); #local ctx = PENROSE_context (pos, ang, dir, level, PENROSE_obj_slim2, 0, context); wide2 (new, ang - dir * 108, dir, level - 1, ctx) #local new = phi * (pos + vrotate (x, (ang + dir * 18) * y)); #local ctx = PENROSE_context (pos, ang, dir, level, PENROSE_obj_slim2, 1, context); slim2 (new, ang - dir * 108, dir, level - 1, ctx) #local ctx = PENROSE_context (pos, ang, dir, level, PENROSE_obj_slim2, 2, context); slim2 (new, ang - dir * 144, -dir, level - 1, ctx) #end #end #macro wide2 (pos, ang, dir, level, context) #if (level < 1) PENROSE_wide2 (pos, ang, dir, context) #else #local new = phi * pos + vrotate (x, (ang - dir * 54) * y); #local ctx = PENROSE_context (pos, ang, dir, level, PENROSE_obj_wide2, 0, context); slim2 (new, ang + dir * 108, -dir, level - 1, ctx) #local new = phi * pos + (phi - 1) * vrotate (x, (ang + dir * 18) * y); #local ctx = PENROSE_context (pos, ang, dir, level, PENROSE_obj_wide2, 1, context); wide2 (new, ang + dir * 144, dir, level - 1, ctx) #end #end // ---------- Thick & Thin Rhomb macros ---------- // The more traditional Rhomb tiling can be made by ignoring all calls // to generate triangles where the type parameter is negative, and treating // the location as the vertex of a rhombus instead of one of the triangles // described above. // The Rhomb's sides are all of length 1. The thick rhomb has angles of // 72 and 108 degrees, the thin rhomb uses angles of 36 and 144 degrees // instead. To draw rhombs, take the three triangle points and add the // point "opposite" of "pos", and ignore any triangles with a negative // value for their typ. #declare s36 = sqrt ((5 - sqrt (5)) / 2); // = 2 * sin ( pi / 5) ~= 1.17557 #declare s72 = sqrt ((5 + sqrt (5)) / 2); // = 2 * sin (2 * pi / 5) ~= 1.90211 #macro thick (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 1, level, PENROSE_obj_thick, 0, context); wide1 (pos, ang, 1, level, ctx) #local ctx = PENROSE_context (pos, ang, -1, level, PENROSE_obj_thick, 1, context); wide1 (pos + vrotate (s36 * x, ang * y), ang + 180, -1, level, ctx) #end #macro thin (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 1, level, PENROSE_obj_thin, 0, context); slim1 (pos, ang, 1, level, ctx) #local ctx = PENROSE_context (pos, ang, -1, level, PENROSE_obj_thin, 1, context); slim1 (pos + vrotate (s72 * x, ang * y), ang + 180, -1, level, ctx) #end // Provide these macros so that nobody else will need to know about s36 // or s72, or how to use them. (As you can see, sprinkling this stuff // all over the place to rotate rhomb in place by 180 degrees would be // a pain...) #macro rthick (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_rthick, 0, context); thick (pos + s36 * vrotate (x, ang * y), ang - 180, level, ctx) #end #macro rthin (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_rthin, 0, context); thin (pos + s72 * vrotate (x, ang * y), ang - 180, level, ctx) #end // ---------- Kite & Dart macros ---------- // These macros are for Kites and Darts. They should look a whole lot // like the Thick and Thin Rhomb macros, except the tiles themselves // are a bit different, and it uses the "other" set of tile inflation // rules. On the other hand, by abstracting out some mundane thing // like PENROSE_cache, we don't have to duplicate all that stuff and // can concentrate on stuff that actually matters. #macro kite (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_kite, 0, context); slim2 (pos, ang + 18, 1, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_kite, 1, context); slim2 (pos, ang - 18, -1, level, ctx) #end #macro dart (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_dart, 0, context); wide2 (pos, ang + 54, 1, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_dart, 1, context); wide2 (pos, ang - 54, -1, level, ctx) #end // ---------- Rhomb Vertex Patterns ---------- #macro vert_a (pos, ang, level, context) #local i = 0; #while (i < 5) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_a, i, context); thick (pos + vrotate (x, (ang + i * 72) * y), ang + i * 72 - 126, level, ctx) #local i = i + 1; #end #end #macro vert_b (pos, ang, level, context) #local i = 0; #while (i < 5) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_b, i, context); thick (pos + vrotate (x, (ang + i * 72) * y), ang + i * 72 + 126, level, ctx) #local i = i + 1; #end #end #macro vert_c (pos, ang, level, context) #local i = 0; #while (i < 4) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_c, i, context); thick (pos + vrotate (x, (ang + i * 72 + 36) * y), ang + i * 72 + 162, level, ctx) #local i = i + 1; #end #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_c, 4, context); thin (pos, ang + 18, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_c, 5, context); rthin (pos, ang - 18, level, ctx) #end #macro vert_d (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_d, 0, context); thick (pos + vrotate (x, ang * y), ang + 126, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_d, 1, context); thin (pos, ang + 126, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_d, 2, context); rthin (pos, ang + 90, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_d, 3, context); thick (pos + vrotate (x, (ang + 144) * y), ang - 90, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_d, 4, context); rthin (pos, ang - 126, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_d, 5, context); thin (pos, ang - 90, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_d, 6, context); thick (pos + vrotate (x, (ang - 72) * y), ang + 54, level, ctx) #end #macro vert_e (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_e, 0, context); thick (pos + vrotate (x, (ang + 144) * y), ang - 90, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_e, 1, context); rthin (pos, ang - 126, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_e, 2, context); thick (pos, ang - 54, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_e, 3, context); thin (pos, ang + 126, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_e, 4, context); rthick (pos, ang + 54, level, ctx) #end #macro vert_f (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_f, 0, context); thick (pos + vrotate (x, (ang + 36) * y), ang - 90, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_f, 1, context); thick (pos + vrotate (x, (ang + 108) * y), ang - 18, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_f, 2, context); thick (pos + vrotate (x, (ang - 36) * y), ang - 162, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_f, 3, context); thin (pos + vrotate (x, (ang - 108) * y), ang + 90, level, ctx) #end #macro vert_g (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_g, 0, context); thick (pos, ang + 54, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_g, 1, context); thin (pos + vrotate (x, (ang + 108) * y), ang - 90, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_g, 2, context); rthick (pos, ang - 54, level, ctx) #end #macro vert_h (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_h, 0, context); thin (pos + vrotate (x, (ang + 144) * y), ang - 18, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_h, 1, context); thick (pos + vrotate (x, (ang - 144) * y), ang + 90, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_vert_h, 2, context); thin (pos + vrotate (x, ang * y), ang - 162, level, ctx) #end // ---------- Kite & Dart Vertex Patterns ---------- #macro sun (pos, ang, level, context) #local i = 0; #while (i < 5) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_sun, i, context); kite (pos, ang + i * 72, level, ctx) #local i = i + 1; #end #end #macro star (pos, ang, level, context) #local i = 0; #while (i < 5) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_star, i, context); dart (pos + (phi - 1 ) * vrotate (x, (ang + i * 72) * y), ang + i * 72 + 180, level, ctx) #local i = i + 1; #end #end #macro ace (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_ace, 0, context); kite (pos - vrotate (x, ang * y), ang - 36, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_ace, 1, context); dart (pos, ang, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_ace, 2, context); kite (pos - vrotate (x, ang * y), ang + 36, level, ctx) #end #macro deuce (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_deuce, 0, context); dart (pos + (phi - 1) * vrotate (x, (ang - 144) * y), ang + 144, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_deuce, 1, context); kite (pos + vrotate (x, (ang - 72) * y), ang + 108, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_deuce, 2, context); kite (pos + vrotate (x, (ang + 72) * y), ang - 108, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_deuce, 3, context); dart (pos + (phi - 1) * vrotate (x, (ang + 144) * y), ang - 144, level, ctx) #end #macro jack (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_jack, 0, context); kite (pos, ang - 144, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_jack, 1, context); dart (pos + (phi - 1) * vrotate (x, (ang - 72) * y), ang - 144, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_jack, 2, context); kite (pos + vrotate (x, ang * y), ang + 180, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_jack, 3, context); dart (pos + (phi - 1) * vrotate (x, (ang + 72) * y), ang + 144, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_jack, 4, context); kite (pos, ang + 144, level, ctx) #end #macro queen (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_queen, 0, context); kite (pos + vrotate (x, (ang - 36) * y), ang + 180, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_queen, 1, context); dart (pos + (phi - 1) * vrotate (x, ang * y), ang + 180, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_queen, 2, context); kite (pos + vrotate (x, (ang + 36) * y), ang + 180, level, ctx) #local new = pos + vrotate (x, (ang + 180) * y); #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_queen, 3, context); kite (new, ang - 36, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_queen, 4, context); kite (new, ang + 36, level, ctx) #end #macro king (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_king, 0, context); kite (pos, ang + 144, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_king, 1, context); kite (pos, ang - 144, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_king, 2, context); dart (pos + (phi - 1) * vrotate (x, (ang - 72) * y), ang + 108, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_king, 3, context); dart (pos + (phi - 1) * vrotate (x, ang * y), ang + 180, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_king, 4, context); dart (pos + (phi - 1) * vrotate (x, (ang + 72) * y), ang - 108, level, ctx) #end // ---------- Other Kite & Dart patterns ---------- #macro short_bowtie (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_short_bowtie, 0, context); ace (pos + vrotate (x, (ang + 36) * y), ang + 36, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_short_bowtie, 1, context); dart (pos + vrotate (x, (ang - 36) * y), ang - 36, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_short_bowtie, 2, context); kite (pos, ang - 72, level, ctx) #end #macro long_bowtie (pos, ang, level, context) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_long_bowtie, 0, context); dart (pos, ang, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_long_bowtie, 1, context); ace (pos + (phi - 1) * vrotate (x, (ang - 108) * y) + vrotate (x, (ang - 36) * y), ang - 36, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_long_bowtie, 2, context); ace (pos + (phi - 1) * vrotate (x, (ang + 108) * y) + vrotate (x, (ang + 36) * y), ang + 36, level, ctx) #end #macro batman (pos, ang, level, context) #local new = pos + (phi - 1) * vrotate (x, (ang + 180) * y); #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_batman, 5, context); jack (new, ang + 180, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_batman, 4, context); kite (new + vrotate (x, (ang - 72) * y) + vrotate (x, (ang - 36) * y), ang + 108, level, ctx) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_batman, 3, context); kite (new + vrotate (x, (ang + 72) * y) + vrotate (x, (ang + 36) * y), ang - 108, level, ctx) #local new = pos + vrotate (x, ang * y); #local i = 0; #while (i < 3) #local ctx = PENROSE_context (pos, ang, 0, level, PENROSE_obj_batman, i, context); dart (new + (phi - 1) * vrotate (x, (ang + i * 72 + 108) * y), ang + i * 72 - 72, level, ctx) #local i = i + 1; #end #end #macro decagon (pos, ang, level, ctx, which) #local sel = which; #local i = 0; #while (i < 10) #if (mod (sel, 2)) ace (pos + (phi + 1) * vrotate (x, (ang + 36 * i ) * y) + (phi - 1) * vrotate (x, (ang + 36 * i + 144) * y), ang + 36 * i - 36, level, ctx) #else ace (pos + (phi + 1) * vrotate (x, (ang + 36 * i + 36) * y) + (phi - 1) * vrotate (x, (ang + 36 * i - 108) * y), ang + 36 * i + 72, level, ctx) #end #local sel = floor (sel / 2); #local i = i + 1; #end #end #macro cartwheel (pos, ang, level, ctx, which) #local loc = pos + vrotate ((2 * phi + 1) * x, ang * y); #local sel = which; #local i = 0; #while (i < 10) #local dir = mod (sel, 2); sun (loc, ang + i * 36 + 144, level, ctx) dart (loc + vrotate (x, (ang + i * 36 + 180) * y), ang + i * 36 + 180, level, ctx) dart (loc + vrotate (x, (ang + i * 36 + 36) * y), ang + i * 36 + 36, level, ctx) dart (loc + vrotate (x, (ang + i * 36 - 36) * y), ang + i * 36 - 36, level, ctx) #if (mod (sel, 2)) long_bowtie (loc + phi * vrotate (x, (ang + i * 36 + 108) * y), ang + i * 36 - 72, level, ctx) #else long_bowtie (loc + vrotate (x, (ang + i * 36 + 108) * y), ang + i * 36 + 108, level, ctx) #end #local loc = loc + (phi + 1) * vrotate (x, (ang + i * 36 + 108) * y); #local sel = floor (sel / 2); #local i = i + 1; #end #end #macro short_cartwheel (pos, ang, level, ctx, which) #local loc = pos + (3 * phi + 2) * vrotate (x, ang * y); #local sel = which; #local i = 0; #while (i < 10) #local dir = mod (sel, 2); ace (loc + (phi - 1) * vrotate (x, (ang + i * 36 + 108) * y), ang + i * 36 - 72, level, ctx) ace (loc + (phi - 1) * vrotate (x, (ang + i * 36 + 180) * y), ang + i * 36, level, ctx) ace (loc + (phi - 1) * vrotate (x, (ang + i * 36 - 108) * y), ang + i * 36 + 72, level, ctx) short_bowtie (loc + (1 + (dir + 1) * phi) * (phi - 1) * vrotate (x, (ang + i * 36 + 108) * y), ang + i * 36 + 180 * dir + 108, level, ctx) #local loc = loc + (2 * phi + 1) * vrotate (x, (ang + i * 36 + 108) * y); #local sel = floor (sel / 2); #local i = i + 1; #end #end