1Math::PlanePath::PeanoCUusrevre(C3o)ntributed Perl DocumMeanttha:t:iPolnanePath::PeanoCurve(3)
2
3
4
6 Math::PlanePath::PeanoCurve -- 3x3 self-similar quadrant traversal
7
9 use Math::PlanePath::PeanoCurve;
10 my $path = Math::PlanePath::PeanoCurve->new;
11 my ($x, $y) = $path->n_to_xy (123);
12
13 # or another radix digits ...
14 my $path5 = Math::PlanePath::PeanoCurve->new (radix => 5);
15
17 This path is an integer version of the curve described by Peano for
18 filling a unit square,
19
20 Giuseppe Peano, "Sur Une Courbe, Qui Remplit Toute Une Aire Plane",
21 Mathematische Annalen, volume 36, number 1, 1890, pages 157-160.
22 DOI 10.1007/BF01199438. <https://eudml.org/doc/157489>,
23 <https://link.springer.com/article/10.1007/BF01199438>
24
25 It traverses a quadrant of the plane one step at a time in a self-
26 similar 3x3 pattern,
27
28 8 60--61--62--63--64--65 78--79--80--...
29 | | |
30 7 59--58--57 68--67--66 77--76--75
31 | | |
32 6 54--55--56 69--70--71--72--73--74
33 |
34 5 53--52--51 38--37--36--35--34--33
35 | | |
36 4 48--49--50 39--40--41 30--31--32
37 | | |
38 3 47--46--45--44--43--42 29--28--27
39 |
40 2 6---7---8---9--10--11 24--25--26
41 | | |
42 1 5---4---3 14--13--12 23--22--21
43 | | |
44 Y=0 0---1---2 15--16--17--18--19--20
45
46 X=0 1 2 3 4 5 6 7 8 9 ...
47
48 The start is an S shape of the nine points N=0 to N=8, and then nine of
49 those groups are put together in the same S configuration. The sub-
50 parts are flipped horizontally and/or vertically to make the starts and
51 ends adjacent, so 8 is next to 9, 17 next to 18, etc,
52
53 60,61,62 --- 63,64,65 78,79,80
54 59,58,57 68,67,55 77,76,75
55 54,55,56 69,70,71 --- 72,73,74
56 |
57 |
58 53,52,51 38,37,36 --- 35,34,33
59 48,49,50 39,40,41 30,31,32
60 47,46,45 --- 44,43,42 29,28,27
61 |
62 |
63 6,7,8 ---- 9,10,11 24,25,26
64 3,4,5 12,13,14 23,22,21
65 0,1,2 15,16,17 --- 18,19,20
66
67 The process repeats, tripling in size each time.
68
69 Within a power-of-3 square, 3x3, 9x9, 27x27, 81x81 etc (3^k)x(3^k) at
70 the origin, all the N values 0 to 3^(2*k)-1 are within the square. The
71 top right corner 8, 80, 728, etc is the 3^(2*k)-1 maximum in each.
72
73 Because each step is by 1, the distance along the curve between two X,Y
74 points is the difference in their N values as given by "xy_to_n()".
75
76 Radix
77 The "radix" parameter can do the calculation in a base other than 3,
78 using the same kind of direction reversals. For example radix 5 gives
79 5x5 groups,
80
81 radix => 5
82
83 4 | 20--21--22--23--24--25--26--27--28--29
84 | | |
85 3 | 19--18--17--16--15 34--33--32--31--30
86 | | |
87 2 | 10--11--12--13--14 35--36--37--38--39
88 | | |
89 1 | 9-- 8-- 7-- 6-- 5 44--43--42--41--40
90 | | |
91 Y=0 | 0-- 1-- 2-- 3-- 4 45--46--47--48--49--50-...
92 |
93 +----------------------------------------------
94 X=0 1 2 3 4 5 6 7 8 9 10
95
96 If the radix is even then the ends of each group don't join up. For
97 example in radix 4 N=15 isn't next to N=16, nor N=31 to N=32, etc.
98
99 radix => 4
100
101 3 | 15--14--13--12 16--17--18--19
102 | | |
103 2 | 8-- 9--10--11 23--22--21--20
104 | | |
105 1 | 7-- 6-- 5-- 4 24--25--26--27
106 | | |
107 Y=0 | 0-- 1-- 2-- 3 31--30--29--28 32--33-...
108 |
109 +------------------------------------------
110 X=0 1 2 4 5 6 7 8 9 10
111
112 Even sizes can be made to join using other patterns, but this module is
113 just Peano's digit construction. For joining up in 2x2 groupings see
114 "HilbertCurve" (which is essentially the only way to join up in 2x2).
115 For bigger groupings there's various ways.
116
117 Unit Square
118 Peano's original form was for filling a unit square by mapping a number
119 T in the range 0<T<1 to a pair of X,Y coordinates 0<X<1 and 0<Y<1. The
120 curve is continuous and every such X,Y is reached by some T, so it
121 fills the unit square. A unit cube or higher dimension can be filled
122 similarly by developing three or more coordinates X,Y,Z, etc. Cantor
123 had shown a line is equivalent to the plane, Peano's mapping is a
124 continuous way to do that.
125
126 The code here could be pressed into service for a fractional T to X,Y
127 by multiplying up by a power of 9 to desired precision then dividing X
128 and Y back by the same power of 3 (perhaps swapping X,Y for which one
129 should be the first ternary digit). Note that if T is a binary
130 floating point then a power of 3 division will round off in general
131 since 1/3 is not exactly representable. (See "HilbertCurve" or
132 "ZOrderCurve" for binary mappings.)
133
134 Sometimes the curve is drawn with line segments crossing unit squares.
135 See PeanoDiagonals for that sort of path.
136
137 Power of 3 Patterns
138 Plotting sequences of values with some connection to ternary digits or
139 powers of 3 will usually give the most interesting patterns on the
140 Peano curve. For example the Mephisto waltz sequence
141 (Math::NumSeq::MephistoWaltz) makes diamond shapes,
142
143 ** * *** * * *** ** *** ** *** ** ** * *
144 * * ** ** *** ** *** * * ** ** *** ** ***
145 *** ** *** ** ** * *** * *** * * *** **
146 ** *** * * *** * ** ** *** * * *** * **
147 *** ** *** ** ** * *** * *** * * *** **
148 * * ** ** *** ** *** * * ** ** *** ** ***
149 *** ** *** ** ** * *** * *** * * *** **
150 ** *** * * *** * ** ** *** * * *** * **
151 ** * *** * * *** ** *** ** *** ** ** * *
152 * * ** ** *** ** *** * * ** ** *** ** ***
153 ** * *** * * *** ** *** ** *** ** ** * *
154 ** *** * * *** * ** ** *** * * *** * **
155 *** ** *** ** ** * *** * *** * * *** **
156 ** *** * * *** * ** ** *** * * *** * **
157 ** * *** * * *** ** *** ** *** ** ** * *
158 ** *** * * *** * ** ** *** * * *** * **
159 *** ** *** ** ** * *** * *** * * *** **
160 * * ** ** *** ** *** * * ** ** *** ** ***
161 *** ** *** ** ** * *** * *** * * *** **
162 ** *** * * *** * ** ** *** * * *** * **
163 ** * *** * * *** ** *** ** *** ** ** * *
164 * * ** ** *** ** *** * * ** ** *** ** ***
165 ** * *** * * *** ** *** ** *** ** ** * *
166 ** *** * * *** * ** ** *** * * *** * **
167 ** * *** * * *** ** *** ** *** ** ** * *
168 * * ** ** *** ** *** * * ** ** *** ** ***
169 *** ** *** ** ** * *** * *** * * *** **
170
171 This arises from each 3x3 block in the Mephisto waltz being one of two
172 shapes which are then flipped by the Peano pattern
173
174 * * _ _ _ *
175 * _ _ or _ * * (inverse)
176 _ _ * * * _
177
178 0,0,1, 0,0,1, 1,1,0 1,1,0, 1,1,0, 0,0,1
179
181 See "FUNCTIONS" in Math::PlanePath for behaviour common to all path
182 classes.
183
184 "$path = Math::PlanePath::PeanoCurve->new ()"
185 "$path = Math::PlanePath::PeanoCurve->new (radix => $integer)"
186 Create and return a new path object.
187
188 The optional "radix" parameter gives the base for digit splitting.
189 The default is ternary "radix => 3".
190
191 "($x,$y) = $path->n_to_xy ($n)"
192 Return the X,Y coordinates of point number $n on the path. Points
193 begin at 0 and if "$n < 0" then the return is an empty list.
194
195 Fractional $n give an X,Y position along a straight line between
196 the integer positions. Integer positions are always just 1 apart
197 either horizontally or vertically, so the effect is that the
198 fraction part appears either added to or subtracted from X or Y.
199
200 "$n = $path->xy_to_n ($x,$y)"
201 Return the integer point number for coordinates "$x,$y". Each
202 integer N is considered the centre of a unit square and an "$x,$y"
203 within that square returns N.
204
205 "($n_lo, $n_hi) = $path->rect_to_n_range ($x1,$y1, $x2,$y2)"
206 Return the range of N values which occur the a rectangle with
207 corners at $x1,$y1 and $x2,$y2. If the X,Y values are not integers
208 then the curve is treated as unit squares centred on each integer
209 point and squares which are partly covered by the given rectangle
210 are included.
211
212 The returned range is exact, meaning $n_lo and $n_hi are the
213 smallest and biggest in the rectangle.
214
215 Level Methods
216 "($n_lo, $n_hi) = $path->level_to_n_range($level)"
217 Return "(0, $radix**(2*$level) - 1)".
218
220 N to X,Y
221 Peano's calculation is based on putting base-3 digits of N alternately
222 to X or Y. From the high end of N, a digit goes to Y then the next
223 goes to X. Beginning at an even digit position in N makes the last
224 digit go to X so the first N=0,1,2 is along the X axis.
225
226 At each stage a "complement" state is maintained for X and for Y. When
227 complemented, the digit is reversed to 2 - digit, so 0,1,2 becomes
228 2,1,0. This reverses the direction so points like N=12,13,14 shown
229 above go leftwards, or groups like 9,10,11 then 12,13,14 then 15,16,17
230 go downwards.
231
232 The complement is calculated by adding the digits from N which went to
233 the other one of X or Y. So the X complement is the sum of digits
234 which have gone to Y so far. Conversely the Y complement is the sum of
235 digits put to X. If the complement sum is odd then the reversal is
236 done. A bitwise XOR can be used instead of a sum to accumulate
237 odd/even-ness the same way as a sum.
238
239 When forming the complement state, the original digits from N are
240 added, before applying any complementing for putting them to X or Y.
241 If the radix is odd, like the default 3, then complementing doesn't
242 change it mod 2 so before or after are the same, but if the radix is
243 even then it's not the same.
244
245 It also works to take the base-3 digits of N from low to high,
246 generating low to high digits in X and Y. If an odd digit is put to X
247 then the low digits of Y so far must be complemented as 22..22 - Y (the
248 22..22 value being all 2s in base 3, ie. 3^k-1). Conversely if an odd
249 digit is put to Y then X must be complemented. With this approach, the
250 high digit position in N doesn't have to be found, just peel off digits
251 of N from the low end. But the subtract to complement is then more
252 work if using bignums.
253
254 X,Y to N
255 The X,Y to N calculation can be done by an inverse of either the high
256 to low or low to high methods above. In both cases digits are put
257 alternately from X and Y into N, with complement as necessary.
258
259 For the low to high approach, it's not easy to complement just the X
260 digits in the N constructed so far, but it works to build and
261 complement the X and Y digits separately then at the end interleave to
262 make the final N. Complementing is the ternary equivalent of an XOR in
263 binary. On a ternary machine maybe some trit-twiddling would do it.
264
265 For low to high with even radix, the complementing is also tricky since
266 changing the accumulated X affects the digits of Y below that, and vice
267 versa. What's the rule? Is it alternate digits which end up
268 complemented? In any case the current "xy_to_n()" code goes high to
269 low which is easier, but means breaking the X,Y inputs into arrays of
270 digits before beginning.
271
272 N on Axes
273 N on the X axis is all Y digits 0 in the X,Y to N described above.
274 This means N is the digits of X, and then digit 0 or 2 at each Y
275 position according to odd or even sum of X digits above. The Y digits
276 are at odd positions so the 0 or 2 ternary is 0 or 6 for N in base-9.
277
278 N on X axis = 0,1,2, 15,16,17, 18,19,20, 141, ... (A163480)
279 ternary 0,1,2, 120,121,122, 200,201,202, 12020
280
281 The Y axis is similar but the X digits are at even positions.
282
283 N on Y axis = 0,5,6, 47,48,53, 54,59,60, 425, ... (A163481)
284 ternary 0,12,20, 1202,1210,1222, 2000,2012,2020, 120202
285
286 N on the X=Y diagonal has the ternary digits of position d go to both X
287 and Y and so both complemented according to sum of digits of d above.
288 That transformation within d is the ternary reflected Gray code.
289
290 Gray3(d) = ternary flip 0<->2 when sum of digits above is odd
291 = 0,1,2, 5,4,3, 6,7,8, 17, ... (A128173)
292 ternary 0,1,2, 12,11,10, 20,21,22, 122, ...
293
294 N on X=Y diag = ternary Gray3(d) and 0,1,2 -> 0,4,8 base9,
295 which is 4*digit
296 = 0,4,8, 44,40,36, 72,76,80, 404, ... (A163343)
297 ternary 0,11,22, 1122,1111,1100, 2200,2211,2222, 112222,
298
299 N to abs(dX),abs(dY)
300 The curve goes horizontally or vertically according to the number of
301 trailing "2" digits when N is written in ternary,
302
303 N trailing 2s direction abs(dX) abs(dY)
304 ------------- --------- ------- -------
305 even horizontal 1 0
306 odd vertical 0 1
307
308 abs(dX) = 1,1,0, 1,1,0, 1,1,1, 1,1,0, 1,1,0, 1,1,1, ... (A014578)
309 abs(dY) = 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,0, ... (A182581)
310
311 For example N=5 is "12" in ternary has 1 trailing "2" which is odd so
312 the step from N=5 to N=6 is vertical.
313
314 This works because when stepping from N to N+1 a carry propagates
315 through the trailing 2s to increment the digit above. Digits go
316 alternately to X or Y so odd or even trailing 2s put that carry into an
317 X digit or Y digit.
318
319 X Y X Y X
320 N ... 2 2 2 2
321 N+1 1 0 0 0 0 carry propagates
322
323 Rectangle to N Range
324 An easy over-estimate of the maximum N in a region can be had by going
325 to the next bigger (3^k)x(3^k) square enclosing the region. This means
326 the biggest X or Y rounded up to the next power of 3 (perhaps using
327 "log()" if you trust its accuracy), so
328
329 find k with 3^k > max(X,Y)
330 N_hi = 3^(2k) - 1
331
332 An exact N range can be found by following the "high to low" N to X,Y
333 procedure above. Start with the easy over-estimate to find a 3^(2k)
334 ternary digit position in N bigger than the desired region, then choose
335 a digit 0,1,2 for X, the biggest which overlaps some of the region. Or
336 if there's an X complement then the smallest digit is the biggest N,
337 again whichever overlaps the region. Then likewise for a digit of Y,
338 etc.
339
340 Biggest and smallest N must maintain separate complement states as they
341 track down different N digits. A single loop can be used since there's
342 the same "2k" many digits of N to consider for both.
343
344 The N range of any shape can be done this way, not just a rectangle
345 like "rect_to_n_range()". The procedure only depends on asking whether
346 a one-third sub-part of X or Y overlaps the target region or not.
347
349 This path is in Sloane's Online Encyclopedia of Integer Sequences in
350 several forms,
351
352 <http://oeis.org/A163528> (etc)
353
354 A163528 X coordinate
355 A163529 Y coordinate
356 A163530 X+Y coordinate sum
357 A163531 X^2+Y^2 square of distance from origin
358 A163532 dX, change in X -1,0,1
359 A163533 dY, change in Y -1,0,1
360 A014578 abs(dX) from n-1 to n, 1=horiz 0=vertical
361 A182581 abs(dY) from n-1 to n, 0=horiz 1=vertical
362 A163534 direction mod 4 of each step (ENWS)
363 A163535 direction mod 4, transposed X,Y
364 A163536 turn 0=straight,1=right,2=left
365 A163537 turn, transposed X,Y
366 A163342 diagonal sums
367 A163479 diagonal sums divided by 6
368
369 A163480 N on X axis
370 A163481 N on Y axis
371 A163343 N on X=Y diagonal, 0,4,8,44,40,36,etc
372 A163344 N on X=Y diagonal divided by 4
373 A007417 N+1 of positions of horizontals, ternary even trailing 0s
374 A145204 N+1 of positions of verticals, ternary odd trailing 0s
375
376 A163332 Peano N <-> ZOrder radix=3 N mapping (self-inverse)
377 A163333 with ternary digit swaps before and after
378
379 And taking X,Y points by the Diagonals sequence, then the value of the
380 following sequences is the N of the Peano curve at those positions.
381
382 A163334 numbering by diagonals, from same axis as first step
383 A163336 numbering by diagonals, from opposite axis
384 A163338 A163334 + 1, Peano starting from N=1
385 A163340 A163336 + 1, Peano starting from N=1
386
387 "Math::PlanePath::Diagonals" numbers points from the Y axis down, which
388 is the opposite axis to the Peano curve first step along the X axis, so
389 a plain "Diagonals" -> "PeanoCurve" is the "opposite axis" form
390 A163336.
391
392 These sequences are permutations of the integers since all X,Y
393 positions of the first quadrant are reached eventually. The inverses
394 are as follows. They can be thought of taking X,Y positions in the
395 Peano curve order and then asking what N the Diagonals would put there.
396
397 A163335 inverse of A163334
398 A163337 inverse of A163336
399 A163339 inverse of A163338
400 A163341 inverse of A163340
401
403 Math::PlanePath, Math::PlanePath::PeanoDiagonals,
404 Math::PlanePath::HilbertCurve, Math::PlanePath::ZOrderCurve,
405 Math::PlanePath::AR2W2Curve, Math::PlanePath::BetaOmega,
406 Math::PlanePath::CincoCurve, Math::PlanePath::KochelCurve,
407 Math::PlanePath::WunderlichMeander
408
409 Math::PlanePath::KochCurve
410
412 <http://user42.tuxfamily.org/math-planepath/index.html>
413
415 Copyright 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
416 2020 Kevin Ryde
417
418 This file is part of Math-PlanePath.
419
420 Math-PlanePath is free software; you can redistribute it and/or modify
421 it under the terms of the GNU General Public License as published by
422 the Free Software Foundation; either version 3, or (at your option) any
423 later version.
424
425 Math-PlanePath is distributed in the hope that it will be useful, but
426 WITHOUT ANY WARRANTY; without even the implied warranty of
427 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
428 General Public License for more details.
429
430 You should have received a copy of the GNU General Public License along
431 with Math-PlanePath. If not, see <http://www.gnu.org/licenses/>.
432
433
434
435perl v5.36.0 2022-07-22 Math::PlanePath::PeanoCurve(3)