POV-Ray : Newsgroups : povray.binaries.images : Penrose Tile Macros : Penrose Tile Macros Server Time
5 Nov 2024 18:22:01 EST (-0500)
  Penrose Tile Macros  
From: Jon Buller
Date: 22 Jun 2006 15:10:01
Message: <web.449aeaa42c4ebcd048362b170@news.povray.org>
Janet's image in
http://news.povray.org/povray.binaries.images/thread/%3Cweb.4494513cc659528fa30dd5a00%40news.povray.org%3E/
motivated me to clean up my Penrose macros and she wanted to see them, so
they are  coming in this thread, along with several demo images...
(Provided I can get all the software to work the way I want it to.)

The macros are pretty short, but the demonstration scene file is much longer
than it needs to be for any one image, because it has #if's all over it so
it can do them all at the same time.  (I should have made it generate each
image based on the clock, but I haven't done it in a long time and my first
guess at it was wrong.)

Note that the macros only generate tile type, placement, and orientation
information.  The scene file has all the information about actual shapes,
colors, etc.  In particular, notice that the Ammon Bars for the Dart tile
go outside the boundaries of the dart itself.

As for cloning Janet's image with my macros, I haven't done it, but it looks
like it is a Kite&Dart pattern with each tile broken in half and colored
seperately.  The colors look like they are a random function based on the
distance from the center point of the image.  Doing either of those things,
or both of them should not be difficult with these macros.

Let me know if you like (or don't like) them, and any changes you would like
(or have made).  Finally, please don't judge them based on the colors and
textures in the images, they can be described in no other way than "utterly
simplistic in the extreme".

Jon

----- Begin Penrose.inc -----
// Penrose tiling macros

// ---------- Penrose Constants ----------

#declare phi = (sqrt (5) + 1) / 2;        //                    ~= 1.61803
#declare c36 = phi / 2;     // = cos (    pi / 5) ~= 0.80902
#declare c72 = (phi - 1) / 2;    // = cos (2 * pi / 5) ~= 0.30902
#declare s36 = sqrt ((5 - sqrt (5)) / 8); // = sin (    pi / 5) ~= 0.58779
#declare s72 = sqrt ((5 + sqrt (5)) / 8); // = sin (2 * pi / 5) ~= 0.95106

// ---------- Macros to cache results of inflation ----------

// This isn't strictly necessary, but due to the way tiles overlap when
// the inflation routines operate on tiles, many duplicate tiles will be
// generated without doing this.  The procedure is simple, take the location
// of a new tile, and check the array entry for that location.  If false,
// the tile has not been done yet.  If true, it's already been done, so skip
it.
// This should be done at every level of the recursion to really be worth
while.

#declare inflate_cache_scale = phi + 1;

// Use this to initialize everything, the values define the rectangle where
// Penrose tiles will be allowed, level is the maximum level of recursion
// the inflation routines will be using.

#macro inflate_init (min_r, min_c, max_r, max_c, level)
  #declare inflate_minr = min_r;
  #declare inflate_minc = min_c;
  #declare inflate_maxr = max_r - min_r;
  #declare inflate_maxc = max_c - min_c;
  #local inflate_rs = ceil (inflate_cache_scale * inflate_maxr);
  #local inflate_cs = ceil (inflate_cache_scale * inflate_maxc);
  #declare inflate_done = array[inflate_rs][inflate_cs][level+1];
  #local row = 0;
  #while (row < inflate_rs)
    #local col = 0;
    #while (col < inflate_cs)
      #local lev = 0;
      #while (lev < level + 1)
        #declare inflate_done[row][col][lev] = false;
 #local lev = lev + 1;
      #end
      #local col = col + 1;
    #end
    #local row = row + 1;
  #end
#end

// This macro will determine if the tile has already been generated.  If it
hasn't
// been generated yet, call PENROSE_object to generate it.

#macro PENROSE_cache (pos, ang, level)
  #local row = floor (inflate_cache_scale * pos.x) - inflate_minr;
  #local col = floor (inflate_cache_scale * pos.z) - inflate_minc;
  #if (row > 0 & row < inflate_maxr & col > 0 & col < inflate_maxc)
    #if (inflate_done[row][col][level] = false)
      #declare inflate_done[row][col][level] = true;
      PENROSE_object (pos, ang, level)
    #end
  #end
#end

// ---------- Thick & Thin Rhomb macros ----------

// 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.

// Given the center and orientation of a thick tile,
// generate the corners of the tile and pass them to the
// "thick_corners" macro.

#macro PENROSE_thick_corners (pos, ang)
  #local major = c36 * vrotate (x,  ang       * y);
  #local minor = s36 * vrotate (x, (ang + 90) * y);
  PENROSE_object (pos + major, pos + minor, pos - major, pos - minor)
#end

// Inflate a thick rhomb.  The original rhomb should be centered
// at "pos", and be oriented in the direction "ang".

#macro thick (pos, ang, level)
  #macro PENROSE_object (pos, ang, level)
    #if (level < 1)
      PENROSE_thick (pos, ang)
    #else
      #local new = 2 * c36 * (pos + vrotate((c36 - 1 / (2 * c36)) * x, ang *
y));
      thick (new + vrotate (s36 * x, (ang +  54) * y), ang + 144, level - 1)
      thin  (new + vrotate (s72 * x, (ang + 126) * y), ang +  36, level - 1)
      thick (new + vrotate (c36 * x, (ang + 180) * y), ang + 180, level - 1)
      thin  (new + vrotate (s72 * x, (ang - 126) * y), ang -  36, level - 1)
      thick (new + vrotate (s36 * x, (ang -  54) * y), ang - 144, level - 1)
    #end
  #end
  PENROSE_cache (pos, ang, level)
