Math-PlanePath
view release on metacpan or search on metacpan
lib/Math/PlanePath/Diagonals.pm view on Meta::CPAN
=item C<$n = $path-E<gt>xy_to_n ($x,$y)>
Return the point number for coordinates C<$x,$y>. C<$x> and C<$y> are
each rounded to the nearest integer, which has the effect of treating each
point C<$n> as a square of side 1, so the quadrant x>=-0.5, y>=-0.5 is
entirely covered.
=item C<($n_lo, $n_hi) = $path-E<gt>rect_to_n_range ($x1,$y1, $x2,$y2)>
The returned range is exact, meaning C<$n_lo> and C<$n_hi> are the smallest
and biggest in the rectangle.
=back
=head1 FORMULAS
=head2 X,Y to N
The sum d=X+Y numbers each diagonal from d=0 upwards, corresponding to the Y
coordinate where the diagonal starts (or X if direction=up).
d=2
\
d=1 \
\ \
d=0 \ \
\ \ \
N is then given by
d = X+Y
N = d*(d+1)/2 + X + Nstart
The d*(d+1)/2 shows how the triangular numbers fall on the Y axis when X=0
and Nstart=0. For the default Nstart=1 it's 1 more than the triangulars, as
noted above.
=cut
# N = (X+Y)*(X+Y+1)/2 + X + Nstart
# = [ (X+Y)*(X+Y+1) + 2X ]/2 + Nstart
# = [ X^2 + XY + X + XY + Y^2 + Y + 2X ]/2 + Nstart
# = [ X^2 + 3X + 2XY + Y + Y^2 ]/2 + Nstart
# GP-Test my(d='x+'y); d*(d+1)/2 + 'x == (('x+'y)^2 + 3*'x + 'y)/2
=pod
d can be expanded out to the following quite symmetric form. This almost
suggests something parabolic but is still the straight line diagonals.
X^2 + 3X + 2XY + Y + Y^2
N = ------------------------ + Nstart
2
(X+Y)^2 + 3X + Y
= ---------------- + Nstart (using one square)
2
=head2 N to X,Y
The above formula N=d*(d+1)/2 can be solved for d as
d = floor( (sqrt(8*N+1) - 1)/2 )
# with n_start=0
For example N=12 is d=floor((sqrt(8*12+1)-1)/2)=4 as that N falls in the
fifth diagonal. Then the offset from the Y axis NY=d*(d-1)/2 is the X
position,
X = N - d*(d+1)/2
Y = X - d
In the code, fractional N is handled by imagining each diagonal beginning
0.5 back from the Y axis. That's handled by adding 0.5 into the sqrt, which
is +4 onto the 8*N.
d = floor( (sqrt(8*N+5) - 1)/2 )
# N>=-0.5
The X and Y formulas are unchanged, since N=d*(d-1)/2 is still the Y axis.
But each diagonal d begins up to 0.5 before that and therefore X extends
back to -0.5.
=cut
# GP-DEFINE \\ diagonal length
# GP-DEFINE d(n) = floor( (sqrt(8*n+1) - 1)/2 );
# GP-Test vector(16,n,n--; d(n)) == \
# GP-Test [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5]
# GP-DEFINE X(n) = my(d=d(n)); n - d*(d+1)/2;
# GP-DEFINE Y(n) = my(d=d(n)); d - X(n);
# GP-Test vector(16,n,n--; X(n)) == \
# GP-Test [0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0]
# GP-Test vector(16,n,n--; Y(n)) == \
# GP-Test [0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5]
# GP-Test my(l=List([])); \
# GP-Test for(d=0,3, for(x=0,d, my(y=d-x); x+y==d || error(); \
# GP-Test listput(l,[x,y]))); \
# GP-Test vector(#l,n,n--; [X(n),Y(n)]) == Vec(l)
=pod
=head2 Rectangle to N Range
Within each row increasing X is increasing N, and in each column increasing
Y is increasing N. So in a rectangle the lower left corner is minimum N and
the upper right is maximum N.
| \ \ N max
| \ ----------+
| | \ |\
| |\ \ |
| \| \ \ |
| +----------
| N min \ \ \
+-------------------------
=head1 OEIS
Entries in Sloane's Online Encyclopedia of Integer Sequences related to this
path include
=over
L<http://oeis.org/A002262> (etc)
=back
direction=down (the default)
A002262 X coordinate, runs 0 to k
A025581 Y coordinate, runs k to 0
A003056 X+Y coordinate sum, k repeated k+1 times
A114327 Y-X coordinate diff
A101080 HammingDist(X,Y)
A127949 dY, change in Y coordinate
A000124 N on Y axis, triangular numbers + 1
A001844 N on X=Y diagonal
( run in 0.868 second using v1.01-cache-2.11-cpan-df04353d9ac )