NAME
Math::PlanePath::ArchimedeanChords -- radial spiral chords
SYNOPSIS
use Math::PlanePath::ArchimedeanChords;
my $path = Math::PlanePath::ArchimedeanChords->new;
my ($x, $y) = $path->n_to_xy (123);
DESCRIPTION
This path puts points at unit chord steps along an Archimedean spiral. The spiral goes outwards by 1 unit each revolution and the points are spaced 1 apart. The result is roughly
31
32 30 ... 3
33 29
14
34 15 13 28 50 2
12
16 3
35 2 27 49 1
4 11
17
36 5 0 1 26 48 <- Y=0
10
18
37 6 25 47 -1
9
19 7 8 24 46
38 -2
20 23
39 21 22 45
-3
40 44
41 42 43
^
-3 -2 -1 X=0 1 2 3 4
X,Y positions returned are fractional. Each revolution is about 2*pi longer than the previous, so the effect is a kind of 6.28 increment looping.
Because the spacing is by unit chords, adjacent unit circles centred on each N position touch but don't overlap. The spiral spacing of 1 unit per revolution means they don't overlap radially either.
The unit chords here are a little like the TheodorusSpiral. But the TheodorusSpiral goes by unit steps at a fixed right-angle and approximates an Archimedean spiral (of 3.14 radial spacing). Whereas this ArchimedeanChords is an actual Archimedean spiral (of radial spacing 1), with unit steps angling along that.
FUNCTIONS
$path = Math::PlanePath::ArchimedeanChords->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.$n
can be any value$n >= 0
and fractions give positions on the chord between the integer points (ie. straight line between the points).$n==0
is the origin 0,0.For
$n < 0
the return is an empty list, it being considered there are no negative points in the spiral. $n = $path->xy_to_n ($x,$y)
-
Return an integer point number for coordinates
$x,$y
. Each integer N is considered the centre of a circle of diameter 1 and an$x,$y
within that circle returns N.The unit spacing of the spiral means those circles don't overlap, but they also don't cover the plane and if
$x,$y
is not within one then the return isundef
.The current implementation is a bit slow.
$n = $path->n_start ()
-
Return 0, the first
$n
on the path. $n = $path->figure ()
-
Return "circle".
FORMULAS
N to X,Y
The current code keeps a position as a polar angle t and calculates an increment u needed to move along by a unit chord. If dist(u) is the straight-line distance between t and t+u, then squared is the hypotenuse
dist^2(u) = ((t+u)/2pi*cos(t+u) - t/2pi*cos(t))^2 # X
+ ((t+u)/2pi*sin(t+u) - t/2pi*sin(t))^2 # Y
which simplifies to
dist^2(u) = [ (t+u)^2 + t^2 - 2*t*(t+u)*cos(u) ] / (4*pi^2)
Switch from cos to sin using the half angle cos(u) = 1 - 2*sin^2(u/2)) in case if u is small then the cos(u) near 1.0 might lose floating point accuracy, and also as a slight simplification,
dist^2(u) = [ u^2 + 4*t*(t+u)*sin^2(u/2) ] / (4*pi^2)
Then want the u which has dist(u)=1 for a unit chord. The u*sin(u) part probably doesn't have a good closed form inverse, so the current code is a Newton/Raphson iteration on f(u) = dist^2(u)-1, seeking f(u)=0
f(u) = u^2 + 4*t*(t+u)*sin^2(u/2) - 4*pi^2
Derivative f'(u) for the slope from the cos form is
f'(u) = 2*(t+u) - 2*t*[ cos(u) - (t+u)*sin(u) ]
And again switching from cos to sin in case u is small,
f'(u) = 2*[ u + t*[2*sin^2(u/2) + (t+u)*sin(u)] ]
X,Y to N
For a given x,y point the nearest spiral position (the nearest radially) which might have an N covering that x,y is easily calculated from hypot(x,y) and the angle atan2(y,x). What's not so easily calculated is how many N chords it takes to get around to that point.
The current code looks in saved n_to_xy
positions for an N below the target, and searches up from there until past the point and thus not overlapping x,y. With N saved 500 apart this means somewhere between 1 and 500 points.
One possibility for calculating a lower bound for N both for xy_to_n
and rect_to_n_range
would be to add up the chords in circles. A circle of radius k fits pi/arcsin(1/2k) many unit chords, so
k=floor(r) pi
total = sum ------------
k=0 arcsin(1/2k)
would be less than the chords along the spiral its radius r. What's a good polynomial over-estimate of arcsin, to become an under-estimate total, without giving away so much?
N Range
For the rect_to_n_range
upper bound, the current code takes the arc length along with spiral with the usual formula
arc = 1/4pi * (theta*sqrt(1+theta^2) + asinh(theta))
Written in terms of the r radius (theta = 2pi*r) as calculated from the biggest of the rectangle x,y corners,
arc = pi*r^2*sqrt(1+1/r^2) + asinh(2pi*r)/4pi
The arc length is longer than chords, so N=ceil(arc) is an upper bound for the N range.
An upper bound can also be calculated simply from the circumferences of circles 1 to r, since a spiral loop from radius k to k+1 is shorter than a circle of radius k.
k=ceil(r)
total = sum 2pi*k
k=1
= pi*r*(r+1)
This is bigger than the arc length, thus a poorer upper bound, but an easier calculation. (Incidentally, for smallish r have arc length <= pi*(r^2+1) which is a tighter bound and an easy calculation, but it only holds up to somewhere around r=10^7.)
SEE ALSO
Math::PlanePath, Math::PlanePath::TheodorusSpiral, Math::PlanePath::SacksSpiral
HOME PAGE
http://user42.tuxfamily.org/math-planepath/index.html
LICENSE
Math-PlanePath is Copyright 2010, 2011 Kevin Ryde
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/>.