++ed by:

2 PAUSE users

Kevin Ryde
and 1 contributors


Math::PlanePath -- points on a path through the 2-D plane


 use Math::PlanePath;
 # only a base class, see the subclasses for actual operation


This is the base class for some mathematical paths which turn an integer position $n into coordinates $x,$y. The current classes include

    SquareSpiral           four-sided spiral
    DiamondSpiral          four-sided spiral, looping faster
    PyramidSpiral          square base pyramid
    TriangleSpiral         equilateral triangle
    TriangleSpiralSkewed   equilateral skewed for compactness
    PentSpiralSkewed       five-sided spiral, compact
    HexSpiral              six-sided spiral
    HexSpiralSkewed        six-sided spiral skewed for compactness
    HeptSpiralSkewed       seven-sided spiral, compact
    SacksSpiral            quadratic on an Archimedean spiral
    VogelFloret            seeds in a sunflower
    TheodorusSpiral        unit steps at right angles
    KnightSpiral           an infinite knight's tour

    Rows                   fixed-width rows
    Columns                fixed-height columns
    Diagonals              diagonals between X and Y axes
    Corner                 expanding stripes around a corner
    PyramidRows            expanding rows pyramid
    PyramidSides           along the sides of a 45-degree pyramid

The paths are object oriented to allow parameters, though only a few subclasses actually have any parameters.

The classes are generally oriented towards integer $n positions and the classes designed for a square grid turn an integer $n into integer $x,$y. Usually they give in-between positions for fractional $n too. Classes not on a square grid, like SacksSpiral and VogelFloret, are scaled for a unit circle at each $n but they too can give in-between positions on request.

In general there's no parameters for scaling, or an offset for the 0,0 origin, or a reflection up or down. Those things are thought better done by a general coordinate transformer that might expand or invert for display. Even clockwise instead of counter-clockwise spiralling can be had just by negating $x (or negate $y to stay starting at the right), or a quarter turn by swapping $x and $y.

Loop Step

The paths can be characterized by how much longer each loop or repetition is than the preceding one. For example each cycle around the SquareSpiral is 8 longer than the preceding.

    Step      Path
    ----      ----
      0     Rows, Columns (fixed widths)
      1     Diagonals
      2     SacksSpiral, PyramidSides, Corner
      2     PyramidRows (default step parameter)
      4     DiamondSpiral
      5     PentSpiralSkewed
      6     HexSpiral, HexSpiralSkewed
      7     HeptSpiralSkewed
      8     SquareSpiral, PyramidSpiral
      9     TriangleSpiral, TriangleSpiralSkewed
     19.74  TheodorusSpiral (approaches 2*pi^2)
     32     KnightSpiral (counting the 2-wide loop)

The step determines which quadratic number sequences fall on straight lines. For example the gap between successive perfect squares increases by 2 each time (4 to 9 is +5, 9 to 16 is +7, 16 to 25 is +9, etc), so the perfect squares make a straight line in the paths of step 2.

A factor of 4 splits a straight line into two, so for example on the SquareSpiral of step 8 the perfect squares fall on two lines to the lower left and upper right. Effectively it's one line of the even squares (2k)^2 == 4*k^2 and another of the odd squares (2k+1)^2 == 4*k^2+4*k+1. The gap between successive even squares increases by 8 each time and likewise the odd squares.


$path = Math::PlanePath::Foo->new (key=>value, ...)

Create and return a new path object. Optional key/value parameters may control aspects of the object. Foo here is one of the various subclasses, see the list under "SEE ALSO".

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

Return x,y coordinates of point $n on the path. If there's no point $n then the return is an empty list, so for example

    my ($x,$y) = $path->n_to_xy (-123)
      or next;   # likely no negatives in $path

Currently all paths start from N=1, though some will give a position for N=0 or N=0.5 too.

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

    my $n = $path->xy_to_n(20,20);
    if (! defined $n) {
      next;   # nothing at this x,y

$x and $y can be fractional and the path classes will give an integer $n which contains $x,$y within a unit square, circle, or intended figure centred on that $n.

For paths which completely tile the plane there's always an $n to return, but for the spread-out paths an $x,$y position may fall in between (no $n close enough).

($n_lo, $n_hi) = $path->rect_to_n_range ($x1,$y1, $x2,$y2)

Return a range of N values which occur in a rectangle with corners at $x1,$y1 and $x2,$y2. The range is inclusive, so for instance

     my ($n_lo, $n_hi) = $path->rect_to_n_range (-5,-5, 5,5);
     foreach my $n ($n_lo .. $n_hi) {
         my ($x, $y) = $path->n_to_xy ($n) or next;
         print "$n  $x,$y";

Note the return may be an over-estimate, and of course some of the points between $n_lo and $n_hi may go outside the rectangle. $n_hi is usually no more than an extra partial row or revolution. $n_lo is often just the starting point 1, which is correct if you always want the origin 0,0, but a rectangle away from the origin could start higher.

$x1,$y1 and $x2,$y2 can be fractional and if they partly overlap some N figures then those N's are included in the return. If there's no points in the rectangle then the return is a "crossed" range like $n_lo=1, $n_hi=0 (which makes a foreach do no loops).

$bool = $path->x_negative
$bool = $path->y_negative

Return true if the path extends into negative X coordinates and/or negative Y coordinates respectively.

$str = $path->figure

Return the name of the figure (shape) intended to be drawn at each $n position. Currently this is one of

    square         side 1 centred on $x,$y
    circle         diameter 1 centred on $x,$y

Of course this is only a suggestion as PlanePath doesn't draw anything itself. A figure like a diamond for instance would work well too.


Math::PlanePath::SquareSpiral, Math::PlanePath::DiamondSpiral, Math::PlanePath::PentSpiralSkewed, Math::PlanePath::HexSpiral, Math::PlanePath::HexSpiralSkewed, Math::PlanePath::HeptSpiralSkewed, Math::PlanePath::SacksSpiral, Math::PlanePath::VogelFloret, Math::PlanePath::TheodorusSpiral, Math::PlanePath::KnightSpiral

Math::PlanePath::Rows, Math::PlanePath::Columns, Math::PlanePath::Diagonals, Math::PlanePath::Corner, Math::PlanePath::PyramidRows, Math::PlanePath::PyramidSides

examples/numbers.pl in the sources to print all the paths.




Math-PlanePath is Copyright 2010 Kevin Ryde

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