Skip to content

Commit

Permalink
adding MVector support and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
scottenglert committed May 17, 2022
1 parent a6175b1 commit ce722b8
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 0 deletions.
73 changes: 73 additions & 0 deletions src/Math.inl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ Vector
py::arg("y"),
py::arg("z"))
.def(py::init<const MVector &>(), py::arg("src"))
.def(py::init([](const py::sequence & seq) {
if (seq.size() < 2 || seq.size() > 3)
{
throw std::invalid_argument("You must provide a list of 2 or 3 floats.");
}
double tmp[3] = {0.0, 0.0, 0.0};
std::transform(std::begin(seq), std::end(seq), std::begin(tmp),
[](pybind11::handle handle) -> double { return handle.cast<double>(); });

return std::unique_ptr<MVector>(new MVector(tmp[0], tmp[1], tmp[2]));
}), "Create a new vector from a seqence or 2 or 3 floats")

.def_readwrite("x", &MVector::x)
.def_readwrite("y", &MVector::y)
.def_readwrite("z", &MVector::z)
Expand All @@ -16,13 +28,74 @@ Vector
.def(py::self += py::self, py::arg("other"))
.def(py::self -= py::self, py::arg("other"))
.def(py::self *= double(), py::arg("other"))
.def(py::self *= MMatrix(), py::arg("other"))
.def(py::self /= double(), py::arg("other"))
.def(py::self * double(), py::arg("other"))
.def(py::self * MMatrix(), py::arg("other"))
.def(py::self * py::self, py::arg("other"))
.def(py::self / double(), py::arg("other"))
.def(py::self == py::self, py::arg("other"))
.def(py::self != py::self, py::arg("other"))
.def(py::self ^ py::self, py::arg("other"))
.def(-py::self)

.def("angle", &MVector::angle, py::arg("other"),
"Returns the angle, in radians, between this vector and another.")

.def("isEquivalent", &MVector::isEquivalent, py::arg("other"), py::arg("tolerance"),
"Returns True if this vector and another are within a given tolerance of being equal.")

.def("isParallel", &MVector::isParallel,
py::arg("other"), py::arg("tolerance"),
"Returns True if this vector and another are within the given tolerance of being parallel.")

.def("length", &MVector::length,
"Returns the magnitude of this vector.")

.def("normal", &MVector::normal,
"Returns a new vector containing the normalized version of this one.")

.def("normalize", &MVector::normalize,
"Normalizes this vector in-place and returns a new reference to it.")

.def("rotateBy", [](const MVector &self, const MQuaternion& rot){
return self.rotateBy(rot);
}, py::arg("rot"), "Returns the vector resulting from rotating this one by the given amount.")

.def("rotateBy", [](const MVector &self, const MEulerRotation& rot){
return self.rotateBy(rot);
}, py::arg("rot"), "Returns the vector resulting from rotating this one by the given amount.")

.def("rotateTo", &MVector::rotateTo, py::arg("target"),
"Returns the quaternion which will rotate this vector into another.")

.def("transformAsNormal", &MVector::transformAsNormal, py::arg("matrix"),
"Returns a new vector which is calculated by postmultiplying this vector by the transpose of the given matrix's inverse and then normalizing the result.")

.def_property_readonly_static("oneVector", [](py::object) { return MVector::one; },
"The vector <1,1,1>")

.def_property_readonly_static("zeroVector", [](py::object) { return MVector::zero; },
"The vector <0,0,0>")

.def_property_readonly_static("xAxisVector", [](py::object) { return MVector::xAxis; },
"The vector <1,0,0>")

.def_property_readonly_static("xNegAxisVector", [](py::object) { return MVector::xNegAxis; },
"The vector <-1,0,0>")

.def_property_readonly_static("yAxisVector", [](py::object) { return MVector::yAxis; },
"The vector <0,1,0>")

.def_property_readonly_static("yNegAxisVector", [](py::object) { return MVector::yNegAxis; },
"The vector <0,-1,0>")

.def_property_readonly_static("zAxisVector", [](py::object) { return MVector::zAxis; },
"The vector <0,0,1>")

.def_property_readonly_static("zNegAxisVector", [](py::object) { return MVector::zNegAxis; },
"The vector <0,0,-1>")

