From 4b06b05e2d3586bf4db0e823c4a3bc11eb98b6ed Mon Sep 17 00:00:00 2001 From: Igor Gnatenko Date: Dec 06 2015 09:14:49 +0000 Subject: Initial import Signed-off-by: Igor Gnatenko --- diff --git a/.gitignore b/.gitignore index e69de29..8ddc795 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +/transforms3d-0.2.1.tar.gz diff --git a/0001-RF-allow-for-unnormalized-quaternions-in-axangle.patch b/0001-RF-allow-for-unnormalized-quaternions-in-axangle.patch new file mode 100644 index 0000000..847b0a3 --- /dev/null +++ b/0001-RF-allow-for-unnormalized-quaternions-in-axangle.patch @@ -0,0 +1,167 @@ +From c5819d5e408368157f650bf6e6b1c2bc2612bdbe Mon Sep 17 00:00:00 2001 +From: Matthew Brett +Date: Sat, 26 Sep 2015 13:56:50 -0700 +Subject: [PATCH] RF: allow for unnormalized quaternions in axangle + +quat2axangle previously assumed input quaternions were normalized. Do +normalization of the quaternion, and signal various precision-related +error conditions. +--- + transforms3d/quaternions.py | 38 +++++++++++++++++-------- + transforms3d/tests/test_quaternions.py | 51 ++++++++++++++++++++++++++++++---- + 2 files changed, 73 insertions(+), 16 deletions(-) + +diff --git a/transforms3d/quaternions.py b/transforms3d/quaternions.py +index 5d28421..1990f8a 100644 +--- a/transforms3d/quaternions.py ++++ b/transforms3d/quaternions.py +@@ -113,13 +113,12 @@ def quat2mat(q): + Notes + ----- + Rotation matrix applies to column vectors, and is applied to the +- left of coordinate vectors. The algorithm here allows non-unit +- quaternions. ++ left of coordinate vectors. The algorithm here allows quaternions that ++ have not been normalized. + + References + ---------- +- Algorithm from +- http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion ++ Algorithm from http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion + + Examples + -------- +@@ -439,20 +438,37 @@ def quat2axangle(quat, identity_thresh=None): + >>> quat2axangle([1, 0, 0, 0]) + (array([ 1., 0., 0.]), 0.0) + ++ If any of the quaternion values are not finite, we return a NaN in the ++ angle, and an arbitrary vector: ++ ++ >>> quat2axangle([1, np.inf, 0, 0]) ++ (array([ 1., 0., 0.]), nan) ++ + Notes + ----- + A quaternion for which x, y, z are all equal to 0, is an identity rotation. +- In this case we return a 0 angle and an arbitrary vector, here [1, 0, 0] ++ In this case we return a 0 angle and an arbitrary vector, here [1, 0, 0]. ++ ++ The algorithm allows for quaternions that have not been normalized. + ''' + w, x, y, z = quat +- vec = np.asarray([x, y, z]) ++ Nq = w * w + x * x + y * y + z * z ++ if not np.isfinite(Nq): ++ return np.array([1.0, 0, 0]), float('nan') + if identity_thresh is None: + try: +- identity_thresh = np.finfo(vec.dtype).eps * 3 +- except ValueError: # integer type ++ identity_thresh = np.finfo(Nq.type).eps * 3 ++ except (AttributeError, ValueError): # Not a numpy type or not float + identity_thresh = _FLOAT_EPS * 3 +- n = math.sqrt(x*x + y*y + z*z) +- if n < identity_thresh: ++ if Nq < _FLOAT_EPS ** 2: # Results unreliable after normalization ++ return np.array([1.0, 0, 0]), 0.0 ++ if Nq != 1: # Normalize if not normalized ++ s = math.sqrt(Nq) ++ w, x, y, z = w / s, x / s, y / s, z / s ++ len2 = x * x + y * y + z * z ++ if len2 < identity_thresh ** 2: + # if vec is nearly 0,0,0, this is an identity rotation + return np.array([1.0, 0, 0]), 0.0 +- return vec / n, 2 * math.acos(w) ++ # Make sure w is not slightly above 1 or below -1 ++ theta = 2 * math.acos(max(min(w, 1), -1)) ++ return np.array([x, y, z]) / math.sqrt(len2), theta +diff --git a/transforms3d/tests/test_quaternions.py b/transforms3d/tests/test_quaternions.py +index 9a8998e..9be2770 100644 +--- a/transforms3d/tests/test_quaternions.py ++++ b/transforms3d/tests/test_quaternions.py +@@ -6,7 +6,8 @@ import numpy as np + + from nose.tools import (assert_raises, assert_true, assert_equal) + +-from numpy.testing import assert_array_almost_equal, assert_array_equal ++from numpy.testing import (assert_array_almost_equal, assert_array_equal, ++ assert_almost_equal) + + from .. import quaternions as tq + from .. import axangles as taa +@@ -72,12 +73,15 @@ def test_quat2mat(): + # also tested in roundtrip case below + M = tq.quat2mat([1, 0, 0, 0]) + yield assert_array_almost_equal, M, np.eye(3) ++ # Non-unit quaternion + M = tq.quat2mat([3, 0, 0, 0]) + yield assert_array_almost_equal, M, np.eye(3) + M = tq.quat2mat([0, 1, 0, 0]) + yield assert_array_almost_equal, M, np.diag([1, -1, -1]) ++ # Non-unit quaternion, same result as normalized + M = tq.quat2mat([0, 2, 0, 0]) + yield assert_array_almost_equal, M, np.diag([1, -1, -1]) ++ yield assert_array_almost_equal, M, np.diag([1, -1, -1]) + M = tq.quat2mat([0, 0, 0, 0]) + yield assert_array_almost_equal, M, np.eye(3) + +@@ -138,13 +142,50 @@ def test_quaternion_reconstruction(): + + def test_angle_axis2quat(): + q = tq.axangle2quat([1, 0, 0], 0) +- yield assert_array_equal, q, [1, 0, 0, 0] ++ assert_array_equal(q, [1, 0, 0, 0]) + q = tq.axangle2quat([1, 0, 0], np.pi) +- yield assert_array_almost_equal, q, [0, 1, 0, 0] ++ assert_array_almost_equal(q, [0, 1, 0, 0]) + q = tq.axangle2quat([1, 0, 0], np.pi, True) +- yield assert_array_almost_equal, q, [0, 1, 0, 0] ++ assert_array_almost_equal(q, [0, 1, 0, 0]) + q = tq.axangle2quat([2, 0, 0], np.pi, False) +- yield assert_array_almost_equal, q, [0, 1, 0, 0] ++ assert_array_almost_equal(q, [0, 1, 0, 0]) ++ ++ ++def test_quat2axangle(): ++ ax, angle = tq.quat2axangle([1, 0, 0, 0]) ++ assert_array_equal(ax, [1, 0, 0]) ++ assert_array_equal(angle, 0) ++ # Non-normalized quaternion, unit quaternion ++ ax, angle = tq.quat2axangle([5, 0, 0, 0]) ++ assert_array_equal(ax, [1, 0, 0]) ++ assert_array_equal(angle, 0) ++ # Rotation by 90 degrees around x ++ r2d2 = np.sqrt(2) / 2. ++ quat_x_90 = np.array([r2d2, r2d2, 0, 0]) ++ ax, angle = tq.quat2axangle(quat_x_90) ++ assert_almost_equal(ax, [1, 0, 0]) ++ assert_almost_equal(angle, np.pi / 2) ++ # Not-normalized version of same, gives same output ++ ax, angle = tq.quat2axangle(quat_x_90 * 7) ++ assert_almost_equal(ax, [1, 0, 0]) ++ assert_almost_equal(angle, np.pi / 2) ++ # Any non-finite value gives nan angle ++ for pos in range(4): ++ for val in np.nan, np.inf, -np.inf: ++ q = [1, 0, 0, 0] ++ q[pos] = val ++ ax, angle = tq.quat2axangle(q) ++ assert_almost_equal(ax, [1, 0, 0]) ++ assert_true(np.isnan(angle)) ++ # Infinite length likewise, because of length overflow ++ f64info = np.finfo(np.float64) ++ ax, angle = tq.quat2axangle([2, f64info.max, 0, 0]) ++ assert_almost_equal(ax, [1, 0, 0]) ++ assert_true(np.isnan(angle)) ++ # Very small values give indentity transformation ++ ax, angle = tq.quat2axangle([0, f64info.eps / 2, 0, 0]) ++ assert_almost_equal(ax, [1, 0, 0]) ++ assert_equal(angle, 0) + + + def sympy_aa2mat(vec, theta): +-- +2.6.3 + diff --git a/python-transforms3d.spec b/python-transforms3d.spec new file mode 100644 index 0000000..98a8a78 --- /dev/null +++ b/python-transforms3d.spec @@ -0,0 +1,102 @@ +%global modname transforms3d + +Name: python-%{modname} +Version: 0.2.1 +Release: 3%{?dist} +Summary: 3 dimensional spatial transformations + +License: BSD +URL: http://matthew-brett.github.io/transforms3d/ +Source0: https://github.com/matthew-brett/transforms3d/archive/%{version}/%{modname}-%{version}.tar.gz +# https://github.com/matthew-brett/transforms3d/pull/5 +Patch0: 0001-RF-allow-for-unnormalized-quaternions-in-axangle.patch +BuildArch: noarch + +%description +Code to convert between various geometric transformations. + +* Composing rotations / zooms / shears / translations into affine matrix; +* Decomposing affine matrix into rotations / zooms / shears / translations; +* Conversions between different representations of rotations, including: +** 3x3 Rotation matrices; +** Euler angles; +** quaternions. + +%package -n python2-%{modname} +Summary: %{summary} +%{?python_provide:%python_provide python2-%{modname}} +BuildRequires: python2-devel +# Test deps +BuildRequires: python2-nose +BuildRequires: numpy +Requires: numpy +Recommends: sympy + +%description -n python2-%{modname} +Code to convert between various geometric transformations. + +* Composing rotations / zooms / shears / translations into affine matrix; +* Decomposing affine matrix into rotations / zooms / shears / translations; +* Conversions between different representations of rotations, including: +** 3x3 Rotation matrices; +** Euler angles; +** quaternions. + +Python 2 version. + +%package -n python3-%{modname} +Summary: %{summary} +%{?python_provide:%python_provide python3-%{modname}} +BuildRequires: python3-devel +# Test deps +BuildRequires: python3-nose +BuildRequires: python3-numpy +Requires: python3-numpy +Recommends: python3-sympy + +%description -n python3-%{modname} +Code to convert between various geometric transformations. + +* Composing rotations / zooms / shears / translations into affine matrix; +* Decomposing affine matrix into rotations / zooms / shears / translations; +* Conversions between different representations of rotations, including: +** 3x3 Rotation matrices; +** Euler angles; +** quaternions. + +Python 3 version. + +%prep +%autosetup -n %{modname}-%{version} -p1 + +%build +%py2_build +%py3_build + +%install +%py2_install +%py3_install + +%check +nosetests-%{python2_version} -v +nosetests-%{python3_version} -v + +%files -n python2-%{modname} +%license LICENSE +%doc README.rst AUTHORS +%{python2_sitelib}/%{modname}* + +%files -n python3-%{modname} +%license LICENSE +%doc README.rst AUTHORS +%{python3_sitelib}/%{modname}* + +%changelog +* Sun Dec 06 2015 Igor Gnatenko - 0.2.1-3 +- Backport another upstream patch + +* Sun Nov 01 2015 Igor Gnatenko - 0.2.1-2 +- Backport patch from upstream (nipy/nipy) + +* Sun Nov 01 2015 Igor Gnatenko - 0.2.1-1 +- Initital package diff --git a/sources b/sources index e69de29..91df0ab 100644 --- a/sources +++ b/sources @@ -0,0 +1 @@ +bbda991fd472c11c68a2be9995e02431 transforms3d-0.2.1.tar.gz