The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Math::PlanePath::Flowsnake -- self-similar path through hexagons

SYNOPSIS

 use Math::PlanePath::Flowsnake;
 my $path = Math::PlanePath::Flowsnake->new;
 my ($x, $y) = $path->n_to_xy (123);

DESCRIPTION

This path is an integer version of the flowsnake curve by William Gosper,

                         39----40----41                        8
                           \           \
          32----33----34    38----37    42                     7
            \           \        /     /
             31----30    35----36    43    47----48            6
                  /                    \     \     \
          28----29    17----16----15    44    46    49...      5
         /              \           \     \  /
       27    23----22    18----19    14    45                  4
         \     \     \        /     /
          26    24    21----20    13    11----10               3
            \  /                    \  /     /
             25     4---- 5---- 6    12     9                  2
                     \           \         /
                       3---- 2     7---- 8                     1
                           /
                    0---- 1                                  y=0

     x=-4 -3 -2 -1  0  1  2  3  4  5  6  7  8  9 10 11

The points are spread out on every second X coordinate to make little triangles with integer coordinates, per "Triangular Lattice" in Math::PlanePath.

The basic pattern is the seven points 0 to 6,

    4---- 5---- 6
     \           \
       3---- 2
           /
    0---- 1

This repeats at 7-fold increasing scale, with sub-sections rotated according to the edge direction, and the 1, 2 and 6 sub-sections in mirror image. The next level can be seen at the multiple of 7 points N=0,7,14,21,28,35,42,49.

                                  42
                      -----------    ---
                   35                   ---
       -----------                         ---
    28                                        49 ---
      ---
         ----                  14
             ---   -----------  |
                21              |
                               |
                              |
                              |
                        ---- 7
                   -----
              0 ---

Notice this is the same shape as the 0 to 6, but rotated by atan(1/sqrt(7)) = 20.68 degrees anti-clockwise. Each level rotates further and for example after about 18 levels it goes all the way around and back to the first quadrant.

The rotation doesn't mean it fills the plane though. The shape fattens as it curls around, but leaves a spiral gap beneath it (see "Arms" below).

Tiling

