++ed by:
EGOR BHANN

2 PAUSE users

Kevin Ryde
and 1 contributors

NAME

Math::PlanePath::AlternateTerdragon -- alternate terdragon curve

SYNOPSIS

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

DESCRIPTION

This is the alternate terdragon curve by Davis and Knuth,

    Chandler Davis and Donald Knuth, "Number Representations and Dragon Curves -- I", Journal Recreational Mathematics, volume 3, number 2 (April 1970), pages 66-81 and "Number Representations and Dragon Curves -- II", volume 3, number 3 (July 1970), pages 133-149.

    Reprinted with addendum in Knuth "Selected Papers on Fun and Games", 2010, pages 571--614. http://www-cs-faculty.stanford.edu/~uno/fg.html

Points are a triangular grid using every second integer X,Y as per "Triangular Lattice" in Math::PlanePath, beginning

                                 \   /       \   /
    Y=2                          14,17 --- 15,24,33 --
                                     \       /   \
                                       \   /       \   /
    Y=1          2 ------- 3,12 ---- 10,13,34 -- 32,35,38
                   \       /   \       /   \       /   \
                     \   /       \   /       \   /
    Y=0    0 -------- 1,4 ----- 5,8,11 ----- 9,36 ----
                                 /   \
                               /       \
    Y=-1                     6 --------- 7

           ^     ^     ^     ^     ^     ^     ^     ^
          X=0    1     2     3     4     5     6     7

A segment 0 to 1 is unfolded to

       2-----3
        \
         \
    0-----1

Then 0 to 3 is unfolded likewise, but the folds are the opposite way. Where 1-2 went on the left, for 3-6 goes to the right.

       2-----3                   2-----3
        \   /                     \   /
         \ /                       \ /
    0----1,4----5             0----1,4---5,8----9
               /                         / \
              /                         /   \
             6                         6-----7

Successive unfolds go alternate ways. Taking two unfold at a time is segment replacement by the 0 to 9 figure (rotated as necessary). The curve never crosses itself. Vertices touch at triangular corners. Points can be visited 1, 2 or 3 times.

The two triangles have segment 4-5 between. In general points to a level N=3^k have a single segment between two blobs, for example N=0 to N=3^6=729 below. But as the curve continues it comes back to put further segments there (and a single segment between bigger blobs).

                 * *
                * * * *
                 * * * *
              * * * * *   * *
             * * * * * * * * * *
              * * * * * * * * * *
             * * * * * * * * * *
              * * * * * * * * * * *
                 * * * * * * * * * *
        * *   * * * * * * * * * * *         * *
       * * * * * * * * * * * * *           * * * *
        * * * * * * * * * * * * *           * * * *
     * * * * * * * * * * * * * *   * *   * * * * *   * *
    O * * * * * * * * * * * * * * * * * * * * * * * * * * E
       * *   * * * * *   * *   * * * * * * * * * * * * * *
            * * * *           * * * * * * * * * * * * *
             * * * *           * * * * * * * * * * * * *
                * *         * * * * * * * * * * *   * *
                           * * * * * * * * * *
                            * * * * * * * * * * *
                               * * * * * * * * * *
                              * * * * * * * * * *
                               * * * * * * * * * *
                                  * *   * * * * *
                                       * * * *
                                        * * * *
                                           * *

The top boundary extent is at an angle +60 degrees and the bottom at -30 degrees,

       / 60 deg
      /
     /
    O-------------------
     --__
         --__ 30 deg

An even expansion level is within a rectangle with endpoint at X=2*3^(k/2),Y=0.

Arms

The curve fills a sixth of the plane and six copies rotated by 60, 120, 180, 240 and 300 degrees mesh together perfectly. The arms parameter can choose 1 to 6 such curve arms successively advancing.

For example arms => 6 begins as follows. N=0,6,12,18,etc is the first arm (the same shape as the plain curve above), then N=1,7,13,19 the second, N=2,8,14,20 the third, etc.

                  \         /             \           /
                   \       /               \         /
                --- 7,8,26 ----------------- 1,12,19 ---
                  /        \               /         \
     \           /          \             /           \          /
      \         /            \           /             \        /
    --- 3,14,21 ------------- 0,1,2,3,4,5 -------------- 6,11,24 ---
      /         \            /           \             /        \
     /           \          /             \           /          \
                  \        /               \         /
               ---- 9,10,28 ---------------- 5,16,23 ---
                  /        \               /         \
                 /          \             /           \

With six arms every X,Y point is visited three times, except the origin 0,0 where all six begin. Every edge between points is traversed once.

FUNCTIONS

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

