From a6175b141d89b519bae041eaa33272e04294a669 Mon Sep 17 00:00:00 2001 From: Scott Englert Date: Sun, 8 May 2022 20:27:47 -0700 Subject: [PATCH 01/11] Adding Transform related classes Adding MFnTransform, MTransformationMatrix, MEulerRotation, and more to start to support transform function sets --- src/ForwardDeclarations.inl | 8 +- src/MEulerRotation.inl | 194 +++++++++++ src/MFnTransform.inl | 507 ++++++++++++++++++++++++++++ src/MTransformationMatrix.inl | 322 ++++++++++++++++++ src/Math.inl | 15 +- src/Types.inl | 11 + src/main.cpp | 6 + tests/test_MFnTransform.py | 24 ++ tests/test_MTransformationMatrix.py | 54 +++ 9 files changed, 1137 insertions(+), 4 deletions(-) create mode 100644 src/MEulerRotation.inl create mode 100644 src/MFnTransform.inl create mode 100644 src/MTransformationMatrix.inl create mode 100644 tests/test_MFnTransform.py create mode 100644 tests/test_MTransformationMatrix.py diff --git a/src/ForwardDeclarations.inl b/src/ForwardDeclarations.inl index 08f719c..ddc5144 100644 --- a/src/ForwardDeclarations.inl +++ b/src/ForwardDeclarations.inl @@ -15,6 +15,7 @@ py::class_ Plug(m, "Plug"); py::class_ Point(m, "Point"); py::class_ PxData(m, "PxData"); py::class_ Quaternion(m, "Quaternion"); +py::class_ EulerRotation(m, "EulerRotation"); py::class_ SelectionList(m, "SelectionList"); py::class_ Status(m, "Status"); py::class_ String(m, "String"); @@ -25,6 +26,9 @@ py::enum_ fn_type(Fn, "Type"); py::class_ Uuid(m, "Uuid"); py::class_ NodeClass(m, "NodeClass"); py::class_ DGModifier(m, "DGModifier"); -py::class_ FnDagNode(m, "FnDagNode"); +py::class_ FnDagNode(m, "FnDagNode"); py::class_ BoundingBox(m, "BoundingBox"); -py::class_ Color(m, "Color"); \ No newline at end of file +py::class_ Color(m, "Color"); +py::class_ Space(m, "Space"); +py::class_ TransformationMatrix(m, "TransformationMatrix"); +py::class_ FnTransform(m, "FnTransform"); \ No newline at end of file diff --git a/src/MEulerRotation.inl b/src/MEulerRotation.inl new file mode 100644 index 0000000..8e74a79 --- /dev/null +++ b/src/MEulerRotation.inl @@ -0,0 +1,194 @@ +#define _doc_EulerRotation_setValue \ + "Set the rotation." + +#define _doc_EulerRotation_asQuaternion \ + "Returns the rotation as an equivalent quaternion." + +#define _doc_EulerRotation_asMatrix \ + "Returns the rotation as an equivalent matrix." + +#define _doc_EulerRotation_asVector \ + "Returns the X, Y and Z rotations as a vector." + +#define _doc_EulerRotation_isEquivalent \ + "Returns true if this rotation has the same order as another and their X, Y and Z components are within a tolerance of each other." + +#define _doc_EulerRotation_isZero \ + "Returns true if the X, Y and Z components are each within a tolerance of 0.0." + +#define _doc_EulerRotation_incrementalRotateBy \ + "Increase this rotation by a given angle around the specified axis. The update is done in series of small increments to avoid flipping." + +#define _doc_EulerRotation_inverse \ + "Returns a new MEulerRotation containing the inverse rotation of this one and reversed rotation order." + +#define _doc_EulerRotation_invertIt \ + "In-place inversion of the rotation. Rotation order is also reversed." + +#define _doc_EulerRotation_reorder \ + "Returns a new MEulerRotation having this rotation, reordered to use the given rotation order." + +#define _doc_EulerRotation_reorderIt \ + "In-place reordering to use the given rotation order." + +#define _doc_EulerRotation_bound \ + "Returns a new MEulerRotation having this rotation, but with each rotation component bound within +/- PI." + +#define _doc_EulerRotation_boundIt \ + "In-place bounding of each rotation component to lie wthin +/- PI." + +#define _doc_EulerRotation_alternateSolution \ + "Returns an equivalent rotation which is not simply a multiple." + +#define _doc_EulerRotation_setToAlternateSolution \ + "Replace this rotation with an alternate solution." + +#define _doc_EulerRotation_closestSolution \ + "Returns the equivalent rotation which comes closest to a target." + +#define _doc_EulerRotation_setToClosestSolution \ + "Replace this rotation with the closest solution to a target." + +#define _doc_EulerRotation_closestCut \ + "Returns the rotation which is full spin multiples of this one and comes closest to target." + +#define _doc_EulerRotation_setToClosestCut \ + "Replace this rotation with the closest cut to a target." + +#define _doc_EulerRotation_decompose \ + "Extracts a rotation from a matrix." + +py::enum_(EulerRotation, "RotationOrder") + .value("kXYZ", MEulerRotation::RotationOrder::kXYZ) + .value("kYZX", MEulerRotation::RotationOrder::kYZX) + .value("kZXY", MEulerRotation::RotationOrder::kZXY) + .value("kXZY", MEulerRotation::RotationOrder::kXZY) + .value("kYXZ", MEulerRotation::RotationOrder::kYXZ) + .value("kZYX", MEulerRotation::RotationOrder::kZYX) + .export_values(); + +EulerRotation + .def(py::init<>()) + .def(py::init(), py::arg("src")) + .def(py::init(), py::arg("vec"), py::arg("order") = MEulerRotation::kXYZ) + + .def_readwrite("x", &MEulerRotation::x) + .def_readwrite("y", &MEulerRotation::y) + .def_readwrite("z", &MEulerRotation::z) + .def_readwrite("order", &MEulerRotation::order) + + .def(py::self + MEulerRotation(), py::arg("other")) + .def(py::self - MEulerRotation(), py::arg("other")) + .def(py::self * MEulerRotation(), py::arg("other")) + .def(py::self *= MEulerRotation(), py::arg("other")) + .def(py::self * MQuaternion(), py::arg("other")) + .def(py::self *= MQuaternion(), py::arg("other")) + .def(py::self * double(), py::arg("other")) + .def(py::self *= double(), py::arg("other")) + .def(py::self == MEulerRotation(), py::arg("other")) + .def(py::self != MEulerRotation(), py::arg("other")) + .def(-py::self) + + .def("__repr__", [](const MEulerRotation &a) { + return ""; + } + ) + + .def("alternateSolution", [](const MEulerRotation& self) { + return self.alternateSolution(); + }, _doc_EulerRotation_alternateSolution) + + .def("asMatrix", &MEulerRotation::asMatrix, _doc_EulerRotation_asMatrix) + + .def("asQuaternion", &MEulerRotation::asQuaternion, _doc_EulerRotation_asQuaternion) + + .def("asVector", &MEulerRotation::asVector, _doc_EulerRotation_asVector) + + .def("bound", [](const MEulerRotation& self) { + return self.bound(); + }, _doc_EulerRotation_bound) + + // .def("boundIt", [](MEulerRotation & self) -> & { + // throw std::logic_error{"Function not yet implemented."}; + // }, _doc_EulerRotation_boundIt) + + // .def("boundIt", [](MEulerRotation & self, MEulerRotation src) -> & { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("src"), _doc_EulerRotation_boundIt) + + // .def("closestCut", [](MEulerRotation & self, MEulerRotation dst) -> MEulerRotation { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("dst"), _doc_EulerRotation_closestCut) + + // .def("closestSolution", [](MEulerRotation & self, MEulerRotation dst) -> MEulerRotation { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("dst"), _doc_EulerRotation_closestSolution) + + // .def_static("decompose", [](MMatrix matrix, MEulerRotation::RotationOrder ord) -> MEulerRotation { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("matrix"), py::arg("ord"), _doc_EulerRotation_decompose) + + // .def("incrementalRotateBy", [](MEulerRotation & self, MVector axis, double angle) -> & { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("axis"), py::arg("angle"), _doc_EulerRotation_incrementalRotateBy) + + // .def("inverse", [](MEulerRotation & self) -> MEulerRotation { + // throw std::logic_error{"Function not yet implemented."}; + // }, _doc_EulerRotation_inverse) + + // .def("invertIt", [](MEulerRotation & self) -> & { + // throw std::logic_error{"Function not yet implemented."}; + // }, _doc_EulerRotation_invertIt) + + // .def("isEquivalent", [](MEulerRotation & self, MEulerRotation other, double tolerance = kEulerRotationEpsilon) -> bool { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("other"), py::arg("tolerance") = kEulerRotationEpsilon, _doc_EulerRotation_isEquivalent) + + // .def("isZero", [](MEulerRotation & self, double tolerance = kEulerRotationEpsilon) -> bool { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("tolerance") = kEulerRotationEpsilon, _doc_EulerRotation_isZero) + + // .def("reorder", [](MEulerRotation & self, MEulerRotation::RotationOrder ord) -> MEulerRotation { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("ord"), _doc_EulerRotation_reorder) + + // .def("reorderIt", [](MEulerRotation & self, MEulerRotation::RotationOrder ord) -> & { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("ord"), _doc_EulerRotation_reorderIt) + + // .def("setToAlternateSolution", [](MEulerRotation & self) -> & { + // throw std::logic_error{"Function not yet implemented."}; + // }, _doc_EulerRotation_setToAlternateSolution) + + // .def("setToAlternateSolution", [](MEulerRotation & self, MEulerRotation src) -> & { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("src"), _doc_EulerRotation_setToAlternateSolution) + + // .def("setToClosestCut", [](MEulerRotation & self, MEulerRotation dst) -> & { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("dst"), _doc_EulerRotation_setToClosestCut) + + // .def("setToClosestCut", [](MEulerRotation & self, MEulerRotation src, MEulerRotation dst) -> & { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("src"), py::arg("dst"), _doc_EulerRotation_setToClosestCut) + + // .def("setToClosestSolution", [](MEulerRotation & self, MEulerRotation dst) -> & { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("dst"), _doc_EulerRotation_setToClosestSolution) + + // .def("setToClosestSolution", [](MEulerRotation & self, MEulerRotation src, MEulerRotation dst) -> & { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("src"), py::arg("dst"), _doc_EulerRotation_setToClosestSolution) + + // .def("setValue", [](MEulerRotation & self, MVector v, MEulerRotation::RotationOrder ord = MEulerRotation::kXYZ) -> & { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("v"), py::arg("ord") = MEulerRotation::kXYZ, _doc_EulerRotation_setValue) + + // .def("setValue", [](MEulerRotation & self, double xx, double yy, double zz, MEulerRotation::RotationOrder ord = MEulerRotation::kXYZ) -> & { + // throw std::logic_error{"Function not yet implemented."}; + // }, py::arg("xx"), py::arg("yy"), py::arg("zz"), py::arg("ord") = MEulerRotation::kXYZ, _doc_EulerRotation_setValue); + ; \ No newline at end of file diff --git a/src/MFnTransform.inl b/src/MFnTransform.inl new file mode 100644 index 0000000..21d46ae --- /dev/null +++ b/src/MFnTransform.inl @@ -0,0 +1,507 @@ +#define _doc_FnTransform_create \ + "Creates a new transform node and attaches it to the function set." + +#define _doc_FnTransform_transformation \ + "Returns the transformation matrix represented by this transform." + +#define _doc_FnTransform_setTranslation \ + "Sets the transform's translation." + +#define _doc_FnTransform_translateBy \ + "Adds an MVector to the transform's translation." + +#define _doc_FnTransform_setScale \ + "Sets the transform's scale components." + +#define _doc_FnTransform_scaleBy \ + "Multiplies the transform's XYZ scale components by a sequence of three floats." + +#define _doc_FnTransform_scalePivot \ + "Returns the transform's scale pivot." + +#define _doc_FnTransform_setScalePivot \ + "Sets the transform's scale pivot." + +#define _doc_FnTransform_scalePivotTranslation \ + "Returns the transform's scale pivot translation." + +#define _doc_FnTransform_setScalePivotTranslation \ + "Sets the transform's scale pivot translation." + +#define _doc_FnTransform_setShear \ + "Sets the transform's shear." + +#define _doc_FnTransform_shearBy \ + "Multiplies the transform's shear components by a sequence of three floats." + +#define _doc_FnTransform_setRotation \ + "Sets the transform's rotation using an MEulerRotation or MQuaternion." + +#define _doc_FnTransform_rotateBy \ + "Adds an MEulerRotation or MQuaternion to the transform's rotation." + +#define _doc_FnTransform_rotatePivot \ + "Returns the transform's rotate pivot." + +#define _doc_FnTransform_setRotatePivot \ + "Sets the transform's rotate pivot." + +#define _doc_FnTransform_rotatePivotTranslation \ + "Returns the transform's rotate pivot translation." + +#define _doc_FnTransform_setRotatePivotTranslation \ + "Sets the transform's rotate pivot translation." + +#define _doc_FnTransform_rotateOrientation \ + "Returns the MQuaternion which orients the local rotation space." + +#define _doc_FnTransform_setRotateOrientation \ + "Sets the MQuaternion which orients the local rotation space." + +#define _doc_FnTransform_rotationOrder \ + "Returns the order of rotations when the transform's rotation is expressed as an MEulerRotation." + +#define _doc_FnTransform_setRotationOrder \ + "Sets the transform's rotation order." + +#define _doc_FnTransform_restPosition \ + "Returns the transform's rest position matrix." + +#define _doc_FnTransform_setRestPosition \ + "Sets the transform's rest position matrix." + +#define _doc_FnTransform_resetFromRestPosition \ + "Resets the transform from its rest position matrix." + +#define _doc_FnTransform_clearRestPosition \ + "Clears the transform's rest position matrix." + +#define _doc_FnTransform_isLimited \ + "Returns True if the specified limit type is enabled." + +#define _doc_FnTransform_limitValue \ + "Returns the value of the specified limit." + +#define _doc_FnTransform_setLimit \ + "Sets the value of the specified limit." + +#define _doc_FnTransform_enableLimit \ + "Enables or disables a specified limit type." + +py::enum_(FnTransform, "LimitType") + .value("kScaleMinX", MFnTransform::kScaleMinX) + .value("kScaleMaxX", MFnTransform::kScaleMaxX) + .value("kScaleMinY", MFnTransform::kScaleMinY) + .value("kScaleMaxY", MFnTransform::kScaleMaxY) + .value("kScaleMinZ", MFnTransform::kScaleMinZ) + .value("kScaleMaxZ", MFnTransform::kScaleMaxZ) + + .value("kShearMinXY", MFnTransform::kShearMinXY) + .value("kShearMaxXY", MFnTransform::kShearMaxXY) + .value("kShearMinXZ", MFnTransform::kShearMinXZ) + .value("kShearMaxXZ", MFnTransform::kShearMaxXZ) + .value("kShearMinYZ", MFnTransform::kShearMinYZ) + .value("kShearMaxYZ", MFnTransform::kShearMaxYZ) + + .value("kRotateMinX", MFnTransform::kRotateMinX) + .value("kRotateMaxX", MFnTransform::kRotateMaxX) + .value("kRotateMinY", MFnTransform::kRotateMinY) + .value("kRotateMaxY", MFnTransform::kRotateMaxY) + .value("kRotateMinZ", MFnTransform::kRotateMinZ) + .value("kRotateMaxZ", MFnTransform::kRotateMaxZ) + + .value("kTranslateMinX", MFnTransform::kTranslateMinX) + .value("kTranslateMaxX", MFnTransform::kTranslateMaxX) + .value("kTranslateMinY", MFnTransform::kTranslateMinY) + .value("kTranslateMaxY", MFnTransform::kTranslateMaxY) + .value("kTranslateMinZ", MFnTransform::kTranslateMinZ) + .value("kTranslateMaxZ", MFnTransform::kTranslateMaxZ) + .export_values(); + +FnTransform + .def(py::init<>()) + + .def(py::init([](MObject& object) { + MStatus status; + auto result = std::unique_ptr(new MFnTransform(object, &status)); + + if (!status) { + throw std::runtime_error( + "Invalid parameter passed for object - " + "not a Transform Node, " + "Node does not exist or " + "no valid pointer to Node" + ); + } + + return result; + }), py::arg("object")) + + .def(py::init([](MDagPath& dagPath) { + MStatus status; + auto result = std::unique_ptr(new MFnTransform(dagPath, &status)); + + if (!status) { + throw std::runtime_error( + "Invalid parameter passed for object - " + "not a Transform Node, " + "Node does not exist or " + "no valid pointer to Node" + ); + } + + return result; + }), py::arg("dagPath")) + + .def("clearRestPosition", [](MFnTransform & self) { + MStatus status = self.clearRestPosition(); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, _doc_FnTransform_clearRestPosition) + + .def("create", [](MFnTransform & self, MObject parent = MObject::kNullObj) -> MObject { + MStatus status; + MObject transform = self.create(parent, &status); + + if (!status) { + throw std::runtime_error(status.errorString().asChar()); + } + + return transform; + }, py::arg("parent") = MObject::kNullObj, _doc_FnTransform_create) + + .def("enableLimit", [](MFnTransform & self, MFnTransform::LimitType limitType, bool enable) { + MStatus status = self.enableLimit(limitType, enable); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("limitType"), py::arg("enable"), _doc_FnTransform_enableLimit) + + .def("isLimited", [](MFnTransform & self, MFnTransform::LimitType limitType) -> bool { + MStatus status; + bool result = self.isLimited(limitType, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return result; + }, py::arg("limitType"), _doc_FnTransform_isLimited) + + .def("limitValue", [](MFnTransform & self, MFnTransform::LimitType limitType) -> double { + MStatus status; + double limit = self.limitValue(limitType, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return limit; + }, py::arg("limitType"), _doc_FnTransform_limitValue) + + .def("resetFromRestPosition", [](MFnTransform & self) { + MStatus status = self.resetFromRestPosition(); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, _doc_FnTransform_resetFromRestPosition) + + .def("restPosition", [](MFnTransform & self) -> MTransformationMatrix { + MStatus status; + MTransformationMatrix matrix = self.restPosition(&status); + + if (! status) { + throw std::logic_error(status.errorString().asChar()); + } + + return matrix; + }, _doc_FnTransform_restPosition) + + .def("rotateBy", [](MFnTransform & self, MEulerRotation rotation, MSpace::Space space = MSpace::kTransform) { + MStatus status = self.rotateBy(rotation, space); + + if (! status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("rotation"), py::arg("space") = MSpace::kTransform, _doc_FnTransform_rotateBy) + + .def("rotateBy", [](MFnTransform & self, MQuaternion quaternion, MSpace::Space space = MSpace::kTransform) { + MStatus status = self.rotateBy(quaternion, space); + + if (! status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("quaternion"), py::arg("space") = MSpace::kTransform, _doc_FnTransform_rotateBy) + + .def("rotateBy", [](MFnTransform & self, const py::list & rotation, MTransformationMatrix::RotationOrder order, MSpace::Space space = MSpace::kTransform) { + if (rotation.size() != 3) + { + throw std::invalid_argument("You must provide a list of 3 floats for rotate."); + } + double tmp[3]; + std::transform(std::begin(rotation), std::end(rotation), std::begin(tmp), + [](pybind11::handle handle) -> double { return handle.cast(); }); + + MStatus status = self.rotateBy(tmp, order, space); + + if (! status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("rotation"), py::arg("order"), py::arg("space") = MSpace::kTransform, _doc_FnTransform_rotateBy) + + .def("rotateOrientation", [](MFnTransform & self, MSpace::Space space) -> MQuaternion { + MStatus status; + MQuaternion orient = self.rotateOrientation(space, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return orient; + }, py::arg("space"), _doc_FnTransform_rotateOrientation) + + .def("rotatePivot", [](MFnTransform & self, MSpace::Space space) -> MPoint { + MStatus status; + MPoint pivot = self.rotatePivot(space, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return pivot; + }, py::arg("space"), _doc_FnTransform_rotatePivot) + + .def("rotatePivotTranslation", [](MFnTransform & self, MSpace::Space space) -> MVector { + MStatus status; + MVector pivot = self.rotatePivotTranslation(space, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return pivot; + }, py::arg("space"), _doc_FnTransform_rotatePivotTranslation) + + .def_property_readonly("rotationOrder", [](MFnTransform & self) -> MTransformationMatrix::RotationOrder { + MStatus status; + auto result = self.rotationOrder(&status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return result; + }, _doc_FnTransform_rotationOrder) + + .def("scaleBy", [](MFnTransform & self, const py::list & scale) { + if (scale.size() != 3) + { + throw std::invalid_argument("You must provide a list of 3 floats for scale."); + } + double tmp[3]; + std::transform(std::begin(scale), std::end(scale), std::begin(tmp), + [](pybind11::handle handle) -> double { return handle.cast(); }); + + MStatus status = self.scaleBy(tmp); + + if (! status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("scale"), _doc_FnTransform_scaleBy) + + .def("scalePivot", [](MFnTransform & self, MSpace::Space space) -> MPoint { + MStatus status; + MPoint pivot = self.scalePivot(space, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return pivot; + }, py::arg("space"), _doc_FnTransform_scalePivot) + + .def("scalePivotTranslation", [](MFnTransform & self, MSpace::Space space) -> MVector { + MStatus status; + MVector pivot = self.scalePivotTranslation(space, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return pivot; + }, py::arg("space"), _doc_FnTransform_scalePivotTranslation) + + .def("setLimit", [](MFnTransform & self, MFnTransform::LimitType limitType, double value) { + MStatus status = self.setLimit(limitType, value); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("limitType"), py::arg("value"), _doc_FnTransform_setLimit) + + .def("setRestPosition", [](MFnTransform & self, const MTransformationMatrix & matrix) { + MStatus status = self.setRestPosition(matrix); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("matrix"), _doc_FnTransform_setRestPosition) + + .def("setRotateOrientation", [](MFnTransform & self, const MQuaternion& quat, MSpace::Space space, bool balance) { + MStatus status = self.setRotateOrientation(quat, space, balance); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("quat"), py::arg("space"), py::arg("balance"), _doc_FnTransform_setRotateOrientation) + + .def("setRotatePivot", [](MFnTransform & self, const MPoint& point, MSpace::Space space, bool balance) { + MStatus status = self.setRotatePivot(point, space, balance); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("point"), py::arg("space"), py::arg("balance"), _doc_FnTransform_setRotatePivot) + + .def("setRotatePivotTranslation", [](MFnTransform & self, const MVector & vec, MSpace::Space space) { + MStatus status = self.setRotatePivotTranslation(vec, space); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("vec"), py::arg("space"), _doc_FnTransform_setRotatePivotTranslation) + + .def("setRotation", [](MFnTransform & self, MEulerRotation rotation) { + MStatus status = self.setRotation(rotation); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("rotation"), _doc_FnTransform_setRotation) + + .def("setRotation", [](MFnTransform & self, MQuaternion quaternion, MSpace::Space space = MSpace::kTransform) { + MStatus status = self.setRotation(quaternion, space); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("quaternion"), py::arg("space") = MSpace::kTransform, _doc_FnTransform_setRotation) + + .def("setRotation", [](MFnTransform & self, const py::list & rotation, MTransformationMatrix::RotationOrder order) { + if (rotation.size() != 3) + { + throw std::invalid_argument("You must provide a list of 3 floats for rotation."); + } + double tmp[3]; + std::transform(std::begin(rotation), std::end(rotation), std::begin(tmp), + [](pybind11::handle handle) -> double { return handle.cast(); }); + + MStatus status = self.setRotation(tmp); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("rotation"), py::arg("order"), _doc_FnTransform_setRotation) + + .def("setRotationOrder", [](MFnTransform & self, MTransformationMatrix::RotationOrder order, bool reorder) { + MStatus status = self.setRotationOrder(order, reorder); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("order"), py::arg("reorder"), _doc_FnTransform_setRotationOrder) + + .def("setScale", [](MFnTransform & self, const py::list & scale) { + if (scale.size() != 3) + { + throw std::invalid_argument("You must provide a list of 3 floats for scale."); + } + double tmp[3]; + std::transform(std::begin(scale), std::end(scale), std::begin(tmp), + [](pybind11::handle handle) -> double { return handle.cast(); }); + + MStatus status = self.setScale(tmp); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("scale"), _doc_FnTransform_setScale) + + .def("setScalePivot", [](MFnTransform & self, const MPoint & point, MSpace::Space space, bool balance) { + MStatus status = self.setScalePivot(point, space, balance); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("point"), py::arg("space"), py::arg("balance"), _doc_FnTransform_setScalePivot) + + .def("setScalePivotTranslation", [](MFnTransform & self, const MVector & vec, MSpace::Space space) { + MStatus status = self.setScalePivotTranslation(vec, space); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("vec"), py::arg("space"), _doc_FnTransform_setScalePivotTranslation) + + .def("setShear", [](MFnTransform & self, const py::list & shear) { + if (shear.size() != 3) + { + throw std::invalid_argument("You must provide a list of 3 floats for shear."); + } + double tmp[3]; + std::transform(std::begin(shear), std::end(shear), std::begin(tmp), + [](pybind11::handle handle) -> double { return handle.cast(); }); + + MStatus status = self.setShear(tmp); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("shear"), _doc_FnTransform_setShear) + + .def("setTranslation", [](MFnTransform & self, const MVector & vec, MSpace::Space space) { + MStatus status = self.setTranslation(vec, space); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("vec"), py::arg("space"), _doc_FnTransform_setTranslation) + + .def("shearBy", [](MFnTransform & self, const py::list & shear) { + if (shear.size() != 3) + { + throw std::invalid_argument("You must provide a list of 3 floats for shear."); + } + double tmp[3]; + std::transform(std::begin(shear), std::end(shear), std::begin(tmp), + [](pybind11::handle handle) -> double { return handle.cast(); }); + + MStatus status = self.shearBy(tmp); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("shear"), _doc_FnTransform_shearBy) + + .def_property_readonly("transformation", [](MFnTransform & self) -> MTransformationMatrix { + MStatus status; + MTransformationMatrix matrix = self.transformation(&status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return matrix; + }, _doc_FnTransform_transformation) + + .def("translateBy", [](MFnTransform & self, const MVector & vec, MSpace::Space space) { + MStatus status = self.translateBy(vec, space); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("vec"), py::arg("space"), _doc_FnTransform_translateBy); \ No newline at end of file diff --git a/src/MTransformationMatrix.inl b/src/MTransformationMatrix.inl new file mode 100644 index 0000000..918232f --- /dev/null +++ b/src/MTransformationMatrix.inl @@ -0,0 +1,322 @@ +#define _doc_TransformationMatrix_asMatrix \ + "Interpolates between the identity transformation and that currently in the object, returning the result as an MMatrix." + +#define _doc_TransformationMatrix_asMatrixInverse \ + "Returns the inverse of the matrix representing the transformation." + +#define _doc_TransformationMatrix_asScaleMatrix \ + "Returns the matrix which takes points from object space to the space immediately following scale and shear transformations." + +#define _doc_TransformationMatrix_asRotateMatrix \ + "Returns the matrix which takes points from object space to the space immediately following the scale/shear/rotation transformations." + +#define _doc_TransformationMatrix_setScale \ + "Sets the transformation's scale components to the three floats in the provided sequence." + +#define _doc_TransformationMatrix_setRotation \ + "Sets the transformation's rotation component." + +#define _doc_TransformationMatrix_rotationOrder \ + "Returns the order of rotations when the transformation's rotate component is expressed as an euler rotation." + +#define _doc_TransformationMatrix_reorderRotation \ + "Reorders the transformation's rotate component to give the same overall rotation but using a new order or rotations." + +#define _doc_TransformationMatrix_setToRotationAxis \ + "Sets the transformation's rotate component to be a given axis vector and angle in radians." + +#define _doc_TransformationMatrix_rotationOrientation \ + "Returns a quaternion which orients the local rotation space." + +#define _doc_TransformationMatrix_setRotationOrientation \ + "Sets a quaternion which orients the local rotation space." + +#define _doc_TransformationMatrix_setTranslation \ + "Sets the transformation's translation component." + +#define _doc_TransformationMatrix_setShear \ + "Sets the transformation's shear component." + +#define _doc_TransformationMatrix_scalePivot \ + "Returns the transformation's scale pivot component." + +#define _doc_TransformationMatrix_setScalePivot \ + "Sets the transformation's scale pivot component." + +#define _doc_TransformationMatrix_scalePivotTranslation \ + "Returns the transformation's scale pivot translation component." + +#define _doc_TransformationMatrix_setScalePivotTranslation \ + "Sets the transformation's scale pivot translation component." + +#define _doc_TransformationMatrix_rotatePivot \ + "Returns the transformation's rotate pivot component." + +#define _doc_TransformationMatrix_setRotatePivot \ + "Sets the transformation's rotate pivot component." + +#define _doc_TransformationMatrix_rotatePivotTranslation \ + "Returns the transformation's rotate pivot translation component." + +#define _doc_TransformationMatrix_setRotatePivotTranslation \ + "Sets the transformation's rotate pivot translation component." + +#define _doc_TransformationMatrix_isEquivalent \ + "Returns true if this transformation's matrix is within tolerance of another's matrix." + +py::enum_(TransformationMatrix, "RotationOrder") + .value("kInvalid", MTransformationMatrix::RotationOrder::kInvalid) + .value("kXYZ", MTransformationMatrix::RotationOrder::kXYZ) + .value("kYZX", MTransformationMatrix::RotationOrder::kYZX) + .value("kZXY", MTransformationMatrix::RotationOrder::kZXY) + .value("kXZY", MTransformationMatrix::RotationOrder::kXZY) + .value("kYXZ", MTransformationMatrix::RotationOrder::kYXZ) + .value("kZYX", MTransformationMatrix::RotationOrder::kZYX) + .value("kLast", MTransformationMatrix::RotationOrder::kLast) + .export_values(); + +TransformationMatrix + .def(py::init<>()) + .def(py::init(), py::arg("src")) + .def(py::init(), py::arg("src")) + + .def("asMatrix", [](MTransformationMatrix & self, double interp = 1.0) -> MMatrix { + return self.asMatrix(interp); + }, _doc_TransformationMatrix_asMatrix) + + .def("asMatrixInverse", &MTransformationMatrix::asMatrixInverse, _doc_TransformationMatrix_asMatrixInverse) + + .def("asRotateMatrix", &MTransformationMatrix::asRotateMatrix, _doc_TransformationMatrix_asRotateMatrix) + + .def("asScaleMatrix", &MTransformationMatrix::asScaleMatrix, _doc_TransformationMatrix_asScaleMatrix) + + .def("isEquivalent", &MTransformationMatrix::isEquivalent, py::arg("other"), py::arg("tolerance") = MTransformationMatrix_kTol, _doc_TransformationMatrix_isEquivalent) + + .def("reorderRotation", [](MTransformationMatrix & self, MTransformationMatrix::RotationOrder order) { + MStatus status = self.reorderRotation(order); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("order"), _doc_TransformationMatrix_reorderRotation) + + .def("rotatePivot", [](MTransformationMatrix & self, MSpace::Space space) -> MPoint { + MStatus status; + MPoint result = self.rotatePivot(space, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return result; + }, py::arg("space"), _doc_TransformationMatrix_rotatePivot) + + .def("rotatePivotTranslation", [](MTransformationMatrix & self, MSpace::Space space) -> MVector { + MStatus status; + MVector result = self.rotatePivotTranslation(space, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return result; + }, py::arg("space"), _doc_TransformationMatrix_rotatePivotTranslation) + + .def_property_readonly("quaternionRotation", &MTransformationMatrix::rotation, + "Returns the rotation component of the transformation matrix as a quaternion.") + + .def_property_readonly("eulerRotation", &MTransformationMatrix::eulerRotation, + "Returns the rotation component of the transformation matrix as an euler rotation.") + + .def("setRotation", [](MTransformationMatrix & self, const MQuaternion& rotation) { + self.rotateTo(rotation); + }, py::arg("rotation"), "Set the rotation component of the transformation matrix using a quaternion.") + + .def("setRotation", [](MTransformationMatrix & self, const MEulerRotation& rotation) { + self.rotateTo(rotation); + }, py::arg("rotation"), "Set the rotation component of the transformation matrix using an euler rotation.") + + .def("rotateBy", [](MTransformationMatrix & self, const MQuaternion& rotate, MSpace::Space space) { + MStatus status; + self.rotateBy(rotate, space, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("rotate"), py::arg("space"), "Adds to the rotation component of the rotation matrix by rotating relative to the existing transformation using a quaternion.") + + .def("rotateBy", [](MTransformationMatrix & self, const MEulerRotation& rotate, MSpace::Space space) { + MStatus status; + self.rotateBy(rotate, space, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("rotate"), py::arg("space"), "Adds to the rotation component of the rotation matrix by rotating relative to the existing transformation using an euler rotation.") + + .def_property_readonly("rotationOrder", [](MTransformationMatrix & self) -> MTransformationMatrix::RotationOrder { + MStatus status; + auto result = self.rotationOrder(&status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return result; + }, _doc_TransformationMatrix_rotationOrder) + + .def("rotationOrientation", &MTransformationMatrix::rotationOrientation, _doc_TransformationMatrix_rotationOrientation) + + .def("setRotationOrientation", &MTransformationMatrix::setRotationOrientation, _doc_TransformationMatrix_setRotationOrientation) + + .def("scalePivot", [](MTransformationMatrix & self, MSpace::Space space) -> MPoint { + MStatus status; + MPoint result = self.scalePivot(space, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return result; + }, py::arg("space"), _doc_TransformationMatrix_scalePivot) + + .def("scalePivotTranslation", [](MTransformationMatrix & self, MSpace::Space space) -> MVector { + MStatus status; + MVector result = self.scalePivotTranslation(space, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + return result; + }, py::arg("space"), _doc_TransformationMatrix_scalePivotTranslation) + + .def("setRotatePivot", [](MTransformationMatrix & self, MPoint point, MSpace::Space space, bool balance) { + MStatus status = self.setRotatePivot(point, space, balance); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("point"), py::arg("space"), py::arg("balance"), _doc_TransformationMatrix_setRotatePivot) + + .def("setRotatePivotTranslation", [](MTransformationMatrix & self, MVector vector, MSpace::Space space) { + MStatus status = self.setRotatePivotTranslation(vector, space); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("vector"), py::arg("space"), _doc_TransformationMatrix_setRotatePivotTranslation) + + .def("scale", [](MTransformationMatrix & self, MSpace::Space space) -> py::list { + py::list result; + double scale[3]; + MStatus status = self.getScale(scale, space); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + + result.append(scale[0]); + result.append(scale[1]); + result.append(scale[2]); + + return result; + }, py::arg("space"), "Get the scale component of the transformation matrix and retun it as a list of 3 floats.") + + .def("setScale", [](MTransformationMatrix & self, const py::list & scale, MSpace::Space space) { + if (scale.size() != 3) + { + throw std::invalid_argument("You must provide a list of 3 floats for scale."); + } + double tmp[3]; + std::transform(std::begin(scale), std::end(scale), std::begin(tmp), + [](pybind11::handle handle) -> double { return handle.cast(); }); + + MStatus status = self.setScale(tmp, space); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("scale"), py::arg("space"), _doc_TransformationMatrix_setScale) + + .def("scaleBy", [](MTransformationMatrix & self, const py::list & scale, MSpace::Space space) { + if (scale.size() != 3) + { + throw std::invalid_argument("You must provide a list of 3 floats for scale."); + } + double tmp[3]; + std::transform(std::begin(scale), std::end(scale), std::begin(tmp), + [](pybind11::handle handle) -> double { return handle.cast(); }); + + MStatus status = self.addScale(tmp, space); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("scale"), py::arg("space"), "Multiplies the transformation's scale components by the three floats in the provided sequence.") + + .def("setScalePivot", [](MTransformationMatrix & self, MPoint point, MSpace::Space space, bool balance) { + MStatus status = self.setScalePivot(point, space, balance); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("point"), py::arg("space"), py::arg("balance"), _doc_TransformationMatrix_setScalePivot) + + .def("setScalePivotTranslation", [](MTransformationMatrix & self, MVector vector, MSpace::Space space) { + MStatus status = self.setScalePivotTranslation(vector, space); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("vector"), py::arg("space"), _doc_TransformationMatrix_setScalePivotTranslation) + + .def("setShear", [](MTransformationMatrix & self, const py::list & shear, MSpace::Space space) { + if (shear.size() != 3) + { + throw std::invalid_argument("You must provide a list of 3 floats for shear."); + } + double tmp[3]; + std::transform(std::begin(shear), std::end(shear), std::begin(tmp), + [](pybind11::handle handle) -> double { return handle.cast(); }); + + MStatus status = self.setShear(tmp, space); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("shear"), py::arg("space"), _doc_TransformationMatrix_setShear) + + .def("setToRotationAxis", [](MTransformationMatrix & self, MVector axis, double rotation) { + MStatus status = self.setToRotationAxis(axis, rotation); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("axis"), py::arg("rotation"), _doc_TransformationMatrix_setToRotationAxis) + + .def("translation", [](MTransformationMatrix & self, MSpace::Space space) -> MVector { + MStatus status; + MVector translation = self.getTranslation(space, &status); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + return translation; + }, py::arg("space"), "Returns the transformation's translation component as a vector.") + + .def("setTranslation", [](MTransformationMatrix & self, MVector vector, MSpace::Space space) { + MStatus status = self.setTranslation(vector, space); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("vector"), py::arg("space"), _doc_TransformationMatrix_setTranslation) + + .def("translateBy", [](MTransformationMatrix & self, MVector vector, MSpace::Space space) { + MStatus status = self.addTranslation(vector, space); + + if (!status) { + throw std::logic_error(status.errorString().asChar()); + } + }, py::arg("vector"), py::arg("space"), "Adds a vector to the transformation's translate component."); \ No newline at end of file diff --git a/src/Math.inl b/src/Math.inl index 1e165f0..6d0ff59 100644 --- a/src/Math.inl +++ b/src/Math.inl @@ -19,8 +19,9 @@ Vector .def(py::self /= double(), py::arg("other")) .def(py::self * double(), py::arg("other")) .def(py::self / double(), py::arg("other")) - - .def("__neg__", [](MVector v) { return -v; }, py::is_operator()) + .def(py::self == py::self, py::arg("other")) + .def(py::self != py::self, py::arg("other")) + .def(-py::self) // Support print() .def("__repr__", [](const MVector &a) { @@ -99,6 +100,7 @@ Matrix ); Quaternion + .def(py::init<>()) .def(py::init(), py::arg("angle"), py::arg("axis")) + + .def(py::self + MQuaternion(), py::arg("other")) + .def(py::self - MQuaternion(), py::arg("other")) + .def(py::self * MQuaternion(), py::arg("other")) + .def(py::self *= MQuaternion(), py::arg("other")) + .def(py::self == MQuaternion(), py::arg("other")) + .def(py::self != MQuaternion(), py::arg("other")) + .def(-py::self) + .def_readwrite("x", &MQuaternion::x) .def_readwrite("y", &MQuaternion::y) .def_readwrite("z", &MQuaternion::z) diff --git a/src/Types.inl b/src/Types.inl index 1f7ec0d..45a1a8c 100644 --- a/src/Types.inl +++ b/src/Types.inl @@ -46,3 +46,14 @@ String return ""; } ); + + +py::enum_(Space, "Space") + .value("kInvalid", MSpace::Space::kInvalid) + .value("kTransform", MSpace::Space::kTransform) + .value("kPreTransform", MSpace::Space::kPreTransform) + .value("kPostTransform", MSpace::Space::kPostTransform) + .value("kWorld", MSpace::Space::kWorld) + .value("kObject", MSpace::Space::kObject) + .value("kLast", MSpace::Space::kLast) + .export_values(); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 89c967b..740cfa6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include // Types #include @@ -43,6 +45,7 @@ // Function sets #include #include +#include #include "util/atov.hpp" #include "util/plug.hpp" @@ -69,6 +72,7 @@ PYBIND11_MODULE(cmdc, m) { #include "ForwardDeclarations.inl" #include "Math.inl" + #include "MEulerRotation.inl" #include "MDagModifier.inl" #include "MDagPath.inl" #include "MDGModifier.inl" @@ -80,6 +84,8 @@ PYBIND11_MODULE(cmdc, m) { #include "MBoundingBox.inl" #include "MPlug.inl" #include "MSelectionList.inl" + #include "MTransformationMatrix.inl" + #include "MFnTransform.inl" #ifdef VERSION_INFO m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); diff --git a/tests/test_MFnTransform.py b/tests/test_MFnTransform.py new file mode 100644 index 0000000..e364adc --- /dev/null +++ b/tests/test_MFnTransform.py @@ -0,0 +1,24 @@ +import nose.tools + +import cmdc + + +def test_init_transform(): + transform = cmdc.FnTransform() + + +def test_create_transform(): + transform = cmdc.FnTransform() + + transformObj = transform.create() + assert not transformObj.isNull() + + +def test_limits(): + transform = cmdc.FnTransform() + transform.create() + + assert not transform.isLimited(cmdc.FnTransform.kScaleMinX) + + transform.enableLimit(cmdc.FnTransform.kScaleMinX, True) + assert transform.isLimited(cmdc.FnTransform.kScaleMinX) \ No newline at end of file diff --git a/tests/test_MTransformationMatrix.py b/tests/test_MTransformationMatrix.py new file mode 100644 index 0000000..e0e5713 --- /dev/null +++ b/tests/test_MTransformationMatrix.py @@ -0,0 +1,54 @@ +import nose.tools + +import cmdc + +def test_new_transformation_matrix(): + origMatrix = cmdc.TransformationMatrix() + copyMatrix = cmdc.TransformationMatrix(origMatrix) + assert copyMatrix is not origMatrix + + otherCopy = cmdc.TransformationMatrix(cmdc.Matrix()) + +def test_as_matrix(): + mat = cmdc.TransformationMatrix() + + invMatrix = mat.asMatrixInverse() + rotMatrix = mat.asRotateMatrix() + scaleMatrix = mat.asScaleMatrix() + +def test_translation(): + mat = cmdc.TransformationMatrix() + + translation = cmdc.Vector(1.0, 2.0, 3.0) + mat.setTranslation(translation, cmdc.Space.kTransform) + assert mat.translation(cmdc.Space.kTransform) == translation + + mat.translateBy(translation, cmdc.Space.kTransform) + assert mat.translation(cmdc.Space.kTransform) == cmdc.Vector(2.0, 4.0, 6.0) + +def test_rotation(): + mat = cmdc.TransformationMatrix() + + mat.reorderRotation(cmdc.TransformationMatrix.kZYX) + assert mat.rotationOrder == cmdc.TransformationMatrix.kZYX + + mat.rotateBy(cmdc.Quaternion(), cmdc.Space.kTransform) + mat.rotateBy(cmdc.EulerRotation(), cmdc.Space.kTransform) + + rotation = cmdc.Quaternion() + mat.setRotation(rotation) + assert rotation == mat.quaternionRotation + + rotation = cmdc.EulerRotation() + mat.setRotation(rotation) + assert rotation == mat.eulerRotation + +def test_scale(): + mat = cmdc.TransformationMatrix() + + scale = [2.0, 3.0, 4.0] + mat.setScale(scale, cmdc.Space.kTransform) + assert mat.scale(cmdc.Space.kTransform) == scale + + mat.scaleBy(scale, cmdc.Space.kTransform) + assert mat.scale(cmdc.Space.kTransform) == [4.0, 9.0, 16.0] From ce722b809812b050ad705b667d4eb4e92442b538 Mon Sep 17 00:00:00 2001 From: Scott Englert Date: Tue, 17 May 2022 09:02:21 -0700 Subject: [PATCH 02/11] adding MVector support and tests --- src/Math.inl | 73 ++++++++++++++++++++++++ tests/test_MVector.py | 130 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 tests/test_MVector.py diff --git a/src/Math.inl b/src/Math.inl index 6d0ff59..6ef2af4 100644 --- a/src/Math.inl +++ b/src/Math.inl @@ -7,6 +7,18 @@ Vector py::arg("y"), py::arg("z")) .def(py::init(), 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(); }); + + return std::unique_ptr(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) @@ -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 " Date: Thu, 19 May 2022 17:59:22 -0700 Subject: [PATCH 03/11] Update src/MTransformationMatrix.inl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Pinsard --- src/MTransformationMatrix.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MTransformationMatrix.inl b/src/MTransformationMatrix.inl index 918232f..6feff96 100644 --- a/src/MTransformationMatrix.inl +++ b/src/MTransformationMatrix.inl @@ -82,7 +82,7 @@ TransformationMatrix .def("asMatrix", [](MTransformationMatrix & self, double interp = 1.0) -> MMatrix { return self.asMatrix(interp); - }, _doc_TransformationMatrix_asMatrix) + }, py::arg("interp") = 1.0, _doc_TransformationMatrix_asMatrix) .def("asMatrixInverse", &MTransformationMatrix::asMatrixInverse, _doc_TransformationMatrix_asMatrixInverse) From 1e7e93478f962ff62c20e5107290662a67b41f3b Mon Sep 17 00:00:00 2001 From: Scott Englert Date: Thu, 19 May 2022 17:59:37 -0700 Subject: [PATCH 04/11] Update src/MTransformationMatrix.inl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Pinsard --- src/MTransformationMatrix.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MTransformationMatrix.inl b/src/MTransformationMatrix.inl index 6feff96..45b265c 100644 --- a/src/MTransformationMatrix.inl +++ b/src/MTransformationMatrix.inl @@ -167,7 +167,7 @@ TransformationMatrix .def("rotationOrientation", &MTransformationMatrix::rotationOrientation, _doc_TransformationMatrix_rotationOrientation) - .def("setRotationOrientation", &MTransformationMatrix::setRotationOrientation, _doc_TransformationMatrix_setRotationOrientation) + .def("setRotationOrientation", &MTransformationMatrix::setRotationOrientation, py::arg("q"), _doc_TransformationMatrix_setRotationOrientation) .def("scalePivot", [](MTransformationMatrix & self, MSpace::Space space) -> MPoint { MStatus status; From 33101d3a58c4f409d3c77832f90147bb101c62ed Mon Sep 17 00:00:00 2001 From: Scott Englert Date: Thu, 19 May 2022 17:59:49 -0700 Subject: [PATCH 05/11] Update src/Math.inl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Pinsard --- src/Math.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Math.inl b/src/Math.inl index 6ef2af4..6e0dc0d 100644 --- a/src/Math.inl +++ b/src/Math.inl @@ -17,7 +17,7 @@ Vector [](pybind11::handle handle) -> double { return handle.cast(); }); return std::unique_ptr(new MVector(tmp[0], tmp[1], tmp[2])); - }), "Create a new vector from a seqence or 2 or 3 floats") + }), py::arg("d"), "Create a new vector from a seqence or 2 or 3 floats") .def_readwrite("x", &MVector::x) .def_readwrite("y", &MVector::y) From 5bbfe0076dd1ba59c59b23b2c9c2ac5cca075539 Mon Sep 17 00:00:00 2001 From: Scott Englert Date: Thu, 19 May 2022 18:00:10 -0700 Subject: [PATCH 06/11] Update src/MFnTransform.inl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Pinsard --- src/MFnTransform.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MFnTransform.inl b/src/MFnTransform.inl index 21d46ae..a455cd3 100644 --- a/src/MFnTransform.inl +++ b/src/MFnTransform.inl @@ -170,7 +170,7 @@ FnTransform } return transform; - }, py::arg("parent") = MObject::kNullObj, _doc_FnTransform_create) + }, py::arg_v("parent", MObject::kNullObj, "Object.kNullObj"), _doc_FnTransform_create) .def("enableLimit", [](MFnTransform & self, MFnTransform::LimitType limitType, bool enable) { MStatus status = self.enableLimit(limitType, enable); From 35a76852baabb96fc42057ce2d871266561b1370 Mon Sep 17 00:00:00 2001 From: Scott Englert Date: Wed, 1 Jun 2022 14:11:36 -0700 Subject: [PATCH 07/11] Adding more functionality to MVector, MMatrix, MPoint, and MQuaternion --- src/MFn.Types.inl | 2 +- src/Math.inl | 158 ++++++++++++++--- tests/test_MMatrix.py | 38 ++++ tests/test_MPoint.py | 92 ++++++++++ tests/test_MQuaternion.py | 56 ++++++ tests/test_MTransformationMatrix.py | 108 +++++------ tests/test_MVector.py | 266 ++++++++++++++-------------- 7 files changed, 511 insertions(+), 209 deletions(-) create mode 100644 tests/test_MMatrix.py create mode 100644 tests/test_MPoint.py create mode 100644 tests/test_MQuaternion.py diff --git a/src/MFn.Types.inl b/src/MFn.Types.inl index bd29607..9d88683 100644 --- a/src/MFn.Types.inl +++ b/src/MFn.Types.inl @@ -1 +1 @@ -// Auto-generated by /Scripts/mfn.py at build time +// Auto-generated by /Scripts/mfn.py at build time diff --git a/src/Math.inl b/src/Math.inl index 6e0dc0d..8158a15 100644 --- a/src/Math.inl +++ b/src/Math.inl @@ -5,19 +5,15 @@ Vector const double>(), py::arg("x"), py::arg("y"), - py::arg("z")) + py::arg("z") = 0.0) .def(py::init(), 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(); }); - - return std::unique_ptr(new MVector(tmp[0], tmp[1], tmp[2])); - }), py::arg("d"), "Create a new vector from a seqence or 2 or 3 floats") + .def(py::init(), py::arg("src")) + .def(py::init([](std::array seq) { + return std::unique_ptr(new MVector(seq[0], seq[1], seq[2])); + }), py::arg("seq"), "Create a new vector from a seqence of 3 floats") + .def(py::init([](std::array seq) { + return std::unique_ptr(new MVector(seq[0], seq[1])); + }), py::arg("seq"), "Create a new vector from a seqence of 2 floats") .def_readwrite("x", &MVector::x) .def_readwrite("y", &MVector::y) @@ -95,6 +91,10 @@ Vector .def_property_readonly_static("zNegAxisVector", [](py::object) { return MVector::zNegAxis; }, "The vector <0,0,-1>") + + .def("__len__", [](const MVector &self) -> int { + return 3; + }, "Returns length of the vector, which is always 3.") // Support print() .def("__repr__", [](const MVector &a) { @@ -107,27 +107,50 @@ Vector Point .def(py::init<>()) - .def(py::init(), - py::arg("x"), - py::arg("y"), - py::arg("z"), - py::arg("w")) + .def(py::init(), + py::arg("x"), py::arg("y"), py::arg("z") = 0.0, py::arg("w") = 1.0) .def(py::init(), py::arg("src")) + .def(py::init(), py::arg("src")) + .def(py::init([](std::array seq) { + return std::unique_ptr(new MPoint(seq[0], seq[1], seq[2], seq[3])); + }), py::arg("seq"), "Create a new point from a seqence of 4 floats.") + .def(py::init([](std::array seq) { + return std::unique_ptr(new MPoint(seq[0], seq[1], seq[2])); + }), py::arg("seq"), "Create a new point from a seqence of 3 floats.`w` will be set to 1.0") + .def(py::init([](std::array seq) { + return std::unique_ptr(new MPoint(seq[0], seq[1])); + }), py::arg("seq"), "Create a new point from a seqence of 2 floats. `z` will be set to 0.0 and `w` will be set to 1.0") + .def_readwrite("x", &MPoint::x) .def_readwrite("y", &MPoint::y) .def_readwrite("z", &MPoint::z) .def_readwrite("w", &MPoint::w) - .def(py::self + py::self, py::arg("other")) + .def(py::self + MVector(), py::arg("other")) + .def(py::self - MVector(), 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 += MVector(), py::arg("other")) + .def(py::self -= MVector(), 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 *= MMatrix(), 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("cartesianize", &MPoint::cartesianize) + .def("rationalize", &MPoint::rationalize) + .def("homogenize", &MPoint::homogenize) + .def("distanceTo", &MPoint::distanceTo, py::arg("other")) + .def("isEquivalent", &MPoint::isEquivalent, py::arg("other"), py::arg("tol") = MPoint_kTol) + + .def_property_readonly_static("origin", [](py::object) { return MPoint::origin; }, + "The point <0,0,0,1>") + + .def("__len__", [](const MPoint &self) -> int { + return 4; + }, "Returns length of the point, which is always 4.") // Support print() .def("__repr__", [](const MPoint &a) { @@ -142,6 +165,24 @@ Point Matrix .def(py::init<>()) .def(py::init(), py::arg("src")) + .def(py::init([](std::array seq) { + double tmp[4][4] = {{seq[0], seq[1], seq[2], seq[3]}, + {seq[4], seq[5], seq[6], seq[7]}, + {seq[8], seq[9], seq[10], seq[11]}, + {seq[12], seq[13], seq[14], seq[15]} + }; + + return std::unique_ptr(new MMatrix(tmp)); + }), py::arg("seq"), "Create a new matrix from a sequence of 16 float values.") + .def(py::init([](std::array, 4> seq) { + double tmp[4][4] = {{seq[0][0], seq[0][1], seq[0][2], seq[0][3]}, + {seq[1][0], seq[1][1], seq[1][2], seq[1][3]}, + {seq[2][0], seq[2][1], seq[2][2], seq[2][3]}, + {seq[3][0], seq[3][1], seq[3][2], seq[3][3]}, + }; + + return std::unique_ptr(new MMatrix(tmp)); + }), py::arg("seq"), "Create a new matrix from a sequence of 4 tuples of 4 float values each.") .def(py::self += MMatrix(), py::arg("other")) .def(py::self + MMatrix(), py::arg("other")) @@ -160,6 +201,16 @@ Matrix .def("inverse", &MMatrix::inverse) .def("adjoint", &MMatrix::adjoint) .def("homogenize", &MMatrix::homogenize) + .def("transpose", &MMatrix::transpose) + .def("det3x3", &MMatrix::det3x3) + .def("det4x4", &MMatrix::det4x4) + .def("setToIdentity", &MMatrix::setToIdentity) + + .def_property_readonly("isSingular", &MMatrix::isSingular) + + .def("__len__", [](const MMatrix &self) -> int { + return 16; + }, "Returns length of the matrix, which is always 16.") // Support print() .def("__repr__", [](const MMatrix &a) { @@ -183,16 +234,21 @@ Quaternion py::arg("z"), py::arg("w")) .def(py::init(), py::arg("src")) - .def(py::init(), + .def(py::init(), py::arg("a"), - py::arg("b")) + py::arg("b"), + py::arg("factor") = 1.0) .def(py::init(), py::arg("angle"), py::arg("axis")) + .def(py::init([](std::array seq) { + return std::unique_ptr(new MQuaternion(seq[0], seq[1], seq[2], seq[3])); + }), py::arg("seq"), "Create a new quaternion from a seqence of 4 floats") .def(py::self + MQuaternion(), py::arg("other")) .def(py::self - MQuaternion(), py::arg("other")) .def(py::self * MQuaternion(), py::arg("other")) + .def(double() * py::self, py::arg("scale")) .def(py::self *= MQuaternion(), py::arg("other")) .def(py::self == MQuaternion(), py::arg("other")) .def(py::self != MQuaternion(), py::arg("other")) @@ -203,6 +259,58 @@ Quaternion .def_readwrite("z", &MQuaternion::z) .def_readwrite("w", &MQuaternion::w) + .def("asAxisAngle", [](const MQuaternion& self) -> std::pair { + MVector axis; + double theta; + self.getAxisAngle(axis, theta); + + return std::make_pair(axis, theta); + }, "Returns the rotation as a tuple containing an axis vector and an angle in radians about that axis.") + + .def("asEulerRotation", &MQuaternion::asEulerRotation) + .def("asMatrix", &MQuaternion::asMatrix) + .def("conjugate", &MQuaternion::conjugate) + .def("conjugateIt", &MQuaternion::conjugateIt) + .def("exp", &MQuaternion::exp) + .def("inverse", &MQuaternion::inverse) + .def("invertIt", &MQuaternion::invertIt) + .def("log", &MQuaternion::log) + .def("isEquivalent", &MQuaternion::isEquivalent, py::arg("other"), py::arg("tol") = kQuaternionEpsilon) + .def("negateIt", &MQuaternion::negateIt) + .def("normal", &MQuaternion::normal) + .def("normalizeIt", &MQuaternion::normalizeIt) + .def("setToXAxis", &MQuaternion::setToXAxis) + .def("setToYAxis", &MQuaternion::setToYAxis) + .def("setToZAxis", &MQuaternion::setToZAxis) + .def("setValue", [](MQuaternion& self, const MQuaternion& quat) { + self = quat; + }) + .def("setValue", [](MQuaternion& self, const MEulerRotation& rot) { + self = rot; + }) + .def("setValue", [](MQuaternion& self, const MMatrix& matrix) { + self = matrix; + }) + .def("setValue", [](MQuaternion& self, const MVector& axis, double angle) { + self.setAxisAngle(axis, angle); + }) + + .def_static("slerp", [](const MQuaternion& p, const MQuaternion& q, double t, short spin) { + return slerp(p, q, t, spin); + }) + .def_static("squad", [](const MQuaternion& p, const MQuaternion& a, const MQuaternion& b, + const MQuaternion& q, double t, short spin) { + return squad(p, a, b, q, t, spin); + + }) + .def_static("squadPt", [](const MQuaternion& q0, const MQuaternion& q1, const MQuaternion& q2) { + return squadPt(q0, q1, q2); + }) + + .def("__len__", [](const MQuaternion &self) -> int { + return 4; + }, "Returns length of the quaternion, which is always 4.") + .def("__repr__", [](const MQuaternion& q) { return " Date: Thu, 9 Jun 2022 22:51:13 -0700 Subject: [PATCH 08/11] Completed MEulerRotation binding --- src/MEulerRotation.inl | 130 ++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 61 deletions(-) diff --git a/src/MEulerRotation.inl b/src/MEulerRotation.inl index 8e74a79..9a02e5e 100644 --- a/src/MEulerRotation.inl +++ b/src/MEulerRotation.inl @@ -70,7 +70,15 @@ py::enum_(EulerRotation, "RotationOrder") EulerRotation .def(py::init<>()) .def(py::init(), py::arg("src")) - .def(py::init(), py::arg("vec"), py::arg("order") = MEulerRotation::kXYZ) + .def(py::init(), + py::arg("vec"), py::arg("order") = MEulerRotation::kXYZ) + .def(py::init(), + py::arg("x"), py::arg("y"), py::arg("x"), py::arg("order") = MEulerRotation::kXYZ) + .def(py::init([](std::array seq, MEulerRotation::RotationOrder order) + { + return std::unique_ptr(new MEulerRotation(seq[0], seq[1], seq[2], order)); + } + ), py::arg("seq"), py::arg("order") = MEulerRotation::kXYZ) .def_readwrite("x", &MEulerRotation::x) .def_readwrite("y", &MEulerRotation::y) @@ -112,83 +120,83 @@ EulerRotation return self.bound(); }, _doc_EulerRotation_bound) - // .def("boundIt", [](MEulerRotation & self) -> & { - // throw std::logic_error{"Function not yet implemented."}; - // }, _doc_EulerRotation_boundIt) + .def("boundIt", [](MEulerRotation & self) -> MEulerRotation& { + return self.boundIt(); + }, _doc_EulerRotation_boundIt) - // .def("boundIt", [](MEulerRotation & self, MEulerRotation src) -> & { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("src"), _doc_EulerRotation_boundIt) + .def("boundIt", [](MEulerRotation & self, MEulerRotation src) -> MEulerRotation& { + return self.boundIt(src); + }, py::arg("src"), _doc_EulerRotation_boundIt) - // .def("closestCut", [](MEulerRotation & self, MEulerRotation dst) -> MEulerRotation { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("dst"), _doc_EulerRotation_closestCut) + .def("closestCut", [](MEulerRotation & self, MEulerRotation target) -> MEulerRotation { + return self.closestCut(target); + }, py::arg("target"), _doc_EulerRotation_closestCut) - // .def("closestSolution", [](MEulerRotation & self, MEulerRotation dst) -> MEulerRotation { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("dst"), _doc_EulerRotation_closestSolution) + .def("closestSolution", [](MEulerRotation & self, MEulerRotation target) -> MEulerRotation { + return self.closestSolution(target); + }, py::arg("dst"), _doc_EulerRotation_closestSolution) - // .def_static("decompose", [](MMatrix matrix, MEulerRotation::RotationOrder ord) -> MEulerRotation { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("matrix"), py::arg("ord"), _doc_EulerRotation_decompose) + .def_static("decompose", &MEulerRotation::decompose, + py::arg("matrix"), py::arg("ord"), _doc_EulerRotation_decompose) - // .def("incrementalRotateBy", [](MEulerRotation & self, MVector axis, double angle) -> & { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("axis"), py::arg("angle"), _doc_EulerRotation_incrementalRotateBy) + .def("incrementalRotateBy", &MEulerRotation::incrementalRotateBy, + py::arg("axis"), py::arg("angle"), _doc_EulerRotation_incrementalRotateBy) - // .def("inverse", [](MEulerRotation & self) -> MEulerRotation { - // throw std::logic_error{"Function not yet implemented."}; - // }, _doc_EulerRotation_inverse) + .def("inverse", [](MEulerRotation & self) -> MEulerRotation { + return self.inverse(); + }, _doc_EulerRotation_inverse) - // .def("invertIt", [](MEulerRotation & self) -> & { - // throw std::logic_error{"Function not yet implemented."}; - // }, _doc_EulerRotation_invertIt) + .def("invertIt", &MEulerRotation::invertIt, _doc_EulerRotation_invertIt) - // .def("isEquivalent", [](MEulerRotation & self, MEulerRotation other, double tolerance = kEulerRotationEpsilon) -> bool { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("other"), py::arg("tolerance") = kEulerRotationEpsilon, _doc_EulerRotation_isEquivalent) + .def("isEquivalent", [](MEulerRotation & self, MEulerRotation other, double tolerance = kEulerRotationEpsilon) -> bool { + return self.isEquivalent(other, tolerance); + }, py::arg("other"), py::arg("tolerance") = kEulerRotationEpsilon, _doc_EulerRotation_isEquivalent) - // .def("isZero", [](MEulerRotation & self, double tolerance = kEulerRotationEpsilon) -> bool { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("tolerance") = kEulerRotationEpsilon, _doc_EulerRotation_isZero) + .def("isZero", [](MEulerRotation & self, double tolerance = kEulerRotationEpsilon) -> bool { + return self.isZero(tolerance); + }, py::arg("tolerance") = kEulerRotationEpsilon, _doc_EulerRotation_isZero) - // .def("reorder", [](MEulerRotation & self, MEulerRotation::RotationOrder ord) -> MEulerRotation { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("ord"), _doc_EulerRotation_reorder) + .def("reorder", [](MEulerRotation & self, MEulerRotation::RotationOrder order) -> MEulerRotation { + return self.reorder(order); + }, py::arg("order"), _doc_EulerRotation_reorder) - // .def("reorderIt", [](MEulerRotation & self, MEulerRotation::RotationOrder ord) -> & { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("ord"), _doc_EulerRotation_reorderIt) + .def("reorderIt", [](MEulerRotation & self, MEulerRotation::RotationOrder order) -> MEulerRotation& { + return self.reorderIt(order); + }, py::arg("order"), _doc_EulerRotation_reorderIt) - // .def("setToAlternateSolution", [](MEulerRotation & self) -> & { - // throw std::logic_error{"Function not yet implemented."}; - // }, _doc_EulerRotation_setToAlternateSolution) + .def("setToAlternateSolution", [](MEulerRotation & self) -> MEulerRotation& { + return self.setToAlternateSolution(); + }, _doc_EulerRotation_setToAlternateSolution) - // .def("setToAlternateSolution", [](MEulerRotation & self, MEulerRotation src) -> & { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("src"), _doc_EulerRotation_setToAlternateSolution) + .def("setToAlternateSolution", [](MEulerRotation & self, MEulerRotation rot) -> MEulerRotation& { + return self.setToAlternateSolution(rot); + }, py::arg("rot"), _doc_EulerRotation_setToAlternateSolution) - // .def("setToClosestCut", [](MEulerRotation & self, MEulerRotation dst) -> & { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("dst"), _doc_EulerRotation_setToClosestCut) + .def("setToClosestCut", [](MEulerRotation & self, MEulerRotation target) -> MEulerRotation& { + return self.setToClosestCut(target); + }, py::arg("target"), _doc_EulerRotation_setToClosestCut) - // .def("setToClosestCut", [](MEulerRotation & self, MEulerRotation src, MEulerRotation dst) -> & { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("src"), py::arg("dst"), _doc_EulerRotation_setToClosestCut) + .def("setToClosestCut", [](MEulerRotation & self, MEulerRotation src, MEulerRotation target) -> MEulerRotation& { + return self.setToClosestCut(src, target); + }, py::arg("src"), py::arg("target"), _doc_EulerRotation_setToClosestCut) - // .def("setToClosestSolution", [](MEulerRotation & self, MEulerRotation dst) -> & { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("dst"), _doc_EulerRotation_setToClosestSolution) + .def("setToClosestSolution", [](MEulerRotation & self, MEulerRotation target) -> MEulerRotation& { + return self.setToClosestSolution(target); + }, py::arg("target"), _doc_EulerRotation_setToClosestSolution) - // .def("setToClosestSolution", [](MEulerRotation & self, MEulerRotation src, MEulerRotation dst) -> & { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("src"), py::arg("dst"), _doc_EulerRotation_setToClosestSolution) + .def("setToClosestSolution", [](MEulerRotation & self, MEulerRotation src, MEulerRotation target) -> MEulerRotation& { + return self.setToClosestSolution(src, target); + }, py::arg("src"), py::arg("target"), _doc_EulerRotation_setToClosestSolution) - // .def("setValue", [](MEulerRotation & self, MVector v, MEulerRotation::RotationOrder ord = MEulerRotation::kXYZ) -> & { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("v"), py::arg("ord") = MEulerRotation::kXYZ, _doc_EulerRotation_setValue) + .def("setValue", [](MEulerRotation & self, const MEulerRotation & rot) -> MEulerRotation& { + return self.setValue(rot.x, rot.y, rot.z, rot.order); + }) - // .def("setValue", [](MEulerRotation & self, double xx, double yy, double zz, MEulerRotation::RotationOrder ord = MEulerRotation::kXYZ) -> & { - // throw std::logic_error{"Function not yet implemented."}; - // }, py::arg("xx"), py::arg("yy"), py::arg("zz"), py::arg("ord") = MEulerRotation::kXYZ, _doc_EulerRotation_setValue); + .def("setValue", [](MEulerRotation & self, MVector vec, MEulerRotation::RotationOrder order = MEulerRotation::kXYZ) -> MEulerRotation& { + return self.setValue(vec, order); + }, py::arg("v"), py::arg("order") = MEulerRotation::kXYZ, _doc_EulerRotation_setValue) + + .def("setValue", [](MEulerRotation & self, double xx, double yy, double zz, MEulerRotation::RotationOrder order = MEulerRotation::kXYZ) -> MEulerRotation& { + return self.setValue(xx, yy, zz, order); + }, py::arg("xx"), py::arg("yy"), py::arg("zz"), py::arg("order") = MEulerRotation::kXYZ, _doc_EulerRotation_setValue); ; \ No newline at end of file From 48344b8fbe08c3eb791d29d9aefc208128f05b1b Mon Sep 17 00:00:00 2001 From: Scott Englert Date: Sat, 11 Jun 2022 13:25:33 -0700 Subject: [PATCH 09/11] Adding missing signature for stubs --- src/MEulerRotation.inl | 2 +- src/Math.inl | 25 +++++++++++++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/MEulerRotation.inl b/src/MEulerRotation.inl index 9a02e5e..6605273 100644 --- a/src/MEulerRotation.inl +++ b/src/MEulerRotation.inl @@ -190,7 +190,7 @@ EulerRotation .def("setValue", [](MEulerRotation & self, const MEulerRotation & rot) -> MEulerRotation& { return self.setValue(rot.x, rot.y, rot.z, rot.order); - }) + }, py::arg("rot"), _doc_EulerRotation_setValue) .def("setValue", [](MEulerRotation & self, MVector vec, MEulerRotation::RotationOrder order = MEulerRotation::kXYZ) -> MEulerRotation& { return self.setValue(vec, order); diff --git a/src/Math.inl b/src/Math.inl index 8158a15..38e52e0 100644 --- a/src/Math.inl +++ b/src/Math.inl @@ -279,33 +279,38 @@ Quaternion .def("negateIt", &MQuaternion::negateIt) .def("normal", &MQuaternion::normal) .def("normalizeIt", &MQuaternion::normalizeIt) - .def("setToXAxis", &MQuaternion::setToXAxis) - .def("setToYAxis", &MQuaternion::setToYAxis) - .def("setToZAxis", &MQuaternion::setToZAxis) + .def("setToXAxis", &MQuaternion::setToXAxis, py::arg("angle")) + .def("setToYAxis", &MQuaternion::setToYAxis, py::arg("angle")) + .def("setToZAxis", &MQuaternion::setToZAxis, , py::arg("angle")) .def("setValue", [](MQuaternion& self, const MQuaternion& quat) { self = quat; - }) + }, py::arg("quat")) + .def("setValue", [](MQuaternion& self, const MEulerRotation& rot) { self = rot; - }) + }, py::arg("rot")) + .def("setValue", [](MQuaternion& self, const MMatrix& matrix) { self = matrix; - }) + }, py::arg("matrix")) + .def("setValue", [](MQuaternion& self, const MVector& axis, double angle) { self.setAxisAngle(axis, angle); - }) + }, py::arg("axis"), py::arg("angle")) .def_static("slerp", [](const MQuaternion& p, const MQuaternion& q, double t, short spin) { return slerp(p, q, t, spin); - }) + }py::arg("p"), py::arg("q"), py::arg("t"), py::arg("spin")) + .def_static("squad", [](const MQuaternion& p, const MQuaternion& a, const MQuaternion& b, const MQuaternion& q, double t, short spin) { return squad(p, a, b, q, t, spin); - }) + }py::arg("p"), py::arg("a"), py::arg("b"), py::arg("q"), py::arg("t"), py::arg("spin")) + .def_static("squadPt", [](const MQuaternion& q0, const MQuaternion& q1, const MQuaternion& q2) { return squadPt(q0, q1, q2); - }) + }, py::arg("q0"), py::arg("q1"), py::arg("q2")) .def("__len__", [](const MQuaternion &self) -> int { return 4; From 6312a9696be63dd214cb892c08f1690593769bca Mon Sep 17 00:00:00 2001 From: Scott Englert Date: Sat, 11 Jun 2022 13:26:44 -0700 Subject: [PATCH 10/11] fixed extra comma in math --- src/Math.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Math.inl b/src/Math.inl index 38e52e0..943925a 100644 --- a/src/Math.inl +++ b/src/Math.inl @@ -281,7 +281,7 @@ Quaternion .def("normalizeIt", &MQuaternion::normalizeIt) .def("setToXAxis", &MQuaternion::setToXAxis, py::arg("angle")) .def("setToYAxis", &MQuaternion::setToYAxis, py::arg("angle")) - .def("setToZAxis", &MQuaternion::setToZAxis, , py::arg("angle")) + .def("setToZAxis", &MQuaternion::setToZAxis, py::arg("angle")) .def("setValue", [](MQuaternion& self, const MQuaternion& quat) { self = quat; }, py::arg("quat")) From ca8663a100ae2d27308677ffa0ccb74838b33d26 Mon Sep 17 00:00:00 2001 From: Scott Englert Date: Sat, 11 Jun 2022 13:28:25 -0700 Subject: [PATCH 11/11] fixed another missing comma, ide failed to save --- src/Math.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Math.inl b/src/Math.inl index 943925a..0fb2438 100644 --- a/src/Math.inl +++ b/src/Math.inl @@ -300,13 +300,13 @@ Quaternion .def_static("slerp", [](const MQuaternion& p, const MQuaternion& q, double t, short spin) { return slerp(p, q, t, spin); - }py::arg("p"), py::arg("q"), py::arg("t"), py::arg("spin")) + }, py::arg("p"), py::arg("q"), py::arg("t"), py::arg("spin")) .def_static("squad", [](const MQuaternion& p, const MQuaternion& a, const MQuaternion& b, const MQuaternion& q, double t, short spin) { return squad(p, a, b, q, t, spin); - }py::arg("p"), py::arg("a"), py::arg("b"), py::arg("q"), py::arg("t"), py::arg("spin")) + }, py::arg("p"), py::arg("a"), py::arg("b"), py::arg("q"), py::arg("t"), py::arg("spin")) .def_static("squadPt", [](const MQuaternion& q0, const MQuaternion& q1, const MQuaternion& q2) { return squadPt(q0, q1, q2);