Math-PlanePath

 view release on metacpan or  search on metacpan

lib/Math/PlanePath/Corner.pm  view on Meta::CPAN

Return the X,Y coordinates of point number C<$n> on the path.

For C<$n < n_start()-0.5> the return is an empty list.  There's an extra 0.5
before Nstart, but nothing further before there.

=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 as a square of side 1, so the quadrant x>=-0.5
and 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 N to X,Y

Counting d=0 for the first L-shaped gnomon at Y=0, then the start of the
gnomon is

    StartN(d) = d^2 + 1 = 1,2,5,10,17,etc

The current C<n_to_xy()> code extends to the left by an extra 0.5 for
fractional N, so for example N=9.5 is at X=-0.5,Y=3.  With this the starting
N for each gnomon d is

    StartNfrac(d) = d^2 + 0.5

Inverting gives the gnomon d number for an N,

    d = floor(sqrt(N - 0.5))

Subtracting the gnomon start gives an offset into that gnomon

    OffStart = N - StartNfrac(d)

The corner point 1,3,7,13,etc where the gnomon turns down is at d+0.5 into
that remainder, and it's convenient to subtract that so negative for the
horizontal and positive for the vertical,

    Off = OffStart - (d+0.5)
        = N - (d*(d+1) + 1)

Then the X,Y coordinates are

    if (Off < 0)  then  X=d+Off, Y=d
    if (Off >= 0) then  X=d,     Y=d-Off

=head2 X,Y to N

For a given X,Y the bigger of X or Y determines the d gnomon.

If YE<gt>=X then X,Y is on the horizontal part.  At X=0 have N=StartN(d) per
the Start(N) formula above, and any further X is an offset from there.

    if Y >= X then
      d=Y
      N = StartN(d) + X
        = Y^2 + 1 + X

Otherwise if YE<lt>X then X,Y is on the vertical part.  At Y=0 N is the last
point on the gnomon, and one back from the start of the following gnomon,

    if Y <= X then
      d=X
      LastN(d) = StartN(d+1) - 1
               = (d+1)^2
      N = LastN(d) - Y
        = (X+1)^2 - Y

=head2 Rectangle N Range

For C<rect_to_n_range()>, in each row increasing X is increasing N so the
smallest N is in the leftmost column and the biggest N in the rightmost
column.

    |
    |  ------>  N increasing
    |
     -----------------------

Going up a column, N values are increasing away from the X=Y diagonal up or
down, and all N values above X=Y are bigger than the ones below.

    |    ^  N increasing up from X=Y diagonal
    |    |
    |    |/
    |    /
    |   /|
    |  / |  N increasing down from X=Y diagonal
    | /  v
    |/
     -----------------------

This means the biggest N is the top right corner if that corner is YE<gt>=X,
otherwise the bottom right corner.

                                           max N at top right
    |      /                          | --+     if corner Y>=X
    |     / --+                       |   | /
    |    /    |                       |   |/
    |   /     |                       |   |
    |  /  ----v                       |  /|
    | /     max N at bottom right     | --+
    |/        if corner Y<=X          |/
     ----------                        -------

For the smallest N, if the bottom left corner has YE<gt>X then it's in the
"increasing" part and that bottom left corner is the smallest N.  Otherwise
YE<lt>=X means some of the "decreasing" part is covered and the smallest N
is at Y=min(X,Ymax), ie. either the Y=X diagonal if it's in the rectangle or
the top right corner otherwise.

    |      /



( run in 2.677 seconds using v1.01-cache-2.11-cpan-df04353d9ac )