1Math::PlanePath::MultipUlseeRrinCgosn(t3r)ibuted Perl DoMcautmhe:n:tPaltainoenPath::MultipleRings(3)
2
3
4
6 Math::PlanePath::MultipleRings -- rings of multiples
7
9 use Math::PlanePath::MultipleRings;
10 my $path = Math::PlanePath::MultipleRings->new (step => 6);
11 my ($x, $y) = $path->n_to_xy (123);
12
14 This path puts points on concentric rings. Each ring has "step" many
15 points more than the previous and the first is also "step". For
16 example with the default step==6,
17
18 24 23 innermost ring 6
19 25 22 next ring 12
20 10 next ring 18
21 26 11 9 21 ... ringnum*step
22
23 27 12 3 2 8 20 38
24
25 28 13 4 1 7 19 37 <- Y=0
26
27 29 14 5 6 18 36
28
29 30 15 17 35
30 16
31 31 24
32 32 33
33
34 ^
35 X=0
36
37 X,Y positions are not integers, except on the axes. The innermost ring
38 like N=1to6 above has points 1 unit apart. Subsequent rings are a unit
39 chord or unit radial, whichever ensures no overlap.
40
41 step <= 6 unit spacing radially
42 step >= 6 unit chords around the rings
43
44 For step=6 the two spacings are the same. Unit radial spacing ensures
45 the X axis points N=1,7,19,37,etc shown above are 1 unit apart. Unit
46 chord spacing ensures adjacent points such as N=7,8,0,etc don't
47 overlap.
48
49 The layout is similar to the various spiral paths of corresponding
50 step. For example step=6 is like the "HexSpiral", but rounded out to
51 circles instead of a hexagonal grid. Similarly step=4 the
52 "DiamondSpiral" or step=8 the "SquareSpiral".
53
54 The step parameter is also similar to the "PyramidRows" with the rows
55 stretched around circles, but "PyramidRows" starts from a 1-wide
56 initial row whereas for "MultipleRings" here the first is "step" many.
57
58 X Axis
59 The starting Nring=1,7,19,37 etc on the X axis for the default step=6
60 is 6*d*(d-1)/2 + 1, counting the innermost ring as d=1. In general
61 Nring is a multiple of the triangular numbers d*(d-1)/2, plus 1,
62
63 Nring = step*d*(d-1)/2 + 1
64
65 This is the centred polygonal numbers, being the cumulative count of
66 points making concentric polygons or rings in the style of this path.
67
68 Straight line radials further around arise from adding multiples of d,
69 so for example in step=6 shown above the line N=3,11,25,etc is
70 Nring + 2*d. Multiples k*d with k>=step give lines which are in
71 between the base ones from the innermost ring.
72
73 Step 1
74 For step=1 the first ring is 1 point and each subsequent ring has 1
75 further point.
76
77 24
78 23
79 18 12 17
80 25 8
81 13 5
82
83 19 9 3 1 2 4 7 11 16 22 <- Y=0
84
85 14 6
86 26 10
87 20 15 21
88 28
89 27
90
91 ^
92 -5 -4 -3 -2-1 X=0 1 2 3 4 5 6
93
94 The rings are
95
96 polygon radius N values
97 ------------ ------ --------
98 single point 0 1
99 two points 1 2, 3
100 triangle 2 4, 5, 6
101 square 3 7, 8, 9,10
102 pentagon 4 11,12,13,14,15
103 hexagon 5 16,17,18,19,20,21
104 etc
105
106 The X axis as described above is the triangular numbers plus 1, ie.
107 k*(k+1)/2 + 1.
108
109 Step 2
110 For step=2 the arrangement is roughly
111
112 34
113 35 33
114 24 15 23
115 36 25 22 32
116 16 9 4 8 14
117
118 37 26 17 10 5 2 1 3 7 13 21 31
119
120 18 11 6 12 20
121 38 27 30 42
122 28 19 29
123 39 41
124 40
125
126 The pattern is similar to the "SacksSpiral" (see
127 Math::PlanePath::SacksSpiral). In "SacksSpiral" each spiral loop is 2
128 more points than the previous the same as here, but the positioning
129 differs. Here the X axis is the pronic numbers and the squares are to
130 the left, whereas in "SacksSpiral" rotated around to squares on X axis
131 and pronics to the left.
132
133 Ring Shape
134 Option "ring_shape => 'polygon'" puts the points on concentric polygons
135 of "step" many sides, so each concentric polygon has 1 more point on
136 each of its sides than the previous polygon. For example step=4 gives
137 4-sided polygons, ie. diamonds,
138
139 16
140 / \ ring_shape=>'polygon', step=>4
141 17 7 15
142 / / \ \
143 18 8 2 6 14
144 / / / \ \ \
145 19 9 3 1 5 13
146 \ \ \ / / /
147 20 10 4 12 24
148 \ \ / /
149 21 11 23
150 \ /
151 22
152
153 The polygons are scaled to keep points 1 unit apart. For step>=6 this
154 means 1 unit apart sideways. step=6 is in fact a honeycomb grid where
155 each points is 1 away from all six of its neighbours.
156
157 For step=3, 4 and 5 the polygon sides are 1 apart radially, as measured
158 in the centre of each side. This makes points a little more than 1
159 apart along the sides. Squeezing them up to make the closest points
160 exactly 1 apart is possible, but may require iterating a square root
161 for each ring. step=3 squeezed down would in fact become a variable
162 spacing with successively four close then one wider.
163
164 For step=2 and step=1 in the current code the default circle shape is
165 used. Should that change? Is there a polygon style with 2 sides or 1
166 side?
167
168 The polygon layout is only a little different from a circle, but it
169 lines up points on the sides and that might help show a structure for
170 some sets of points plotted on the path.
171
172 Step 3 Pentagonals
173 For step=3 the pentagonal numbers 1,5,12,22,etc, P(k) = (3k-1)*k/2, are
174 a radial going up to the left, and the second pentagonal numbers
175 2,7,15,26, S(k) = (3k+1)*k/2 are a radial going down to the left,
176 respectively 1/3 and 2/3 the way around the circles.
177
178 As described in "Step 3 Pentagonals" in Math::PlanePath::PyramidRows,
179 those P(k) and preceding P(k)-1, P(k)-2, and S(k) and preceding S(k)-1,
180 S(k)-2 are all composites, so plotting the primes on a step=3
181 "MultipleRings" has two radial gaps where there's no primes.
182
184 See "FUNCTIONS" in Math::PlanePath for behaviour common to all path
185 classes.
186
187 "$path = Math::PlanePath::MultipleRings->new (step => $integer)"
188 "$path = Math::PlanePath::MultipleRings->new (step => $integer,
189 ring_shape => $str)"
190 Create and return a new path object.
191
192 The "step" parameter controls how many points are added in each
193 circle. It defaults to 6 which is an arbitrary choice and the
194 suggestion is to always pass in a desired count.
195
196 "($x,$y) = $path->n_to_xy ($n)"
197 Return the X,Y coordinates of point number $n on the path.
198
199 $n can be any value "$n >= 1" and fractions give positions on the
200 rings in between the integer points. For "$n < 1" the return is an
201 empty list since points begin at 1.
202
203 Fractional $n currently ends up on the circle arc between the
204 integer points. Would straight line chords between them be better,
205 reflecting the unit spacing of the points? Neither seems
206 particularly important.
207
208 "$n = $path->xy_to_n ($x,$y)"
209 Return an integer point number for coordinates "$x,$y". Each
210 integer N is considered the centre of a circle of diameter 1 and an
211 "$x,$y" within that circle returns N.
212
213 The unit spacing of the points means those circles don't overlap,
214 but they also don't cover the plane and if "$x,$y" is not within
215 one then the return is "undef".
216
217 "$str = $path->figure ()"
218 Return "circle".
219
221 N to X,Y - Circle
222 As per above, each ring begins at
223
224 Nring = step*d*(d-1)/2 + 1
225
226 This can be inverted to get the ring number d for a given N, and then
227 subtract Nring for a remainder into the ring. (N-1)/step in the
228 formula effectively converts into triangular number style.
229
230 d = floor((sqrt(8*(N-1)/step + 1) + 1) / 2)
231 Nrem = N - Nring
232
233 Rings are sized so that points are spaced 1 unit apart. There are
234 three cases,
235
236 circle, step<=6 unit radially on X axis
237 polygon, step<=6 unit radially on sides centre
238 step>=7 unit chord between points
239
240 For the circle shape the integer points are on a circle and fractional
241 N is on a straight line between those integer points. This means it's
242 a polygon too, but one with ever more sides whereas ring_shape=polygon
243 is a fixed "step" many sides.
244
245 circle numsides = d*step
246 polygon numsides = step
247
248 The radial distance to a polygon corner is calculated as
249
250 base varying with d
251 ---------------- ---------------------------------------
252 circle, step<=6 0.5/sin(pi/step) + d-1
253 polygon, step<=6 0.5/sin(pi/step) + (d-1)/cos(pi/step)
254 circle, step>=7 0 + 0.5/sin(pi/(d*step))
255 polygon, step>=7 0 + d * 0.5/sin(pi/step)
256
257 The step<=6 cases are an initial polygon of "step" many unit sides,
258 then unit spacing d-1 for circle, or for polygon (d-1)/cos(pi/step)
259 which is bigger and ensures the middle of the sides have unit spacing
260 radially.
261
262 The 0.5/sin(pi/step) for radius of a unit sided polygon arises from
263
264 r ___---*
265 ___--- | 1/2 = half the polygon side
266 ___--- alpha |
267 o------------------+
268
269 alpha = (2pi/numsides) / 2 = pi/numsides
270 sin(alpha) = (1/2) / base_r
271 r = 0.5 / sin(pi/numsides)
272
273 The angle theta to a polygon vertex is simply a full circle divided by
274 numsides.
275
276 side = circle Nrem
277 polygon floor(Nrem / step)
278 theta = side * (2pi / numsides)
279 vertex X = r * cos(theta)
280 Y = r * sin(theta)
281
282 next_theta = (side+1) * (2pi / numsides)
283 next_vertex X = r * cos(next_theta)
284 Y = r * sin(next_theta)
285
286 frac into side
287 f = circle frac(Nrem) = Nrem modulo 1
288 polygon Nrem - side*d = Nrem modulo d
289
290 X = vertex_X + f * (next_vertex_X - vertex_X)
291 Y = vertex_Y + f * (next_vertex_Y - vertex_Y)
292
293 If Nrem is an integer for circle, or multiple of d for polygon, then
294 the vertex X,Y is the final X,Y, otherwise a fractional distance
295 between the vertex X,Y and next vertex X,Y.
296
297 For a few cases X or Y are exact integers. Special case code for these
298 cases can ensure floating point rounding of pi doesn't give small
299 offsets from integers.
300
301 For step=6 the base r is r=1 exactly since the innermost ring is a
302 little hexagon. This means for the circle step=6 case the points on
303 the X axis (positive and negative) are all integers X=1,2,3,etc.
304
305 P-----P
306 / 1 / \ 1 <-- innermost points 1 apart
307 / / \
308 P o-----P <-- base_r = 1
309 \ 1 /
310 \ /
311 P-----P
312
313 If theta=pi, which is when 2*Nrem==d*step, then the point is on the
314 negative X axis. Returning Y=0 exactly for that avoids sin(pi) giving
315 some small non-zero due to rounding.
316
317 If theta=pi/2 or theta=3pi/2, which is 4*Nrem==d*step or
318 4*Nrem==3*d*step, then N is on the positive or negative Y axis
319 (respectively). Returning X=0 exactly avoids cos(pi/2) or cos(3pi/2)
320 giving some small non-zero.
321
322 Points on the negative X axis points occur when the step is even.
323 Points on the Y axis points occur when the step is a multiple of 4.
324
325 If theta=pi/4, 3*pi/4, 5*pi/4 or 7*pi/4, which is 8*Nrem==d*step,
326 3*d*step, 5*d*step or 7*d*step then the points are on the 45-degree
327 lines X=Y or X=-Y. The current code doesn't try to ensure X==Y in
328 these cases. The values are not integers and floating point rounding
329 might mean sin(pi/4)!=cos(pi/4) resulting in X!=Y.
330
331 N to RSquared - Step 1
332 For step=1 the rings are point, line, triangle, square, pentagon, etc,
333 with vertices at radius=numsides-1. For fractional N the triangle,
334 square and hexagon cases are quadratics in the fraction part, allowing
335 exact values from "n_to_rsquared()".
336
337 Ring R^2
338 --------------------- --------------
339 triangle 4 <= N < 7 4 - 12*f*(1-f)
340 square 7 <= N < 11 9 - 18*f*(1-f)
341 hexagon 16 <= N < 22 25 - 25*f*(1-f)
342
343 f = N - int(N) fractional part of N
344
345 For example for the square at N=7.5 have f=0.5 and R^2=4.5 exactly.
346 These quadratics arise because sine of 2pi/3, 2pi/4 and 2pi/6 are
347 square roots, which on squaring up in R^2=X^2+Y^2 become integer
348 factors for the fraction f along the polygon side.
349
351 Entries in Sloane's Online Encyclopedia of Integer Sequences related to
352 this path include
353
354 <http://oeis.org/A005448> (etc)
355
356 A005448 A001844 A005891 A003215 A069099 3 to 7
357 A016754 A060544 A062786 A069125 A003154 8 to 12
358 A069126 A069127 A069128 A069129 A069130 13 to 17
359 A069131 A069132 A069133 18 to 20
360 N on X axis of step=k, being the centred pentagonals
361
362 step=1
363 A002024 Radius+1, runs of n repeated n times
364
365 step=8
366 A090915 permutation N at X,-Y, mirror across X axis
367
369 Math::PlanePath, Math::PlanePath::SacksSpiral,
370 Math::PlanePath::TheodorusSpiral, Math::PlanePath::PixelRings
371
373 <http://user42.tuxfamily.org/math-planepath/index.html>
374
376 Copyright 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
377 2020 Kevin Ryde
378
379 This file is part of Math-PlanePath.
380
381 Math-PlanePath is free software; you can redistribute it and/or modify
382 it under the terms of the GNU General Public License as published by
383 the Free Software Foundation; either version 3, or (at your option) any
384 later version.
385
386 Math-PlanePath is distributed in the hope that it will be useful, but
387 WITHOUT ANY WARRANTY; without even the implied warranty of
388 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
389 General Public License for more details.
390
391 You should have received a copy of the GNU General Public License along
392 with Math-PlanePath. If not, see <http://www.gnu.org/licenses/>.
393
394
395
396perl v5.34.0 2021-07-22 Math::PlanePath::MultipleRings(3)