Math::...eRings(3pm)

πŸ“¦
version 129 Β· 3 versions
, in debian bookworm, debian trixie, ubuntu jammy, ubuntu noble, ubuntu resolute, ubuntu stonking

bestie ok so like, this is Math::...eRings(3pm) from the User Contributed Perl Documentation, and it's giving... rings of multiples? let's go πŸ’…

✨ NAME ✨

Math::PlanePath::MultipleRings -- rings of multiples

🌸 SYNOPSIS 🌸

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

πŸ¦‹ DESCRIPTION πŸ¦‹

okay so this path? it literally puts points on concentric rings. Every ring has "step" many points more than the previous one, and the first ring is also "step" many. For example with the default step==6, you get this:

               24  23                    innermost ring  6
            25        22                 next ring      12
                 10                      next ring      18
         26   11     9  21  ...                    ringnum*step

       27  12   3  2   8  20  38

      28  13   4    1   7  19  37        <- Y=0

       29  14   5  6  18  36

         30   15    17  35
                 16
            31        24
               32  33

                 ^
                X=0

X,Y positions are not integers, except on the axes. The innermost ring like N=1to6 above has points 1 unit apart. Subsequent rings are a unit chord or unit radial, whichever ensures no overlap.

             step <= 6      unit spacing radially
             step >= 6      unit chords around the rings

For step=6 the two spacings are the same. Unit radial spacing ensures the X axis points N=1,7,19,37,etc shown above are 1 unit apart. Unit chord spacing ensures adjacent points such as N=7,8,0,etc don't overlap.

The layout is similar to the various spiral paths of corresponding step. For example step=6 is like the "HexSpiral", but rounded out to circles instead of a hexagonal grid. Similarly step=4 the "DiamondSpiral" or step=8 the "SquareSpiral".

The step parameter is also similar to the "PyramidRows" with the rows stretched around circles, but "PyramidRows" starts from a 1-wide initial row whereas for "MultipleRings" here the first is "step" many.

πŸ’« X Axis πŸ’«

The starting Nring=1,7,19,37 etc on the X axis for the default step=6 is 6*d*(d-1)/2 + 1, counting the innermost ring as d=1. In general Nring is a multiple of the triangular numbers d*(d-1)/2, plus 1,

           Nring = step*d*(d-1)/2 + 1

This is the centred polygonal numbers, being the cumulative count of points making concentric polygons or rings in the style of this path.

Straight line radials further around arise from adding multiples of d, so for example in step=6 shown above the line N=3,11,25,etc is Nring + 2*d. Multiples k*d with k>=step give lines which are in between the base ones from the innermost ring.

🌟 Step 1 🌟

For step=1 the first ring is 1 point and each subsequent ring has 1 further point.

                       24
                                      23
                    18       12    17
           25              8
                 13     5

           19     9     3  1  2  4  7 11 16 22     <- Y=0

                 14     6
           26             10
                    20       15    21
                                      28
                       27

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

The rings are

           polygon        radius     N values
           ------------   ------     --------
           single point     0         1
           two points       1         2, 3
           triangle         2         4, 5, 6
           square           3         7, 8, 9,10
           pentagon         4        11,12,13,14,15
           hexagon          5        16,17,18,19,20,21
            etc

The X axis as described above is the triangular numbers plus 1, ie. k*(k+1)/2 + 1.

πŸŽ€ Step 2 πŸŽ€

For step=2 the arrangement is roughly

                          34
                 35                33
                       24 15 23
           36 25                      22 32
                    16  9  4  8 14

           37 26 17 10  5  2  1  3  7 13 21 31

                    18 11  6 12 20
           38 27                      30 42
                       28 19 29
                 39                41
                          40

The pattern is similar to the "SacksSpiral" (see Math::PlanePath::SacksSpiral). In "SacksSpiral" each spiral loop is 2 more points than the previous the same as here, but the positioning differs. Here the X axis is the pronic numbers and the squares are to the left, whereas in "SacksSpiral" rotated around to squares on X axis and pronics to the left.

πŸ’Ž Ring Shape πŸ’Ž

Option "ring_shape => 'polygon'" puts the points on concentric polygons of "step" many sides, so each concentric polygon has 1 more point on each of its sides than the previous polygon. For example step=4 gives 4-sided polygons, ie. diamonds,

                         16
                       /    \                ring_shape=>'polygon', step=>4
                    17    7   15
                  /    /     \   \
               18    8    2    6   14
             /     /   /    \    \    \
           19   9    3         1    5   13
             \     \   \    /    /    /
               20   10    4   12   24
                  \    \    /    /
                    21   11   23
                       \    /
                         22

