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¶
This path puts points on concentric rings. Each ring has "step" many
points more than the previous and the first is also "step". For
example with the default step==6,
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,
ring_shape=>'polygon', step=>4
16
/ \
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".
N to X,Y - Circle¶
As per above, each ring begins at
Nring = step*d*(d-1)/2 + 1
This can be inverted to get the ring number d for a given N, and then subtract
Nring for a remainder into the ring. (N-1)/step in the formula effectively
converts into triangular number style.
d = floor((sqrt(8*(N-1)/step + 1) + 1) / 2)
Nrem = N - Nring
Rings are sized so that points are spaced 1 unit apart. There are three cases,
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 are on a circle and fractional N is on a
straight line between those integer points. This means it's a polygon too, but
one with ever more sides whereas ring_shape=polygon is a fixed
"step" many sides.
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,
then unit spacing d-1 for circle, or for polygon (d-1)/cos(pi/step) which is
bigger and ensures the middle of the sides have unit spacing radially.
The 0.5/sin(pi/step) for radius of a unit sided polygon arises from
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 simply a full circle divided by numsides.
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)
If Nrem is an integer for circle, or multiple of d for polygon, then the vertex
X,Y is the final X,Y, otherwise a fractional distance between the vertex X,Y
and next vertex X,Y.
For a few cases X or Y are exact integers. Special case code for these cases can
ensure floating point rounding of pi doesn't give small offsets from integers.
For step=6 the base r is r=1 exactly since the innermost ring is a little
hexagon. This 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 points occur when the step is even. Points on the
Y axis points 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.
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, allowing exact values from
"n_to_rsquared()".
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
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 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/>.