NAME
Math::PlanePath::DiagonalsOctant -- points in diagonal stripes
SYNOPSIS
use Math::PlanePath::DiagonalsOctant;
my $path = Math::PlanePath::DiagonalsOctant->new;
my ($x, $y) = $path->n_to_xy (123);
DESCRIPTION
This path follows successive diagonals going from the Y axis down to the X=Y line to traverse an eighth of the plane.
8 | 21 27 33 40 47 55 63 72 81
| \ \ \ \ \ \ \
7 | 17 22 28 34 41 48 56 64
| \ \ \ \ \ \
6 | 13 18 23 29 35 42 49
| \ \ \ \ \
5 | 10 14 19 24 30 36
| \ \ \ \
4 | 7 11 15 20 25
| \ \ \
3 | 5 8 12 16
| \ \
2 | 3 6 9
| \
1 | 2 4
|
Y=0 | 1
+ ----------------------------
X=0 1 2 3 4 5 6 7 8
N=1,4,9,16,etc on the X=Y leading diagonal are the perfect squares. N=2,6,12,20,etc to the left of them are the pronic numbers k*(k+1)
Direction
Option direction => 'up'
reverses the order within each diagonal and counts upward from the centre to the Y axis.
8 | 25 29 34 39 45 51 58 65 73
| \ \ \ \ \ \ \
7 | 20 24 28 33 38 44 50 57
| \ \ \ \ \ \
6 | 16 19 23 27 32 37 43
| \ \ \ \ \
5 | 12 15 18 22 26 31
| \ \ \ \
4 | 9 11 14 17 21
| \ \ \
3 | 6 8 10 13
| \ \
2 | 4 5 7
| \
1 | 2 3
|
Y=0 | 1
+---------------------------
X=0 1 2 3 4 5 6 7 8
In this arrangement N=1,2,4,6,9,etc on the Y axis are the squares and pronic numbers. The squares are on even Y and pronic on odd Y.
FUNCTIONS
See "FUNCTIONS" in Math::PlanePath for behaviour common to all path classes.
$path = Math::PlanePath::DiagonalsOctant->new ()
-
Create and return a new path object.
($x,$y) = $path->n_to_xy ($n)
-
Return the X,Y coordinates of point number
$n
on the path.For
$n < 0.5
the return is an empty list, it being considered the path begins at 1. $n = $path->xy_to_n ($x,$y)
-
Return the point number for coordinates
$x,$y
.$x
and$y
are each rounded to the nearest integer, which has the effect of treating each point$n
as a square of side 1. ($n_lo, $n_hi) = $path->rect_to_n_range ($x1,$y1, $x2,$y2)
-
The returned range is exact, meaning
$n_lo
and$n_hi
are the smallest and biggest in the rectangle.
FORMULAS
N to X,Y
To break N into X,Y it's convenient to take two diagonals at a time, since the length then changes by 1 each pair making a quadratic. Starting at each X=0,Y=odd just after perfect square N allows just a sqrt.
Nstart = d*d+1
where d numbers diagonal pairs, eg. d=3 for X=0,Y=5 going down. This is easily reversed as
d = floor sqrt(N-1)
The code reckons the start of the diagonal as 0.5 further back, so that N=9.5 is at X=-.5,Y=5.5. To do that d is formed as
d = floor sqrt(N-0.5)
= int( sqrt(int(4*$n)-2)/2 )
Taking /2 out of the sqrt helps with Math::BigInt
which circa Perl 5.14 doesn't inter-operate with flonums very well.
In any case N-Nstart is an offset into two diagonals, the first of length d many points and the second d+1. For example d=3 starting Y=5 for points N=10,11,12 followed by Y=6 N=13,14,15,16.
The formulas are simplified by calculating a remainder relative to the second diagonal, so it's negative for the first and positive for the second,
Nrem = N - (d*(d+1)+1)
d*(d+1)+1 is 1 past the pronic numbers when end each first diagonal, as described above. In any case for example d=3 is relative to N=13 making Nrem=-3,-2,-1 or Nrem=0,1,2,3.
To include the preceding 0.5 in the second diagonal simply means reckoning Nrem>=-0.5 as belonging to the second. In that base
if Nrem >= -0.5
X = Nrem # direction="down"
Y = 2*d - Nrem
else
X = Nrem + d
Y = d - Nrem - 1
For example N=15 Nrem=1 is the first case, X=1, Y=2*3-1=5. Or N=11 Nrem=-2 the second X=-2+3=1, Y=3-(-2)-1=4.
For "up" direction the Nrem and d are the same, but the coordinate directions reverse.
if Nrem >= -0.5
X = d - Nrem # direction="up"
Y = d + Nrem
else
X = -Nrem - 1
Y = 2d + Nrem
Another way is to reckon Nstart from the X=0,Y=even diagonals, which is then two diagonals of the same length and d formed by a sqrt inverting a pronic Nstart=d*(d+1).
Rectangle to N Range
Within each row increasing X is increasing N, and in each column increasing Y is increasing N. This is so in both "down" and "up" arrangements. On that basis for a rectangle the lower left corner is the minimum N and the upper right is the maximum N.
If the rectangle is partly outside the covered octant then the corners must be shifted to put them in range.
| /
+---- /
| | / if x1 < 0 then x1 = 0
+---/ increase x1 to within octant
x1 |/
+
| |/
| | if y1 < x1 then y1 = x1
| /| increase y1 to bottom-left within octant
|/ +----y1
+ x1
| / x2
| ------+ y2 if x2 > y2 then x2 = y2
| / | decrease x2 so top-right within octant
| / | (the end of the y2 row)
|/
+
SEE ALSO
Math::PlanePath, Math::PlanePath::Diagonals, Math::PlanePath::DiagonalsAlternating
HOME PAGE
http://user42.tuxfamily.org/math-planepath/index.html
LICENSE
Copyright 2012 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/>.