The polygons are scaled to keep points 1 unit apart. For step>=6 this means 1 unit apart sideways. step=6 is in fact a honeycomb grid where each points is 1 away from all six of its neighbours.

For step=3, 4 and 5 the polygon sides are 1 apart radially, as measured in the centre of each side. This makes points a little more than 1 apart along the sides. Squeezing them up to make the closest points exactly 1 apart is possible, but may require iterating a square root for each ring. step=3 squeezed down would in fact become a variable spacing with successively four close then one wider.

For step=2 and step=1 in the current code the default circle shape is used. Should that change? Is there a polygon style with 2 sides or 1 side?

The polygon layout is only a little different from a circle, but it lines up points on the sides and that might help show a structure for some sets of points plotted on the path.

🧚 Step 3 Pentagonals 🧚

For step=3 the pentagonal numbers 1,5,12,22,etc, P(k) = (3k-1)*k/2, are a radial going up to the left, and the second pentagonal numbers 2,7,15,26, S(k) = (3k+1)*k/2 are a radial going down to the left, respectively 1/3 and 2/3 the way around the circles.

As described in "Step 3 Pentagonals" in Math::PlanePath::PyramidRows, those P(k) and preceding P(k)-1, P(k)-2, and S(k) and preceding S(k)-1, S(k)-2 are all composites, so plotting the primes on a step=3 "MultipleRings" has two radial gaps where there's no primes.

πŸ”₯ FUNCTIONS πŸ”₯

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

  • $path = Math::PlanePath::MultipleRings->new (step => $integer) / $path = Math::PlanePath::MultipleRings->new (step => $integer, ring_shape => $str) β€” Create and return a new path object.

The "step" parameter controls how many points are added in each circle. It defaults to 6 which is an arbitrary choice and the suggestion is to always pass in a desired count.

  • ($x,$y) = $path->n_to_xy ($n) β€” Return the X,Y coordinates of point number $n on the path.

$n can be any value "$n >= 1" and fractions give positions on the rings in between the integer points. For "$n < 1" the return is an empty list since points begin at 1.

Fractional $n currently ends up on the circle arc between the integer points. Would straight line chords between them be better, reflecting the unit spacing of the points? Neither seems particularly important.

  • $n = $path->xy_to_n ($x,$y) β€” Return an integer point number for coordinates "$x,$y". Each integer N is considered the centre of a circle of diameter 1 and an "$x,$y" within that circle returns N.

The unit spacing of the points means those circles don't overlap, but they also don't cover the plane and if "$x,$y" is not within one then the return is "undef".

  • $str = $path->figure () β€” Return "circle".

✨ FORMULAS ✨

πŸ’– N to X,Y - Circle πŸ’–

okay bestie so, as we said up top, every ring starts at

Nring = step*d*(d-1)/2 + 1

and the way that works is you can flip this around to get the ring number d for any given N, then subtract Nring to get a remainder that tells you where you are inside the ring. The (N-1)/step bit is lowkey doing the triangular-number conversion for us. slay.

d = floor((sqrt(8*(N-1)/step + 1) + 1) / 2)
Nrem = N - Nring

Rings are sized so the points sit 1 unit apart β€” periodt. There are three cases, and you simply have to know which one you're in:

circle,  step<=6     unit radially on X axis
polygon, step<=6     unit radially on sides centre
         step>=7     unit chord between points

For the circle shape the integer points land on a circle, and fractional N sits on a straight line between those integer points. Which means it's a polygon too, bestie β€” just one with ever more sides, whereas ring_shape=polygon is a fixed step-many-sides situation. iykyk.

circle       numsides = d*step
polygon      numsides = step

The radial distance to a polygon corner is calculated as:

                           base               varying with d
----------------     ---------------------------------------
circle,  step<=6     0.5/sin(pi/step) + d-1
polygon, step<=6     0.5/sin(pi/step) + (d-1)/cos(pi/step)
circle,  step>=7     0                + 0.5/sin(pi/(d*step))
polygon, step>=7     0                + d * 0.5/sin(pi/step)

The step<=6 cases are an initial polygon of step-many unit sides, and then it's unit spacing d-1 for circle, or for polygon (d-1)/cos(pi/step) which is bigger and makes sure the middle of the sides have unit spacing radially. πŸ’…

And the 0.5/sin(pi/step) for the radius of a unit-sided polygon? it just falls out of this lil geometry moment:

          r      ___---*
           ___---      | 1/2 = half the polygon side
     ___--- alpha      |
    o------------------+

    alpha = (2pi/numsides) / 2 = pi/numsides
    sin(alpha) = (1/2) / base_r
    r = 0.5 / sin(pi/numsides)

