package Panotools::Script::Line::Control;

use strict;
use warnings;
use Math::Trig;
use Panotools::Script::Line;

use vars qw /@ISA/;
@ISA = qw /Panotools::Script::Line/;

=head1 NAME

Panotools::Script::Line::Control - Panotools control-point


A pair of control-points forms a 'c' line


One line per point pair.
about one pair of points per image per variable being optimized.
The more variables being optimized the more control points needed.

  n0           first image
  N1           second image
  x1066.5      first image x point position
  y844.333     first image y point position
  X239.52      second image x point position
  Y804.64      second image y point position
  t0           type of control point (optional)
                 0 - normal (default)
                 1 - optimize horizontally only
                 2 - optimize vertically only
                 3+ (all other numbers) - straight line


sub _defaults
    my $self = shift;

sub _valid { return '^([nNxXyYt])(.*)' }

sub Identifier
    my $self = shift;
    return "c";


Get a simplified description of a control point useful for identifying
duplicates like so:

  print $point->Packed;

Format is first image, x, y, second image, x, y, point type

e.g: 2,123,456,3,234,567,0


sub Packed
    my $self = shift;
    if ($self->{n} < $self->{N})
        return join ',', $self->{n}, int ($self->{x}), int ($self->{y}),
                         $self->{N}, int ($self->{X}), int ($self->{Y}), $self->{t};
        return join ',', $self->{N}, int ($self->{X}), int ($self->{Y}),
                         $self->{n}, int ($self->{x}), int ($self->{y}), $self->{t};


Get a value for control point error distance (measured in pixels in the panorama

  print $point->Distance ($pto);

Note that it is necessary to pass a Panotools::Script object to this method.
Note also that the values returned are approximately half those returned by
panotools itself, go figure.


sub Distance
    my $self = shift;
    my $p = shift;

    my $image_N = $p->Image->[$self->{N}];
    my $image_n = $p->Image->[$self->{n}];

    my $vec_N = $image_N->To_Cartesian ($p, [$self->{X},$self->{Y}]);
    my $vec_n = $image_n->To_Cartesian ($p, [$self->{x},$self->{y}]);

    $vec_N = _normalise ($vec_N);
    $vec_n = _normalise ($vec_n);

    my $angle = acos (($vec_N->[0]->[0] * $vec_n->[0]->[0])
                    + ($vec_N->[1]->[0] * $vec_n->[1]->[0])
                    + ($vec_N->[2]->[0] * $vec_n->[2]->[0]));

    if ($p->Panorama->{f} == 0) # special case for rectilinear output
        my $radius = $p->Panorama->{w} / 2 / tan (deg2rad ($p->Panorama->{v}/2));
        return $radius * $angle;

    return $angle / pi() * $p->Panorama->{w} / 2;

sub _normalise
    my $vector = shift;

    my $magnitude = _magnitude ($vector->[0]->[0], $vector->[1]->[0], $vector->[2]->[0]);

    $vector->[0]->[0] = $vector->[0]->[0] / $magnitude;
    $vector->[1]->[0] = $vector->[1]->[0] / $magnitude;
    $vector->[2]->[0] = $vector->[2]->[0] / $magnitude;

    return $vector;

sub _magnitude
    my ($x, $y, $z) = @_;
    sqrt ($x*$x + $y*$y + $z*$z);