Skip to content

Commit eba14ba

Browse files
authored
More tests/bindings for Quaternion and Vectors (#103)
This revision includes more tests and bindings (Python) for Quaternion and Vectors. It fixes the issue with float types as well.
1 parent d7ae667 commit eba14ba

5 files changed

Lines changed: 325 additions & 3 deletions

File tree

include/jet/detail/quaternion-inl.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,19 +156,19 @@ inline void Quaternion<T>::set(const Matrix3x3<T>& m) {
156156
y = (m(0, 2) - m(2, 0)) / S;
157157
z = (m(1, 0) - m(0, 1)) / S;
158158
} else if (m(0, 0) > m(1, 1) && m(0, 0) > m(2, 2)) {
159-
T S = std::sqrt(1.0 + m(0, 0) - m(1, 1) - m(2, 2)) * 2;
159+
T S = std::sqrt(1 + m(0, 0) - m(1, 1) - m(2, 2)) * 2;
160160
w = (m(2, 1) - m(1, 2)) / S;
161161
x = quater * S;
162162
y = (m(0, 1) + m(1, 0)) / S;
163163
z = (m(0, 2) + m(2, 0)) / S;
164164
} else if (m(1, 1) > m(2, 2)) {
165-
T S = std::sqrt(1.0 + m(1, 1) - m(0, 0) - m(2, 2)) * 2;
165+
T S = std::sqrt(1 + m(1, 1) - m(0, 0) - m(2, 2)) * 2;
166166
w = (m(0, 2) - m(2, 0)) / S;
167167
x = (m(0, 1) + m(1, 0)) / S;
168168
y = quater * S;
169169
z = (m(1, 2) + m(2, 1)) / S;
170170
} else {
171-
T S = std::sqrt(1.0 + m(2, 2) - m(0, 0) - m(1, 1)) * 2;
171+
T S = std::sqrt(1 + m(2, 2) - m(0, 0) - m(1, 1)) * 2;
172172
w = (m(1, 0) - m(0, 1)) / S;
173173
x = (m(0, 2) + m(2, 0)) / S;
174174
y = (m(1, 2) + m(2, 1)) / S;

src/python/quaternion.cpp

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,69 @@ void addQuaternionF(pybind11::module& m) {
3030
.def_readwrite("x", &QuaternionF::x)
3131
.def_readwrite("y", &QuaternionF::y)
3232
.def_readwrite("z", &QuaternionF::z)
33+
.def("angle", &QuaternionF::angle)
34+
.def("axis", &QuaternionF::axis)
35+
.def("normalized", &QuaternionF::normalized)
36+
.def("inverse", &QuaternionF::inverse)
37+
.def("l2Norm", &QuaternionF::l2Norm)
38+
.def("setAxisAngle",
39+
[](QuaternionF& instance, py::object axis, float angle) {
40+
instance.set(objectToVector3F(axis), angle);
41+
},
42+
R"pbdoc(
43+
Sets the quaternion with given rotation axis and angle.
44+
)pbdoc",
45+
py::arg("axis"), py::arg("angle"))
46+
.def("setFromTo",
47+
[](QuaternionF& instance, py::object from, py::object to) {
48+
instance.set(objectToVector3F(from), objectToVector3F(to));
49+
},
50+
R"pbdoc(
51+
Sets the quaternion with from and to vectors.
52+
)pbdoc",
53+
py::arg("from"), py::arg("to"))
54+
.def("setRotationBasis",
55+
[](QuaternionF& instance, py::object basis0, py::object basis1,
56+
py::object basis2) {
57+
instance.set(objectToVector3F(basis0),
58+
objectToVector3F(basis1),
59+
objectToVector3F(basis2));
60+
},
61+
R"pbdoc(
62+
Sets quaternion with three basis vectors.
63+
)pbdoc",
64+
py::arg("basis0"), py::arg("basis1"), py::arg("basis2"))
65+
.def("setIdentity", &QuaternionF::setIdentity)
66+
.def("normalize", &QuaternionF::normalize)
67+
.def("rotate",
68+
[](const QuaternionF& instance, py::object other) {
69+
return instance.mul(objectToVector3F(other));
70+
},
71+
R"pbdoc(
72+
Returns this quaternion * other vector.
73+
)pbdoc",
74+
py::arg("other"))
75+
.def("dot", &QuaternionF::dot)
76+
.def("__mul__",
77+
[](const QuaternionF& instance, py::object other) {
78+
return instance.mul(objectToQuaternionF(other));
79+
},
80+
R"pbdoc(
81+
Returns this quaternion * other quaternion.
82+
)pbdoc",
83+
py::arg("other"))
84+
.def("__imul__",
85+
[](QuaternionF& instance, py::object other) {
86+
instance.imul(objectToQuaternionF(other));
87+
},
88+
R"pbdoc(
89+
This quaternion *= other quaternion.
90+
)pbdoc",
91+
py::arg("other"))
92+
.def("__setitem__", [](QuaternionF& instance, size_t i,
93+
float val) { instance[i] = val; })
94+
.def("__getitem__", [](const QuaternionF& instance,
95+
size_t i) -> float { return instance[i]; })
3396
.def("__eq__", [](const QuaternionF& instance, py::object obj) {
3497
QuaternionF other = objectToQuaternionF(obj);
3598
return instance == other;
@@ -54,6 +117,69 @@ void addQuaternionD(pybind11::module& m) {
54117
.def_readwrite("x", &QuaternionD::x)
55118
.def_readwrite("y", &QuaternionD::y)
56119
.def_readwrite("z", &QuaternionD::z)
120+
.def("angle", &QuaternionD::angle)
121+
.def("axis", &QuaternionD::axis)
122+
.def("normalized", &QuaternionD::normalized)
123+
.def("inverse", &QuaternionD::inverse)
124+
.def("l2Norm", &QuaternionD::l2Norm)
125+
.def("setAxisAngle",
126+
[](QuaternionD& instance, py::object axis, double angle) {
127+
instance.set(objectToVector3D(axis), angle);
128+
},
129+
R"pbdoc(
130+
Sets the quaternion with given rotation axis and angle.
131+
)pbdoc",
132+
py::arg("axis"), py::arg("angle"))
133+
.def("setFromTo",
134+
[](QuaternionD& instance, py::object from, py::object to) {
135+
instance.set(objectToVector3D(from), objectToVector3D(to));
136+
},
137+
R"pbdoc(
138+
Sets the quaternion with from and to vectors.
139+
)pbdoc",
140+
py::arg("from"), py::arg("to"))
141+
.def("setRotationBasis",
142+
[](QuaternionD& instance, py::object basis0, py::object basis1,
143+
py::object basis2) {
144+
instance.set(objectToVector3D(basis0),
145+
objectToVector3D(basis1),
146+
objectToVector3D(basis2));
147+
},
148+
R"pbdoc(
149+
Sets quaternion with three basis vectors.
150+
)pbdoc",
151+
py::arg("basis0"), py::arg("basis1"), py::arg("basis2"))
152+
.def("setIdentity", &QuaternionD::setIdentity)
153+
.def("normalize", &QuaternionD::normalize)
154+
.def("rotate",
155+
[](const QuaternionD& instance, py::object other) {
156+
return instance.mul(objectToVector3D(other));
157+
},
158+
R"pbdoc(
159+
Returns this quaternion * other vector.
160+
)pbdoc",
161+
py::arg("other"))
162+
.def("dot", &QuaternionD::dot)
163+
.def("__mul__",
164+
[](const QuaternionD& instance, py::object other) {
165+
return instance.mul(objectToQuaternionD(other));
166+
},
167+
R"pbdoc(
168+
Returns this quaternion * other quaternion.
169+
)pbdoc",
170+
py::arg("other"))
171+
.def("__imul__",
172+
[](QuaternionD& instance, py::object other) {
173+
instance.imul(objectToQuaternionD(other));
174+
},
175+
R"pbdoc(
176+
This quaternion *= other quaternion.
177+
)pbdoc",
178+
py::arg("other"))
179+
.def("__setitem__", [](QuaternionD& instance, size_t i,
180+
double val) { instance[i] = val; })
181+
.def("__getitem__", [](const QuaternionD& instance,
182+
size_t i) -> double { return instance[i]; })
57183
.def("__eq__", [](const QuaternionD& instance, py::object obj) {
58184
QuaternionD other = objectToQuaternionD(obj);
59185
return instance == other;

src/python/vector.cpp

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,44 @@ void addVector2F(pybind11::module& m) {
2525
py::arg("x") = 0.0f, py::arg("y") = 0.0f)
2626
.def_readwrite("x", &Vector2F::x)
2727
.def_readwrite("y", &Vector2F::y)
28+
.def("setZero", &Vector2F::setZero)
29+
.def("normalize", &Vector2F::normalize)
30+
.def("dot",
31+
[](const Vector2F& instance, py::object other) {
32+
return instance.dot(objectToVector2F(other));
33+
})
34+
.def("cross",
35+
[](const Vector2F& instance, py::object other) {
36+
return instance.cross(objectToVector2F(other));
37+
})
38+
.def("sum", &Vector2F::sum)
39+
.def("avg", &Vector2F::avg)
40+
.def("min", &Vector2F::min)
41+
.def("max", &Vector2F::max)
42+
.def("absmin", &Vector2F::absmin)
43+
.def("absmax", &Vector2F::absmax)
44+
.def("dominantAxis", &Vector2F::dominantAxis)
45+
.def("subminantAxis", &Vector2F::subminantAxis)
46+
.def("normalized", &Vector2F::normalized)
47+
.def("length", &Vector2F::length)
48+
.def("lengthSquared", &Vector2F::lengthSquared)
49+
.def("distanceTo",
50+
[](const Vector2F& instance, py::object other) {
51+
return instance.distanceTo(objectToVector2F(other));
52+
})
53+
.def("distanceSquaredTo",
54+
[](const Vector2F& instance, py::object other) {
55+
return instance.distanceSquaredTo(objectToVector2F(other));
56+
})
57+
.def("reflected",
58+
[](const Vector2F& instance, py::object other) {
59+
return instance.reflected(objectToVector2F(other));
60+
})
61+
.def("projected",
62+
[](const Vector2F& instance, py::object other) {
63+
return instance.projected(objectToVector2F(other));
64+
})
65+
.def("tangential", &Vector2F::tangential)
2866
.def("__getitem__", [](const Vector2F& instance,
2967
size_t i) -> float { return instance[i]; })
3068
.def("__setitem__",
@@ -97,6 +135,44 @@ void addVector2D(pybind11::module& m) {
97135
py::arg("x") = 0.0, py::arg("y") = 0.0)
98136
.def_readwrite("x", &Vector2D::x)
99137
.def_readwrite("y", &Vector2D::y)
138+
.def("setZero", &Vector2D::setZero)
139+
.def("normalize", &Vector2D::normalize)
140+
.def("dot",
141+
[](const Vector2D& instance, py::object other) {
142+
return instance.dot(objectToVector2D(other));
143+
})
144+
.def("cross",
145+
[](const Vector2D& instance, py::object other) {
146+
return instance.cross(objectToVector2D(other));
147+
})
148+
.def("sum", &Vector2D::sum)
149+
.def("avg", &Vector2D::avg)
150+
.def("min", &Vector2D::min)
151+
.def("max", &Vector2D::max)
152+
.def("absmin", &Vector2D::absmin)
153+
.def("absmax", &Vector2D::absmax)
154+
.def("dominantAxis", &Vector2D::dominantAxis)
155+
.def("subminantAxis", &Vector2D::subminantAxis)
156+
.def("normalized", &Vector2D::normalized)
157+
.def("length", &Vector2D::length)
158+
.def("lengthSquared", &Vector2D::lengthSquared)
159+
.def("distanceTo",
160+
[](const Vector2D& instance, py::object other) {
161+
return instance.distanceTo(objectToVector2D(other));
162+
})
163+
.def("distanceSquaredTo",
164+
[](const Vector2D& instance, py::object other) {
165+
return instance.distanceSquaredTo(objectToVector2D(other));
166+
})
167+
.def("reflected",
168+
[](const Vector2D& instance, py::object other) {
169+
return instance.reflected(objectToVector2D(other));
170+
})
171+
.def("projected",
172+
[](const Vector2D& instance, py::object other) {
173+
return instance.projected(objectToVector2D(other));
174+
})
175+
.def("tangential", &Vector2D::tangential)
100176
.def("__getitem__", [](const Vector2D& instance,
101177
size_t i) -> double { return instance[i]; })
102178
.def("__setitem__", [](Vector2D& instance, size_t i,
@@ -169,6 +245,44 @@ void addVector3F(pybind11::module& m) {
169245
.def_readwrite("x", &Vector3F::x)
170246
.def_readwrite("y", &Vector3F::y)
171247
.def_readwrite("z", &Vector3F::z)
248+
.def("setZero", &Vector3F::setZero)
249+
.def("normalize", &Vector3F::normalize)
250+
.def("dot",
251+
[](const Vector3F& instance, py::object other) {
252+
return instance.dot(objectToVector3F(other));
253+
})
254+
.def("cross",
255+
[](const Vector3F& instance, py::object other) {
256+
return instance.cross(objectToVector3F(other));
257+
})
258+
.def("sum", &Vector3F::sum)
259+
.def("avg", &Vector3F::avg)
260+
.def("min", &Vector3F::min)
261+
.def("max", &Vector3F::max)
262+
.def("absmin", &Vector3F::absmin)
263+
.def("absmax", &Vector3F::absmax)
264+
.def("dominantAxis", &Vector3F::dominantAxis)
265+
.def("subminantAxis", &Vector3F::subminantAxis)
266+
.def("normalized", &Vector3F::normalized)
267+
.def("length", &Vector3F::length)
268+
.def("lengthSquared", &Vector3F::lengthSquared)
269+
.def("distanceTo",
270+
[](const Vector3F& instance, py::object other) {
271+
return instance.distanceTo(objectToVector3F(other));
272+
})
273+
.def("distanceSquaredTo",
274+
[](const Vector3F& instance, py::object other) {
275+
return instance.distanceSquaredTo(objectToVector3F(other));
276+
})
277+
.def("reflected",
278+
[](const Vector3F& instance, py::object other) {
279+
return instance.reflected(objectToVector3F(other));
280+
})
281+
.def("projected",
282+
[](const Vector3F& instance, py::object other) {
283+
return instance.projected(objectToVector3F(other));
284+
})
285+
.def("tangential", &Vector3F::tangential)
172286
.def("__getitem__", [](const Vector3F& instance,
173287
size_t i) -> float { return instance[i]; })
174288
.def("__setitem__",
@@ -242,6 +356,44 @@ void addVector3D(pybind11::module& m) {
242356
.def_readwrite("x", &Vector3D::x)
243357
.def_readwrite("y", &Vector3D::y)
244358
.def_readwrite("z", &Vector3D::z)
359+
.def("setZero", &Vector3D::setZero)
360+
.def("normalize", &Vector3D::normalize)
361+
.def("dot",
362+
[](const Vector3D& instance, py::object other) {
363+
return instance.dot(objectToVector3D(other));
364+
})
365+
.def("cross",
366+
[](const Vector3D& instance, py::object other) {
367+
return instance.cross(objectToVector3D(other));
368+
})
369+
.def("sum", &Vector3D::sum)
370+
.def("avg", &Vector3D::avg)
371+
.def("min", &Vector3D::min)
372+
.def("max", &Vector3D::max)
373+
.def("absmin", &Vector3D::absmin)
374+
.def("absmax", &Vector3D::absmax)
375+
.def("dominantAxis", &Vector3D::dominantAxis)
376+
.def("subminantAxis", &Vector3D::subminantAxis)
377+
.def("normalized", &Vector3D::normalized)
378+
.def("length", &Vector3D::length)
379+
.def("lengthSquared", &Vector3D::lengthSquared)
380+
.def("distanceTo",
381+
[](const Vector3D& instance, py::object other) {
382+
return instance.distanceTo(objectToVector3D(other));
383+
})
384+
.def("distanceSquaredTo",
385+
[](const Vector3D& instance, py::object other) {
386+
return instance.distanceSquaredTo(objectToVector3D(other));
387+
})
388+
.def("reflected",
389+
[](const Vector3D& instance, py::object other) {
390+
return instance.reflected(objectToVector3D(other));
391+
})
392+
.def("projected",
393+
[](const Vector3D& instance, py::object other) {
394+
return instance.projected(objectToVector3D(other));
395+
})
396+
.def("tangential", &Vector3D::tangential)
245397
.def("__getitem__", [](const Vector3D& instance,
246398
size_t i) -> double { return instance[i]; })
247399
.def("__setitem__", [](Vector3D& instance, size_t i,

src/tests/python_tests/quaternion_tests.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
third parties.
77
"""
88

9+
import math
910
import pyjet
1011
import unittest
1112

@@ -28,6 +29,36 @@ def testInit(self):
2829
self.assertEqual(c.y, 3.0)
2930
self.assertEqual(c.z, 4.0)
3031

32+
def testGetters(self):
33+
a = pyjet.QuaternionD(1, 2, 3, 4)
34+
self.assertEqual(a[0], a.w)
35+
self.assertEqual(a[1], a.x)
36+
self.assertEqual(a[2], a.y)
37+
self.assertEqual(a[3], a.z)
38+
a.normalize()
39+
axis = a.axis()
40+
angle = a.angle()
41+
denom = math.sqrt(1 - a.w * a.w)
42+
self.assertAlmostEqual(axis.x, math.sqrt(2.0 / 15.0) / denom)
43+
self.assertAlmostEqual(axis.y, math.sqrt(3.0 / 10.0) / denom)
44+
self.assertAlmostEqual(axis.z, 2.0 * math.sqrt(2.0 / 15.0) / denom)
45+
self.assertAlmostEqual(angle, 2.0 * math.acos(1.0 / math.sqrt(30.0)))
46+
47+
def testSetters(self):
48+
a = pyjet.QuaternionD()
49+
a.setAxisAngle((0, -1, 0), math.pi / 2)
50+
axis = a.axis()
51+
angle = a.angle()
52+
self.assertEqual(axis.x, 0)
53+
self.assertEqual(axis.y, -1)
54+
self.assertEqual(axis.z, 0)
55+
self.assertEqual(angle, math.pi / 2)
56+
a[0] = 1.0; a[1] = 2.0; a[2] = 3.0; a[3] = 4.0
57+
self.assertEqual(1, a.w)
58+
self.assertEqual(2, a.x)
59+
self.assertEqual(3, a.y)
60+
self.assertEqual(4, a.z)
61+
3162

3263
def main():
3364
pyjet.Logging.mute()

0 commit comments

Comments
 (0)