The base pattern corresponds to a tiling by hexagons as follows, with the "***" lines being the base figure.

                        .     .
                       / \   / \
                      /   \ /   \
                     .     .     .
                     |     |     |
                     |     |     |
                     4*****5*****6
                    /*\   / \   /*\
                   / * \ /   \ / * \
                  .   * .     .   * .
                  |   * |     |    *|
                  |    *|     |    *|
                  .     3*****2     7...
                   \   / \   /*\   /
                    \ /   \ / * \ /
                     .     . *   .
                     |     | *   |
                     |     |*    |
                     0*****1     .
                      \   / \   /
                       \ /   \ /
                        .     .

In the next level the parts corresponding to 1, 2 and 6 are mirrored because they have their hexagon to the right of the line segment, rather than to the left.

Arms

The optional arms parameter can give up to three copies of the flowsnake, each advancing successively. For example arms=>3 is as follows. Notice the N=3*k points are the plain curve, and N=3*k+1 and N=3*k+2 are rotated copies of it.

                                     51----48----45                 5
                                       \           \
                      ...   69----66    54----57    42              4
                        \     \     \        /     /
       28----25----22    78    72    63----60    39    33----30     3
         \           \     \  /                    \  /     /
          31----34    19    75    12----15----18    36    27        2
               /     /              \           \        /
       40----37    16     4---- 1     9---- 6    21----24           1
      /              \     \              /
    43    55----58    13     7     0---- 3    74----77---...    <- Y=0
      \     \     \     \  /                    \
       46    52    61    10     2     8----11    71----68          -1
         \  /     /              \  /     /           /
          49    64    70----73     5    14    62----65             -2
                  \  /     /           /     /
                   67    76    20----17    59    53----50          -3
                        /     /              \  /     /
                      ...   23    35----38    56    47             -4
                              \     \     \        /
                               26    32    41----44                -5
                                 \  /
                                  29                               -6

                                   ^
       -9 -8 -7 -6 -5 -4 -3 -2 -1 X=0 1  2  3  4  5  6  7  8  9

Essentially the flowsnake fills an ever expanding hexagon with one corner at the origin. In the following picture the plain curve fills "A" and there's room for two more arms to fill B and C, rotated 120 and 240 degrees respectively.

            *---*         
           /     \       
      *---*   A   *
     /     \     / 
    *   B   O---*  
     \     /     \ 
      *---*   C   *
           \     /
            *---*

But the sides of these "hexagons" are not straight lines, they're more like wild wiggly spiralling S shapes, and the endpoints rotate around (by the angle described above) at each level. But the opposite sides are symmetric, so they mesh perfectly and with three arms fill the plane.

Fractal

The flowsnake can also be thought of as successively subdividing line segments with suitably scaled copies of the 0 to 7 figure (or its reversal).

The code here could be used for that by taking points N=0 to N=7^level. The Y coordinates should be multiplied by sqrt(3) to make proper equilateral triangles, then a rotation and scaling to have the endpoint come out at X=1,Y=0 or wherever desired. With this the path is confined to a finite fractal boundary.

FUNCTIONS

See "FUNCTIONS" in Math::PlanePath for the behaviour common to all path classes.

$path = Math::PlanePath::Flowsnake->new ()
$path = Math::PlanePath::Flowsnake->new (arms => $a)

Create and return a new flowsnake path object.

The optional arms parameter gives between 1 and 3 copies of the curve successively advancing.

($x,$y) = $path->n_to_xy ($n)

Return the X,Y coordinates of point number $n on the path. Points begin at 0 and if $n < 0 then the return is an empty list.

Fractional positions give an X,Y position along a straight line between the integer positions.

FORMULAS

X,Y to N

The current approach is a slightly crude search for digits of N which will, or might come out as the target X,Y.

Starting at a high digit bigger than a bounding radius for the X,Y values are considered for that digit and those below it. When a digit gives a prospective X,Y further away than the bounding radius for the digits below then that digit can be rejected.

The bounding radius calculation is a bit of an over-estimate when ends up making it examine more points than strictly necessary. Oh, can the current code also does a full n_to_xy() for each prospective point, rather than taking a shortcut by adding-up or stepping through the "side"s represented by a digit at a given level.

Rectangle to N Range

The current code calculates an exact rect_to_n_range() by searching for the highest and lowest N which are in the rectangle.

The curve at a given level is bounded by the Gosper island shape but the wiggly sides make it difficult to calculate, so a bounding radius sqrt(7)^level, plus a bit, is used. The portion of the curve comprising a given set of high digits of N can be excluded if the N point is more than that radius away from the rectangle.

When a part of the curve is excluded it prunes a whole branch of the digits tree. When the lowest digit is reached then a check for that point being actually within the rectangle is made. The radius calculation is a bit rough, and since it doesn't even take into account the direction of the curve it's a rather large over-estimate, but it works.

The same sort of search can be applied to non-rectangular shapes, calculating a radial distance from the shape. The distance calculation doesn't have to be exact, it can work on something bounding the shape until the lowest digit is reached and an individual X,Y is being considered as an candidate high or low N bound.

SEE ALSO

Math::PlanePath, Math::PlanePath::FlowsnakeCentres, Math::PlanePath::GosperIslands

Math::PlanePath::KochCurve, Math::PlanePath::HilbertCurve, Math::PlanePath::PeanoCurve, Math::PlanePath::ZOrderCurve

"New Gosper Space Filling Curves" by Fukuda, Shimizu and Nakamura, on flowsnake variations in bigger hexagons (with wiggly sides too).

    http://kilin.clas.kitasato-u.ac.jp/museum/gosperex/343-024.pdf
      or if down then at archive.org
    http://web.archive.org/web/20070630031400/http://kilin.u-shizuoka-ken.ac.jp/museum/gosperex/343-024.pdf

LICENSE

Copyright 2010, 2011 Kevin Ryde

This file is part of Math-PlanePath.

Math-PlanePath 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, or (at your option) any later version.

Math-PlanePath 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 Math-PlanePath. If not, see <http://www.gnu.org/licenses/>.