$path = Math::PlanePath::AlternateTerdragon->new ()
$path = Math::PlanePath::AlternateTerdragon->new (arms => 6)

Create and return a new path object.

The optional arms parameter can make 1 to 6 copies of the curve, each arm 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.

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

Return the point number for coordinates $x,$y. If there's nothing at $x,$y then return undef.

The curve can visit an $x,$y up to three times. xy_to_n() returns the smallest of the these N values.

@n_list = $path->xy_to_n_list ($x,$y)

Return a list of N point numbers for coordinates $x,$y.

The origin 0,0 has arms_count() many N since it's the starting point for each arm. Other points have up to 3 Ns for a given $x,$y. If arms=6 then every even $x,$y except the origin has exactly 3 Ns.

Descriptive Methods

$n = $path->n_start()

Return 0, the first N in the path.

$dx = $path->dx_minimum()
$dx = $path->dx_maximum()
$dy = $path->dy_minimum()
$dy = $path->dy_maximum()

The dX,dY values on the first arm take three possible combinations, being 120 degree angles.

    dX,dY   for arms=1
    -----
     2, 0        dX minimum = -1, maximum = +2
    -1, 1        dY minimum = -1, maximum = +1
     1,-1

For 2 or more arms the second arm is rotated by 60 degrees so giving the following additional combinations, for a total six. This changes the dX minimum.

    dX,dY   for arms=2 or more
    -----
    -2, 0        dX minimum = -2, maximum = +2
     1, 1        dY minimum = -1, maximum = +1
    -1,-1
$sum = $path->sumxy_minimum()
$sum = $path->sumxy_maximum()

Return the minimum or maximum values taken by coordinate sum X+Y reached by integer N values in the path. If there's no minimum or maximum then return undef.

S=X+Y is an anti-diagonal. The first arm is entirely above a line 135deg -- -45deg, per the +60deg to -30deg extents shown above. Likewise the second arm which is to 60+60=120deg. They have sumxy_minimum = 0. More arms and all sumxy_maximum are unbounded so undef.

$diffxy = $path->diffxy_minimum()
$diffxy = $path->diffxy_maximum()

Return the minimum or maximum values taken by coordinate difference X-Y reached by integer N values in the path. If there's no minimum or maximum then return undef.

D=X-Y is a leading diagonal. The first arm is entirely right of a line 45deg -- -135deg, per the +60deg to -30deg extents shown above, so it has diffxy_minimum = 0. More arms and all diffxy_maximum are unbounded so undef.

Level Methods

($n_lo, $n_hi) = $path->level_to_n_range($level)

Return (0, 3**$level), or for multiple arms return (0, $arms * 3**$level + ($arms-1)).

There are 3^level segments in a curve level, so 3^level+1 points numbered from 0. For multiple arms there are arms*(3^level+1) points, numbered from 0 so n_hi = arms*(3^level+1)-1.

FORMULAS

Turn

At each point N the curve always turns 120 degrees either to the left or right, it never goes straight ahead. If N is written in ternary then the lowest non-zero digit at its position gives the turn. Positions are counted from 0 for the least significant digit and up from there.

   turn          ternary lowest non-zero digit
   -----     ---------------------------------------
   left      1 at even position or 2 at odd position
   right     2 at even position or 1 at odd position

The flip of turn at odd positions is the "alternating" in the curve.

   next turn         ternary lowest non-2 digit
   ---------    ---------------------------------------
     left       0 at even position or 1 at odd position
     right      1 at even position or 0 at odd position

Total Turn

The direction at N, ie. the total cumulative turn, is given by the 1 digits of N written in ternary.

    direction = 120deg * sum / +1  if digit=1 at even position
                             \ -1  if digit=1 at odd position

This is used mod 3 for n_to_dxdy().

X,Y to N

The current code is roughly the same as TerdragonCurve xy_to_n(), but with a conjugate (negate Y, reverse direction d) after each digit low to high.

X,Y Visited

When arms=6 all "even" points of the plane are visited. As per the triangular representation of X,Y this means

    X+Y mod 2 == 0        "even" points

OEIS

Sequences in Sloane's Online Encyclopedia of Integer Sequences related to the alternate terdragon include,

    A156595   next turn 0=left, 1=right (morphism)
    A189715   N positions of left turns
    A189716   N positions of right turns
    A189717   count right turns so far

SEE ALSO

Math::PlanePath, Math::PlanePath::TerdragonCurve

Math::PlanePath::DragonCurve, Math::PlanePath::AlternatePaper

HOME PAGE

http://user42.tuxfamily.org/math-planepath/index.html

LICENSE

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