#end

// Now do the same things for the "thin" rhomb.  Note the similarities.
// Differences should be in the locations of the corners, and the portion
// of the inflation routine that turns a single tile into multiple tiles.

#macro PENROSE_thin_corners (pos, ang)
  #local major = s72 * vrotate (x, (ang + 90) * y);
  #local minor = c72 * vrotate (x,  ang       * y);
  PENROSE_object (pos + minor, pos + major, pos - minor, pos - major)
#end

#macro thin (pos, ang, level)
  #macro PENROSE_object (pos, ang, level)
    #if (level < 1)
      PENROSE_thin (pos, ang)
    #else
      #local new = 2 * c36 * (pos + vrotate(c72 * x, ang * y));
      thick (new + vrotate (c36 * x, (ang + 108) * y), ang + 108, level - 1)
      thin  (new + vrotate (s72 * x, (ang + 162) * y), ang - 108, level - 1)
      thin  (new + vrotate (s72 * x, (ang - 162) * y), ang + 108, level - 1)
      thick (new + vrotate (c36 * x, (ang - 108) * y), ang - 108, level - 1)
    #end
  #end
  PENROSE_cache (pos, ang, level)
#end

// ---------- Kite & Dart macros ----------

// These two macros convert from Kites and Darts to Thick and Thin Rhombs.

#macro PENROSE_kite_rhombs (pos, ang, level)
  thick (pos + vrotate (s36 * x, (ang + 126) * y), ang - 144, level)
  thin  (pos + vrotate (c72 * x,  ang        * y), ang,       level)
  thick (pos + vrotate (s36 * x, (ang - 126) * y), ang + 144, level)
#end

#macro PENROSE_dart_rhombs (pos, ang, level)
  thick (pos + vrotate (s36 * x, (ang + 54) * y), ang + 144, level)
  thick (pos + vrotate (s36 * x, (ang - 54) * y), ang - 144, level)
#end

// These macros are for Kites and Darts.  They should look a whole lot
// like the Thick and Thin Rhomb macros, except the corners are a bit
different
// and the inflation itself is a bit different.  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 PENROSE_kite_corners (pos, ang)
  PENROSE_object (pos + vrotate (2 * c72 * x, ang * y),
                  pos + vrotate (<c72,0,-s72>, ang * y),
                  pos + vrotate (-x, ang * y),
                  pos + vrotate (<c72,0,s72>, ang * y))
#end

#macro kite (pos, ang, level)
  #macro PENROSE_object (pos, ang, level)
    #if (level < 2)
      PENROSE_kite (pos, ang)
    #else
      #local new = 2 * c36 * pos;
      kite (new + vrotate (2 * c72 * x, (ang +  72) * y), ang - 108, level -
1)
      dart (new + vrotate (          x, (ang + 144) * y), ang - 144, level -
1)
      dart (new + vrotate (          x, (ang - 144) * y), ang + 144, level -
1)
      kite (new + vrotate (2 * c72 * x, (ang -  72) * y), ang + 108, level -
1)
    #end
  #end
  PENROSE_cache (pos, ang, level)
#end

#macro PENROSE_dart_corners (pos, ang)
  PENROSE_object (pos,
                  pos + vrotate (<-c72,0,s72>, ang * y),
                  pos + vrotate (x, ang * y),
                  pos + vrotate (<-c72,0,-s72>, ang * y))
#end

#macro dart (pos, ang, level)
  #macro PENROSE_object (pos, ang, level)
    #if (level < 2)
      PENROSE_dart (pos, ang)
    #else
      #local new = 2 * c36 * pos;
      dart (new + vrotate (          x, (ang + 72) * y), ang + 144, level -
1)
      kite (new + vrotate (2 * c72 * x,  ang       * y), ang + 180, level -
1)
      dart (new + vrotate (          x, (ang - 72) * y), ang - 144, level -
1)
    #end
  #end
  PENROSE_cache (pos, ang, level)
#end
----- End Penrose.inc


Post a reply to this message


Attachments:
Download 'penrose0.png' (565 KB)

Preview of image 'penrose0.png'
penrose0.png


 

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.