Subject: | incorrect quaternion generated when passed parallel but opposite vectors |
rotation() incorrectly generates a non-rotating quaternion when passed two vectors which are parallel but opposite. For example,
rotation( [1,0,0], [-1,0,0 ] )
returns [ 1, 0, 0, 0] rather than [ 0, 0, 0, 1 ].
This is caused by the new parallel vector code, which doesn't distinguish the sign of the dot product.
Attached is a patch against 0.06 which fixes the code (and removes some of the comments in that section which are no longer pertinent, as one no longer has the choice of choosing a random rotation vector, since the rotation is not always zero). Test added as well.
Diab
Subject: | Math-Quaternion-0.06.patch |
# This is a patch for Math-Quaternion-0.06.orig to update it to Math-Quaternion-0.06
#
# To apply this patch:
# STEP 1: Chdir to the source directory.
# STEP 2: Run the 'applypatch' program with this patch file as input.
#
# If you do not have 'applypatch', it is part of the 'makepatch' package
# that you can fetch from the Comprehensive Perl Archive Network:
# http://www.perl.com/CPAN/authors/Johan_Vromans/makepatch-x.y.tar.gz
# In the above URL, 'x' should be 2 or higher.
#
# To apply this patch without the use of 'applypatch':
# STEP 1: Chdir to the source directory.
# STEP 2: Run the 'patch' program with this file as input.
#
#### End of Preamble ####
#### Patch data follows ####
diff -c 'Math-Quaternion-0.06.orig/lib/Math/Quaternion.pm' 'Math-Quaternion-0.06/lib/Math/Quaternion.pm'
Index: ./lib/Math/Quaternion.pm
*** ./lib/Math/Quaternion.pm Sat Aug 24 07:52:47 2013
--- ./lib/Math/Quaternion.pm Wed Oct 30 16:45:30 2013
***************
*** 678,698 ****
if (($x == 0) and ($y == 0) and ($z == 0)) {
! # Vectors a and b are parallel, such that rotation vector
! # is the zero-length vector (0,0,0), with theta 0. To
! # remove round-off errors in theta, set it to 0
! $theta = 0;
# Such a zero-length rotation vector is annoying (e.g.
# division by 0 on normalization, and problems combining
! # rotations) Simple solution would be to set a random
! # rotation vector (e.g. 1,0,0) to go with the zero rotation
! # angle. This would satisfy as a quaternion that rotates
! # the first vector on the second, by a zero degree
! # rotation.
! #
! # A more elegant solution is to select a random rotation
# vector that is also perpendicular to both parallel
# vectors a and b. This satisfies the rotation requirement,
# and helps programs relying on the logic that the rotation
--- 678,694 ----
if (($x == 0) and ($y == 0) and ($z == 0)) {
! # Vectors a and b are parallel, such that rotation
! # vector is the zero-length vector (0,0,0), with
! # theta either 0 or pi (if vectors are opposite).
! # To remove round-off errors in theta, explicitly
! # set it.
! $theta = $dotprod > 0 ? 0 : pi;
# Such a zero-length rotation vector is annoying (e.g.
# division by 0 on normalization, and problems combining
! # rotations). To solve this, select a random rotation
# vector that is also perpendicular to both parallel
# vectors a and b. This satisfies the rotation requirement,
# and helps programs relying on the logic that the rotation
***************
*** 724,731 ****
$z = $ax*$by-$ay*$bx;
# ($x,$y,$z) is now a random yet valid rotation vector
! # perpendicular to the two original vectors. theta is still
! # 0.
}
} else {
--- 720,726 ----
$z = $ax*$by-$ay*$bx;
# ($x,$y,$z) is now a random yet valid rotation vector
! # perpendicular to the two original vectors.
}
} else {
diff -c 'Math-Quaternion-0.06.orig/t/001_basic.t' 'Math-Quaternion-0.06/t/001_basic.t'
Index: ./t/001_basic.t
*** ./t/001_basic.t Sat Aug 24 07:35:51 2013
--- ./t/001_basic.t Wed Oct 30 16:42:59 2013
***************
*** 1,4 ****
! use Test::More tests => 82;
use Math::Trig;
use strict;
use Carp;
--- 1,4 ----
! use Test::More tests => 83;
use Math::Trig;
use strict;
use Carp;
***************
*** 59,64 ****
--- 59,81 ----
}
}
+ # take two vectors as array refs; return true if they are equivalent
+ sub check_vector {
+ croak("Wrong number of args") unless (2==@_);
+
+ my ($v1, $v2) = @_;
+
+ if (
+ equal_fuzz ($v1->[0] , $v2->[0])
+ && equal_fuzz ($v1->[1] , $v2->[1])
+ && equal_fuzz ($v1->[2] , $v2->[2])
+ ) {
+ return 1;
+ } else {
+ return undef;
+ }
+ }
+
sub quatequal_fuzz {
my ($q1,$q2) = @_;
***************
*** 512,514 ****
--- 529,547 ----
});
ok( checkquat($uq,1,0,0,0),
"Creating Quaternion from two parallel vectors does not crash");
+
+
+ # quaternion from parallel but opposite vectors
+ {
+ my $v1 = [ 1, 0, 0 ];
+ my $v2 = [ -1, 0, 0 ];
+
+ my $q = Math::Quaternion->new({ v1 => $v1, v2 => $v2 });
+
+ my $v1_v2 = [ $q->rotate_vector( @$v1 ) ];
+
+ ok( check_vector( $v2, $v1_v2 ),
+ "Rotate opposite but parallel vectors" );
+
+
+ }
#### End of Patch data ####
#### ApplyPatch data follows ####
# Data version : 1.0
# Date generated : Wed Oct 30 16:48:37 2013
# Generated by : makepatch 2.05
# Recurse directories : Yes
# Excluded files : (\A|/).*\~\Z
# (\A|/).*\.a\Z
# (\A|/).*\.bak\Z
# (\A|/).*\.BAK\Z
# (\A|/).*\.elc\Z
# (\A|/).*\.exe\Z
# (\A|/).*\.gz\Z
# (\A|/).*\.ln\Z
# (\A|/).*\.o\Z
# (\A|/).*\.obj\Z
# (\A|/).*\.olb\Z
# (\A|/).*\.old\Z
# (\A|/).*\.orig\Z
# (\A|/).*\.rej\Z
# (\A|/).*\.so\Z
# (\A|/).*\.Z\Z
# (\A|/)\.del\-.*\Z
# (\A|/)\.make\.state\Z
# (\A|/)\.nse_depinfo\Z
# (\A|/)core\Z
# (\A|/)tags\Z
# (\A|/)TAGS\Z
# p 'lib/Math/Quaternion.pm' 27300 1383165930 0100644
# p 't/001_basic.t' 16070 1383165779 0100644
#### End of ApplyPatch data ####
#### End of Patch kit [created: Wed Oct 30 16:48:37 2013] ####
#### Patch checksum: 161 5621 38014 ####
#### Checksum: 179 6328 31229 ####