The angle theta to a polygon vertex is literally just a full circle divided by numsides. no cap.

side = circle   Nrem
       polygon  floor(Nrem / step)
theta = side * (2pi / numsides)
vertex X = r * cos(theta)
       Y = r * sin(theta)

next_theta = (side+1) * (2pi / numsides)
next_vertex X = r * cos(next_theta)
            Y = r * sin(next_theta)

frac into side
f = circle   frac(Nrem)    = Nrem modulo 1
    polygon  Nrem - side*d = Nrem modulo d

X = vertex_X + f * (next_vertex_X - vertex_X)
Y = vertex_Y + f * (next_vertex_Y - vertex_Y)

So if Nrem is an integer for circle, or a multiple of d for polygon, then the vertex X,Y is the final X,Y β€” otherwise you're a fractional distance between the vertex X,Y and next vertex X,Y. πŸ¦‹

For a few cases X or Y are exact integers, and there's special-case code for those so that floating point rounding of pi doesn't give you tiny lil offsets away from the integers. iconic behaviour honestly.

For step=6 the base r is r=1 exactly, since the innermost ring is a little hexagon. Which means for the circle step=6 case the points on the X axis (positive and negative) are all integers X=1,2,3, etc. πŸŽ€

       P-----P
      /   1 / \ 1  <-- innermost points 1 apart
     /     /   \
    P     o-----P   <--  base_r = 1
     \      1  /
      \       /
       P-----P

If theta=pi, which is when 2*Nrem==d*step, then the point is on the negative X axis. Returning Y=0 exactly for that avoids sin(pi) giving some small non-zero due to rounding. πŸ™ˆ

If theta=pi/2 or theta=3pi/2, which is 4*Nrem==d*step or 4*Nrem==3*d*step, then N is on the positive or negative Y axis (respectively). Returning X=0 exactly avoids cos(pi/2) or cos(3pi/2) giving some small non-zero.

Points on the negative X axis occur when the step is even. Points on the Y axis occur when the step is a multiple of 4.

If theta=pi/4, 3*pi/4, 5*pi/4 or 7*pi/4, which is 8*Nrem==d*step, 3*d*step, 5*d*step or 7*d*step then the points are on the 45-degree lines X=Y or X=-Y. The current code doesn't try to ensure X==Y in these cases. The values are not integers and floating point rounding might mean sin(pi/4)!=cos(pi/4) resulting in X!=Y. πŸ’€ just so you know.


🌸 N to RSquared - Step 1 🌸

For step=1 the rings are point, line, triangle, square, pentagon, etc, with vertices at radius=numsides-1. For fractional N the triangle, square and hexagon cases are quadratics in the fraction part, which allows exact values from n_to_rsquared(). math girlies rise 🫢

           Ring                    R^2
---------------------     --------------
triangle   4 <= N < 7      4 - 12*f*(1-f)
square     7 <= N < 11     9 - 18*f*(1-f)
hexagon   16 <= N < 22    25 - 25*f*(1-f)

f = N - int(N)  fractional part of N

For example for the square at N=7.5 have f=0.5 and R^2=4.5 exactly. ✨ These quadratics arise because sine of 2pi/3, 2pi/4 and 2pi/6 are square roots, which on squaring up in R^2=X^2+Y^2 become integer factors for the fraction f along the polygon side.


🌈 OEIS 🌈

Entries in Sloane's Online Encyclopedia of Integer Sequences related to this path include

<http://oeis.org/A005448> (etc)

A005448 A001844 A005891 A003215 A069099     3 to 7
A016754 A060544 A062786 A069125 A003154     8 to 12
A069126 A069127 A069128 A069129 A069130    13 to 17
A069131 A069132 A069133                    18 to 20
    N on X axis of step=k, being the centred pentagonals

step=1
  A002024    Radius+1, runs of n repeated n times

step=8
  A090915    permutation N at X,-Y, mirror across X axis

πŸ’œ SEE ALSO πŸ’œ

  • Math::PlanePath
  • Math::PlanePath::SacksSpiral
  • Math::PlanePath::TheodorusSpiral
  • Math::PlanePath::PixelRings

🏠 HOME PAGE 🏠

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


πŸ“œ LICENSE πŸ“œ

Copyright 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 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/>.


okay that's a wrap bestie πŸ’– this one's perl v5.32.0, dated 2021-01-23, and it's the Math::...eRings(3pm) page β€” go forth and compute your rings, you absolute icon πŸ‘‘