// Support print()
.def("__repr__", [](const MVector &a) {
return "<cmdc.Vector( " +
Expand Down
130 changes: 130 additions & 0 deletions tests/test_MVector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import nose.tools

import cmdc


def test_new_vector():
default = cmdc.Vector()
assert default.x == 0.0 and default.y == 0.0 and default.z == 0.0

default.x = 1
default.y = 2
default.z = 3
assert default.x == 1.0 and default.y == 2.0 and default.z == 3.0

a = cmdc.Vector(1, 2, 3)
assert a.x == 1.0 and a.y == 2.0 and a.z == 3.0

b = cmdc.Vector([4, 5, 6])
assert b.x == 4.0 and b.y == 5.0 and b.z == 6.0

c = cmdc.Vector([7, 8])
assert c.x == 7.0 and c.y == 8.0 and c.z == 0.0

d = cmdc.Vector((1, 2, 3))
assert d.x == 1.0 and d.y == 2.0 and d.z == 3.0

nose.tools.assert_raises(
ValueError,
cmdc.Vector,
[9]
)

nose.tools.assert_raises(
ValueError,
cmdc.Vector,
[9, 10, 11, 12]
)

copy = cmdc.Vector(a)
assert a is not copy

one = cmdc.Vector.oneVector
assert one.x == 1.0 and one.y == 1.0 and one.z == 1.0

zero = cmdc.Vector.zeroVector
assert zero.x == 0.0 and zero.y == 0.0 and zero.z == 0.0

xAxis = cmdc.Vector.xAxisVector
assert xAxis.x == 1.0 and xAxis.y == 0.0 and xAxis.z == 0.0

xNegAxis = cmdc.Vector.xNegAxisVector
assert xNegAxis.x == -1.0 and xNegAxis.y == 0.0 and xNegAxis.z == 0.0

yAxis = cmdc.Vector.yAxisVector
assert yAxis.x == 0.0 and yAxis.y == 1.0 and yAxis.z == 0.0

yNegAxis = cmdc.Vector.yNegAxisVector
assert yNegAxis.x == 0.0 and yNegAxis.y == -1.0 and yNegAxis.z == 0.0

zAxis = cmdc.Vector.zAxisVector
assert zAxis.x == 0.0 and zAxis.y == 0.0 and zAxis.z == 1.0

zNegAxis = cmdc.Vector.zNegAxisVector
assert zNegAxis.x == 0.0 and zNegAxis.y == 0.0 and zNegAxis.z == -1.0

def test_add():
xy = cmdc.Vector.xAxisVector + cmdc.Vector.yAxisVector
assert xy.x == 1.0 and xy.y == 1.0 and xy.z == 0

xy += cmdc.Vector.zAxisVector
assert xy.x == 1.0 and xy.y == 1.0 and xy.z == 1.0

def test_multiply():
result = cmdc.Vector.oneVector * 2.0
assert result.x == 2.0 and result.y == 2.0 and result.z == 2.0

result *= 3.0
assert result.x == 6.0 and result.y == 6.0 and result.z == 6.0

result = cmdc.Vector.xAxisVector * cmdc.Matrix()
assert result == cmdc.Vector.xAxisVector

def test_subtract():
xy = cmdc.Vector.xAxisVector - cmdc.Vector.yAxisVector
assert xy.x == 1.0 and xy.y == -1.0 and xy.z == 0

xy -= cmdc.Vector.zAxisVector
assert xy.x == 1.0 and xy.y == -1.0 and xy.z == -1.0

invVector = -xy
assert invVector.x == -1.0 and invVector.y == 1.0 and invVector.z == 1.0

def test_divide():
result = cmdc.Vector.oneVector / 2.0
assert result.x == 0.5 and result.y == 0.5 and result.z == 0.5

result /= 0.5
assert result.x == 1.0 and result.y == 1.0 and result.z == 1.0

def test_dot():
assert cmdc.Vector.xAxisVector * cmdc.Vector.xAxisVector == 1.0
assert cmdc.Vector.xAxisVector * cmdc.Vector.yAxisVector == 0.0

def test_cross():
xAxis = cmdc.Vector.yAxisVector ^ cmdc.Vector.zAxisVector
assert xAxis.x == 1.0 and xAxis.y == 0.0 and xAxis.z == 0.0

def test_normal():
vector = cmdc.Vector.yAxisVector * 2
assert vector.length() == 2.0

normalized = vector.normal()
assert normalized.length() == 1.0
assert vector.length() == 2.0

vector.normalize()
assert vector.length() == 1.0

def test_extra():
angle = cmdc.Vector.xAxisVector.angle(cmdc.Vector.yAxisVector)
assert (angle - 1.570) < 0.001

assert cmdc.Vector.xAxisVector.isParallel(cmdc.Vector.xAxisVector, 0.001)
assert not cmdc.Vector.xAxisVector.isParallel(cmdc.Vector.yAxisVector, 0.001)

assert cmdc.Vector.zAxisVector.isEquivalent(cmdc.Vector.zAxisVector, 0.001)
assert not cmdc.Vector.zAxisVector.isEquivalent(cmdc.Vector.zeroVector, 0.001)

assert cmdc.Vector.oneVector == cmdc.Vector.oneVector
assert cmdc.Vector.oneVector != cmdc.Vector.zeroVector

0 comments on commit ce722b8

Please sign in to comment.