=head1 Name

Math::Intersection::Circle::Line - Find the points at which a circle intersects a line.

=head1 Synopsis

 use Math::Intersection::Circle::Line;

 intersectionCircleLine
  {printf "Intersects at:\n%d, %d\n%d, %d\n", @_ if @_ == 4}
   1, 1, 1,       # Circle at (1,1) with radius 1
  -1, 1, 1, 1;    # x, y coordinates of two points on a diagonally crossing line

 # Intersects at:
 # 0, 1
 # 2, 1

 intersectionCircleLine
  {printf "Tangents touches at\n%d, %d\n", @_ if @_ == 2}
    0, 0, 1,      # Circle centered on origin with radius 1
   -1, 1,  1, 1;  # Line along y == 1

 # Tangents touches at
 # 0, 1

 intersectionCircleLine
  {print "No intersection\n" unless @_}
    0, 0, 1,      # Circle at origin with radius 1
   -2, 2, 2, 2;   # Line along y == 2

 # No intersection

=cut

package Math::Intersection::Circle::Line;

#-------------------------------------------------------------------------------
# Locate the points at which a line crosses a circle in two dimensions
# Philip R Brenan at gmail dot com, Appa Apps Ltd, 2015
#-------------------------------------------------------------------------------

use v5.18;
use warnings FATAL => qw(all);
use strict;
use utf8;

#-------------------------------------------------------------------------------
# Known 𝘅, 𝘆, r, X, x, Y, y find l,t
#
# Circle(t)    Line(l) from (X,Y) to (x,y)
# 𝘅+𝗿*cos(t) = X+l*(X-x)
# 𝘆+𝗿*sin(t) = Y+l*(Y-y)
#
# 𝗿*cos(t)   = X+l*(X-x)-𝘅   let 𝗫 = X - x, 𝕏 = X - 𝘅 => 𝗿*cos(t) == 𝕏 + l𝗫
# 𝗿*sin(t)   = Y+l*(Y-y)-𝘆   let 𝗬 = Y - y, 𝕐 = Y - 𝘆 => 𝗿*sin(t) == 𝕐 + l𝗬
#
# rr         = 𝕏𝕏 + ll𝗫𝗫 + l2𝕏𝗫  + 𝕐𝕐 + ll𝗬𝗬 + l2𝕐𝗬
# 0          = ll(a = 𝗫𝗫 + 𝗬𝗬) + l(b = 2(𝕏𝗫  + 𝕐𝗬)) + (c = 𝕏𝕏 + 𝕐𝕐 - rr)
#
# Quadratic equation axx+bx+c has roots:
#
# x1, x2  = -b/2a +- sqrt(bb-4ac)/2a
#-------------------------------------------------------------------------------

sub intersectionCircleLine(&$$$$$$$)
 {my ($sub,                                                                     # Sub routine to process intersection
      $𝘅, $𝘆, $𝗿,                                                               # Circle centre, radius
      $X, $Y, $x, $y) = @_;                                                     # Line goes through these two points

  my $𝗫 = $X - $x; my $𝕏 = $X - $𝘅;
  my $𝗬 = $Y - $y; my $𝕐 = $Y - $𝘆;

  my $a = $𝗫*$𝗫 + $𝗬*$𝗬;                                                        # Quadratic coefficients
  my $b = 2 * ($𝕏*$𝗫 + $𝕐*$𝗬);
  my $c = ($𝕏*$𝕏 + $𝕐*$𝕐 - $𝗿*$𝗿);

  my $𝗱 = $b * $b - 4 * $a * $c;                                                # Determinant

  return &$sub() if $𝗱 < 0;                                                     # No point of contact

  my $𝗰 = -$b / (2 * $a);                                                       # Center of solution

  if ($𝗱 == 0)                                                                  # Tangent
   {my $l = $𝗰;
    return &$sub($X + $l * $𝗫, $Y + $l * $𝗬);
   }
                                                                                # Two crossing points
  my $d = sqrt($𝗱) / (2 * $a);
  my ($l, $𝗹) = ($𝗰 + $d, $𝗰 - $d);
  &$sub($X + $l * $𝗫, $Y + $l * $𝗬, $X + $𝗹 * $𝗫, $Y + $𝗹 * $𝗬)
 }

# Export details

require 5;
require Exporter;

use vars qw(@ISA @EXPORT $VERSION);

@ISA     = qw(Exporter);
@EXPORT  = qw(intersectionCircleLine);
$VERSION = '1.001'; # Thursday 20 Aug 2015

=head1 Description

Find the points at which a circle intersects a line in two dimensions.

In two dimensional real space a line intersects the circumference of a circle
at two points if the line crosses the circle or at one point if the line is a
tangent to the circle or at zero points if the line misses the circle
completely.

Given a circle specified by the coordinates of its centre and its radius and a
line specified by the coordinates of two separate points on the line,
C<intersectionCircleLine()> calls the subroutine supplied as the first
parameter with the coordinates of the points at which the line and circle
intersect. If there are two intersection points, 2 pairs of numbers are
supplied as parameters. If there is only one intersection point, one pair of
numbers is supplied.  If there are no intersection points, an empty parameter
list is supplied.

C<intersectionCircleLine()> is easy to use and fast. It is written in 100% Pure
Perl.

=head1 Export

The C<intersectionCircleLine()> function is exported.

=head1 Installation

Standard Module::Build process for building and installing modules:

  perl Build.PL
  ./Build
  ./Build test
  ./Build install

Or, if you're on a platform (like DOS or Windows) that doesn't require
the "./" notation, you can do this:

  perl Build.PL
  Build
  Build test
  Build install

=head1 Author

Philip R Brenan at gmail dot com

http://www.appaapps.com

=head1 Copyright

Copyright (c) 2015 Philip R Brenan.

This module is free software. It may be used, redistributed and/or
modified under the same terms as Perl itself.

=cut
