From 782b3ef6f4eb56c74a62df6d04273691eb76d0d1 Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sun, 6 Jun 2021 14:10:13 -0700 Subject: [PATCH 01/14] Add gitignore section for IDE folders --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 50592d7..4829ffc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ tmp/* *.pyc MFn.Types.inl devkit.tgz -devkitBase \ No newline at end of file +devkitBase + +# IDE stuff +.vscode From 03907531334fdae5f3a8ce5541884fe2e028be18 Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sun, 6 Jun 2021 14:20:55 -0700 Subject: [PATCH 02/14] Fix typo in Windows build script --- build_win32.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_win32.ps1 b/build_win32.ps1 index b89ed8f..22dda98 100644 --- a/build_win32.ps1 +++ b/build_win32.ps1 @@ -121,7 +121,7 @@ Write-Host "(3) Finished in $link_duration ms" Write-Host "(3) ----------------------------" Write-Host "(4) Cleaning.." -& python .\scripts\mfn.py clean +& python .\scripts\mfn.py clear $t4 = $stopwatch.ElapsedMilliseconds From 504c432495d640a4a31564d1a675581c8b04d569 Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sun, 6 Jun 2021 14:21:37 -0700 Subject: [PATCH 03/14] Add MDagModifier binding stub --- src/MDagModifier.inl | 39 +++++++++++++++++++++++++++++++++++++++ src/main.cpp | 6 ++++-- 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 src/MDagModifier.inl diff --git a/src/MDagModifier.inl b/src/MDagModifier.inl new file mode 100644 index 0000000..b64f2b1 --- /dev/null +++ b/src/MDagModifier.inl @@ -0,0 +1,39 @@ +py::class_(m, "DagModifier") + .def(py::init<>()) + + .def("createNode", [](MDagModifier & self, MString type, MObject parent = MObject::kNullObj) -> MObject { + throw std::logic_error{"Function not yet implemented."}; + }, +R"pbdoc(Adds an operation to the modifier to create a DAG node of the specified type. +If a parent DAG node is provided the new node will be parented under it. +If no parent is provided and the new DAG node is a transform type then it will be parented under the world. +In both of these cases, the method returns the new DAG node. + +If no parent is provided and the new DAG node is not a transform type +then a transform node will be created and the child parented under that. +The new transform will be parented under the world \ +and it is the transform node which will be returned by the method, not the child. + +None of the newly created nodes will be added to the DAG until the modifier's doIt() method is called.)pbdoc") + + .def("createNode", [](MDagModifier & self, MTypeId typeId, MObject parent = MObject::kNullObj) -> MObject { + throw std::logic_error{"Function not yet implemented."}; + }, R"pbdoc(Adds an operation to the modifier to create a DAG node of the specified type. + +If a parent DAG node is provided the new node will be parented under it. +If no parent is provided and the new DAG node is a transform type then it will be parented under the world. +In both of these cases the method returns the new DAG node. + +If no parent is provided and the new DAG node is not a transform type +then a transform node will be created and the child parented under that. +The new transform will be parented under the world \ +and it is the transform node which will be returned by the method, not the child. + +None of the newly created nodes will be added to the DAG until the modifier's doIt() method is called.)pbdoc") + + .def("reparentNode", [](MDagModifier & self, MObject node, MObject newParent = MObject::kNullObj) { + throw std::logic_error{"Function not yet implemented."}; + }, R"pbdoc(Adds an operation to the modifier to reparent a DAG node under a specified parent. + +If no parent is provided then the DAG node will be reparented under the world, so long as it is a transform type. +If it is not a transform type then the doIt() will raise a RuntimeError.)pbdoc"); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index d69c878..91e8ffa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,11 +12,12 @@ // Types #include #include +#include +#include #include #include -#include #include -#include +#include #include #include #include @@ -66,6 +67,7 @@ PYBIND11_MODULE(cmdc, m) { )pbdoc"; #include "Math.inl" + #include "MDagModifier.inl" #include "MDagPath.inl" #include "MDGModifier.inl" #include "MFn.inl" From 64f18b09acfc5645d4e45b0429a082105c8f6d30 Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sun, 6 Jun 2021 14:51:11 -0700 Subject: [PATCH 04/14] Implement MDagModifier::createNode bindings --- src/MDagModifier.inl | 60 +++++++++++++++++++++++++++++++----- tests/test_MDagModifier.py | 62 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 tests/test_MDagModifier.py diff --git a/src/MDagModifier.inl b/src/MDagModifier.inl index b64f2b1..789dab7 100644 --- a/src/MDagModifier.inl +++ b/src/MDagModifier.inl @@ -1,10 +1,34 @@ -py::class_(m, "DagModifier") +py::class_(m, "DagModifier", "DGModifier") .def(py::init<>()) - .def("createNode", [](MDagModifier & self, MString type, MObject parent = MObject::kNullObj) -> MObject { - throw std::logic_error{"Function not yet implemented."}; - }, -R"pbdoc(Adds an operation to the modifier to create a DAG node of the specified type. + .def("createNode", [](MDagModifier & self, std::string type, MObject parent = MObject::kNullObj) -> MObject { + if (!parent.isNull()) + { + validate::has_fn( + parent, MFn::kDagNode, + "Cannot createNode - 'parent' must be a 'kDagNode' object , not a '^1s' object." + ); + } + + MString type_name(type.c_str()); + MStatus status; + MObject result = self.createNode(type_name, parent, &status); + + if (status == MS::kInvalidParameter) + { + MString error_msg("Cannot create dependency node '^1s' - use DGModifier instead."); + error_msg.format(error_msg, type_name); + throw pybind11::type_error(error_msg.asChar()); + } else if (result.isNull()) { + MString error_msg("Cannot create unknown node type '^1s'."); + error_msg.format(error_msg, type_name); + throw pybind11::type_error(error_msg.asChar()); + } + + CHECK_STATUS(status) + + return result; + }, R"pbdoc(Adds an operation to the modifier to create a DAG node of the specified type. If a parent DAG node is provided the new node will be parented under it. If no parent is provided and the new DAG node is a transform type then it will be parented under the world. In both of these cases, the method returns the new DAG node. @@ -17,8 +41,27 @@ and it is the transform node which will be returned by the method, not the child None of the newly created nodes will be added to the DAG until the modifier's doIt() method is called.)pbdoc") .def("createNode", [](MDagModifier & self, MTypeId typeId, MObject parent = MObject::kNullObj) -> MObject { - throw std::logic_error{"Function not yet implemented."}; - }, R"pbdoc(Adds an operation to the modifier to create a DAG node of the specified type. + MString type_id_str = MString() + typeId.id(); + + MStatus status; + MObject result = self.createNode(typeId, parent, &status); + + if (status == MS::kInvalidParameter) + { + MString error_msg("Cannot create dependency node with type ID '^1s'' - use DGModifier instead."); + error_msg.format(error_msg, type_id_str); + throw pybind11::type_error(error_msg.asChar()); + } else if (result.isNull()) { + MString error_msg("Cannot create unknown node with type ID '^1s'."); + error_msg.format(error_msg, type_id_str); + throw pybind11::type_error(error_msg.asChar()); + } + + CHECK_STATUS(status) + + return result; + }, +R"pbdoc(Adds an operation to the modifier to create a DAG node of the specified type. If a parent DAG node is provided the new node will be parented under it. If no parent is provided and the new DAG node is a transform type then it will be parented under the world. @@ -33,7 +76,8 @@ None of the newly created nodes will be added to the DAG until the modifier's do .def("reparentNode", [](MDagModifier & self, MObject node, MObject newParent = MObject::kNullObj) { throw std::logic_error{"Function not yet implemented."}; - }, R"pbdoc(Adds an operation to the modifier to reparent a DAG node under a specified parent. + }, +R"pbdoc(Adds an operation to the modifier to reparent a DAG node under a specified parent. If no parent is provided then the DAG node will be reparented under the world, so long as it is a transform type. If it is not a transform type then the doIt() will raise a RuntimeError.)pbdoc"); \ No newline at end of file diff --git a/tests/test_MDagModifier.py b/tests/test_MDagModifier.py new file mode 100644 index 0000000..8d9da06 --- /dev/null +++ b/tests/test_MDagModifier.py @@ -0,0 +1,62 @@ +import cmdc +import nose + +from nose.plugins.skip import SkipTest + +from maya import cmds +from maya.api import OpenMaya + +from . import assert_equals, as_obj, as_plug, new_scene + +def test_createNode(): + raise SkipTest("Cannot test DAGModifier.createNode - not sure how to subclass from DGModifier.") + + + node = as_obj(cmds.createNode('transform')) + type_id = cmdc.FnDependencyNode(node).typeId() + + for name, value in ( + ['typeName', 'transform'], + ['typeId', cmdc.TypeId(type_id)] + ): + test_createNode.__doc__ = """Test MDagModifier::createNode if called with a valid {}.""".format(name) + + yield _createNode_pass, value + + for name, value in ( + ['typeName', 'foobar'], + ['typeName', 'network'], + ['typeId', cmdc.TypeId(0xdeadbeef)] + ): + test_createNode.__doc__ = """Test MDagGModifier::createNode raises error if called with an invalid {}.""".format(name) + + yield _createNode_fail, value + + +@nose.with_setup(teardown=new_scene) +def _createNode_fail(arg): + old_nodes = cmds.ls(long=True) + + nose.tools.assert_raises( + TypeError, _createNode_pass, arg + ) + + new_nodes = cmds.ls(long=True) + + assert len(old_nodes) == len(new_nodes), "DagModifier.createNode modified the scene graph." + + +@nose.with_setup(teardown=new_scene) +def _createNode_pass(arg): + old_nodes = cmds.ls(long=True) + + mod = cmdc.DagModifier() + node = mod.createNode(arg, cmdc.Object()) + mod.doIt() + + new_nodes = cmds.ls(long=True) + + add_nodes = set(new_nodes) - set(old_nodes) + + assert not node.isNull(), "Created node is not valid." + assert len(add_nodes) == 1, "`ls` did not return new node." From 90d1aace868486b22020fccb8ecf3e2b262278a5 Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sun, 6 Jun 2021 18:34:16 -0700 Subject: [PATCH 05/14] Make DagModifier a subclass of DGModifier If you Google "pybind11 subclass" you will find a couple of threads on how to solve the problem, none of which worked in this scenario. The issue on the pybind github about subclasses (below) cites a way to subclass an existing binding that feels as robust as creating a class on the fly using type("MyClass", bases, attrs) -> https://github.com/pybind/pybind11/issues/1193 Another issue recommends using py:base, which has been deprecated. -> https://github.com/pybind/pybind11/issues/17://github.com/pybind/pybind11/issues/172 ...also it didn't work. It seems to me that because we are defining each class as an .inl rather than a .hpp or .cpp file, they are not aware of each other. To remedy this, I've included the MDGModifier binding in the MDagModifier file, with an pre-processor to prevent duplicate entries. Otherwise, the base class would be omitted in the main.cpp. I think this solution will work well when we get to other classes with many subclasses, like the whole MFn* tree. --- src/MDGModifier.inl | 5 ++++- src/MDagModifier.inl | 4 +++- tests/test_MDagModifier.py | 3 --- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/MDGModifier.inl b/src/MDGModifier.inl index 26f2521..f294391 100644 --- a/src/MDGModifier.inl +++ b/src/MDGModifier.inl @@ -1,3 +1,5 @@ +#ifndef DGMODIFIER_INL +#define DGMODIFIER_INL py::class_(m, "DGModifier") .def(py::init<>()) @@ -518,4 +520,5 @@ This requirement is used when the plugin is checked to see if it is in use, or i For compound attributes only the topmost parent attribute may be passed in and all of its children will be unlinked, recursively. Thus it's not possible to unlink a child attribute from a plugin by itself. -Note that the link is broken immediately and is not affected by the modifier's doIt() or undoIt() methods.)pbdoc"); \ No newline at end of file +Note that the link is broken immediately and is not affected by the modifier's doIt() or undoIt() methods.)pbdoc"); +#endif // DGMODIFIER_INL \ No newline at end of file diff --git a/src/MDagModifier.inl b/src/MDagModifier.inl index 789dab7..1d1281c 100644 --- a/src/MDagModifier.inl +++ b/src/MDagModifier.inl @@ -1,4 +1,6 @@ -py::class_(m, "DagModifier", "DGModifier") +#include "MDGModifier.inl" + +py::class_(m, "DagModifier") .def(py::init<>()) .def("createNode", [](MDagModifier & self, std::string type, MObject parent = MObject::kNullObj) -> MObject { diff --git a/tests/test_MDagModifier.py b/tests/test_MDagModifier.py index 8d9da06..3da8002 100644 --- a/tests/test_MDagModifier.py +++ b/tests/test_MDagModifier.py @@ -9,9 +9,6 @@ from . import assert_equals, as_obj, as_plug, new_scene def test_createNode(): - raise SkipTest("Cannot test DAGModifier.createNode - not sure how to subclass from DGModifier.") - - node = as_obj(cmds.createNode('transform')) type_id = cmdc.FnDependencyNode(node).typeId() From 4bf96b0c52048a80531545d9f6bb0ca4051885d2 Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sun, 6 Jun 2021 19:13:19 -0700 Subject: [PATCH 06/14] Add missing cases to MDagModifier test coverage --- src/MDagModifier.inl | 14 ++++++++++-- tests/test_MDagModifier.py | 47 +++++++++++++++++++++++--------------- 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/MDagModifier.inl b/src/MDagModifier.inl index 1d1281c..e87749c 100644 --- a/src/MDagModifier.inl +++ b/src/MDagModifier.inl @@ -30,7 +30,8 @@ py::class_(m, "DagModifier") CHECK_STATUS(status) return result; - }, R"pbdoc(Adds an operation to the modifier to create a DAG node of the specified type. + }, +R"pbdoc(Adds an operation to the modifier to create a DAG node of the specified type. If a parent DAG node is provided the new node will be parented under it. If no parent is provided and the new DAG node is a transform type then it will be parented under the world. In both of these cases, the method returns the new DAG node. @@ -43,6 +44,14 @@ and it is the transform node which will be returned by the method, not the child None of the newly created nodes will be added to the DAG until the modifier's doIt() method is called.)pbdoc") .def("createNode", [](MDagModifier & self, MTypeId typeId, MObject parent = MObject::kNullObj) -> MObject { + if (!parent.isNull()) + { + validate::has_fn( + parent, MFn::kDagNode, + "Cannot createNode - 'parent' must be a 'kDagNode' object , not a '^1s' object." + ); + } + MString type_id_str = MString() + typeId.id(); MStatus status; @@ -62,7 +71,8 @@ None of the newly created nodes will be added to the DAG until the modifier's do CHECK_STATUS(status) return result; - }, + }, // py::arg("typeId"), + // py::arg("parent") = MObject::kNullObj, R"pbdoc(Adds an operation to the modifier to create a DAG node of the specified type. If a parent DAG node is provided the new node will be parented under it. diff --git a/tests/test_MDagModifier.py b/tests/test_MDagModifier.py index 3da8002..e08c022 100644 --- a/tests/test_MDagModifier.py +++ b/tests/test_MDagModifier.py @@ -9,33 +9,44 @@ from . import assert_equals, as_obj, as_plug, new_scene def test_createNode(): - node = as_obj(cmds.createNode('transform')) - type_id = cmdc.FnDependencyNode(node).typeId() - - for name, value in ( - ['typeName', 'transform'], - ['typeId', cmdc.TypeId(type_id)] + node = cmds.createNode('transform', name='root') + node_obj = as_obj(node) + null_obj = cmdc.Object() + type_id = cmdc.FnDependencyNode(node_obj).typeId() + + for doc, (value, parent) in ( + ['a valid type name', ('transform', null_obj)], + ['a valid type name and parent', ('transform', node_obj)], + ['a valid typeId', (cmdc.TypeId(type_id), null_obj)], + ['a valid typeId and parent', (cmdc.TypeId(type_id), node_obj)], ): - test_createNode.__doc__ = """Test MDagModifier::createNode if called with a valid {}.""".format(name) + test_createNode.__doc__ = """Test MDagModifier::createNode if called with {}.""".format(doc) + + yield _createNode_pass, value, parent - yield _createNode_pass, value + not_a_dag = as_obj('time1') + not_a_node = as_plug('persp.message').attribute() + type_id = cmdc.FnDependencyNode(as_obj('time1')).typeId() - for name, value in ( - ['typeName', 'foobar'], - ['typeName', 'network'], - ['typeId', cmdc.TypeId(0xdeadbeef)] + for doc, (value, parent) in ( + ['an invalid type name', ('foobar', null_obj)], + ['a non-DAG type name', ('network', null_obj)], + ['an invalid typeId', (cmdc.TypeId(0xdeadbeef), null_obj)], + ['an non-DAG typeId', (cmdc.TypeId(type_id), null_obj)], + ['an invalid parent (not a DAG node)', ('transform', not_a_dag)], + ['an invalid parent (not a node)', ('transform', not_a_node)], ): - test_createNode.__doc__ = """Test MDagGModifier::createNode raises error if called with an invalid {}.""".format(name) + test_createNode.__doc__ = """Test MDagGModifier::createNode raises error if called with {}.""".format(doc) - yield _createNode_fail, value + yield _createNode_fail, value, parent @nose.with_setup(teardown=new_scene) -def _createNode_fail(arg): +def _createNode_fail(value, parent): old_nodes = cmds.ls(long=True) nose.tools.assert_raises( - TypeError, _createNode_pass, arg + TypeError, _createNode_pass, value, parent ) new_nodes = cmds.ls(long=True) @@ -44,11 +55,11 @@ def _createNode_fail(arg): @nose.with_setup(teardown=new_scene) -def _createNode_pass(arg): +def _createNode_pass(value, parent): old_nodes = cmds.ls(long=True) mod = cmdc.DagModifier() - node = mod.createNode(arg, cmdc.Object()) + node = mod.createNode(value, parent) mod.doIt() new_nodes = cmds.ls(long=True) From 0604e77c5d24ad124f32f1ff586f9c3a4d6d4053 Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sun, 6 Jun 2021 19:36:59 -0700 Subject: [PATCH 07/14] Implement MDagModifier::reparentNode binding --- src/MDagModifier.inl | 34 ++++++++++++++++-- tests/test_MDagModifier.py | 70 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/src/MDagModifier.inl b/src/MDagModifier.inl index e87749c..f003029 100644 --- a/src/MDagModifier.inl +++ b/src/MDagModifier.inl @@ -71,8 +71,7 @@ None of the newly created nodes will be added to the DAG until the modifier's do CHECK_STATUS(status) return result; - }, // py::arg("typeId"), - // py::arg("parent") = MObject::kNullObj, + }, R"pbdoc(Adds an operation to the modifier to create a DAG node of the specified type. If a parent DAG node is provided the new node will be parented under it. @@ -87,7 +86,36 @@ and it is the transform node which will be returned by the method, not the child None of the newly created nodes will be added to the DAG until the modifier's doIt() method is called.)pbdoc") .def("reparentNode", [](MDagModifier & self, MObject node, MObject newParent = MObject::kNullObj) { - throw std::logic_error{"Function not yet implemented."}; + validate::is_not_null(node, "Cannot reparent a null object."); + validate::has_fn( + node, MFn::kDagNode, + "Cannot reparent - 'node' must be a 'kDagNode' object , not a '^1s' object." + ); + + if (!newParent.isNull()) + { + validate::has_fn( + newParent, MFn::kDagNode, + "Cannot reparent - 'parent' must be a 'kDagNode' object , not a '^1s' object." + ); + + MFnDagNode fn(newParent); + + if (fn.isChildOf(node)) + { + throw std::invalid_argument("Cannot parent a transform to one of its children."); + } + } + + if (node == newParent) + { + throw std::invalid_argument("Cannot parent a transform to itself."); + } + + + MStatus status = self.reparentNode(node, newParent); + + CHECK_STATUS(status) }, R"pbdoc(Adds an operation to the modifier to reparent a DAG node under a specified parent. diff --git a/tests/test_MDagModifier.py b/tests/test_MDagModifier.py index e08c022..9d2fa6e 100644 --- a/tests/test_MDagModifier.py +++ b/tests/test_MDagModifier.py @@ -9,6 +9,8 @@ from . import assert_equals, as_obj, as_plug, new_scene def test_createNode(): + return + node = cmds.createNode('transform', name='root') node_obj = as_obj(node) null_obj = cmdc.Object() @@ -68,3 +70,71 @@ def _createNode_pass(value, parent): assert not node.isNull(), "Created node is not valid." assert len(add_nodes) == 1, "`ls` did not return new node." + + +def test_reparentNode(): + node_a = cmds.createNode('transform') + node_b = cmds.createNode('transform') + node_c = cmds.createNode('transform', parent=node_a) + node_d = cmds.createNode('transform', parent=node_c) + + node_obj_a = as_obj(node_a) + node_obj_b = as_obj(node_b) + node_obj_c = as_obj(node_c) + node_obj_d = as_obj(node_d) + null_obj = cmdc.Object() + + for doc, (node, new_parent) in ( + ['a null object (parent to world)', (node_obj_c, null_obj)], + ['a valid object', (node_obj_c, node_obj_b)], + ): + test_reparentNode.__doc__ = """Test MDagModifier::reparentNode if called with {}.""".format(doc) + + yield _reparentNode_pass, node, new_parent + + not_a_dag = as_obj('time1') + not_a_node = as_plug('persp.message').attribute() + + for exc, doc, (node, new_parent) in ( + [TypeError, 'an invalid object (not a DAG node)', (node_obj_c, not_a_dag)], + [TypeError, 'an invalid object (not a node)', (node_obj_c, not_a_node)], + [ValueError, 'the same object', (node_obj_c, node_obj_c)], + [ValueError, 'a parent and one of its children', (node_obj_c, node_obj_d)], + ): + test_reparentNode.__doc__ = """Test MDagModifier::reparentNode raises an error if called with {}.""".format(doc) + + yield _reparentNode_fail, exc, node, new_parent + + +def _reparentNode_pass(node, new_parent): + fn_node = cmdc.FnDagNode(node) + + old_parent = fn_node.parent(0) + + mod = cmdc.DagModifier() + mod.reparentNode(node, new_parent) + + mod.doIt() + parent = fn_node.parent(0) + + if new_parent.isNull(): + assert parent == fn_node.dagRoot(), "DagModifier.reparentNode doIt failed" + else: + assert parent == new_parent, "DagModifier.reparentNode doIt failed" + + mod.undoIt() + parent = fn_node.parent(0) + assert parent == old_parent, "DagModifier.reparentNode undo failed" + + # Parent the node to world before the next test. + mod = cmdc.DagModifier() + mod.reparentNode(node, old_parent) + mod.doIt() + + +def _reparentNode_fail(exception, node, new_parent): + nose.tools.assert_raises( + exception, + cmdc.DagModifier().reparentNode, + node, new_parent + ) \ No newline at end of file From 085c79a34f3fc7bac520ebd01f68d60853a2aa90 Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sun, 6 Jun 2021 19:53:51 -0700 Subject: [PATCH 08/14] Include node names in MDagModifier::reparentNode binding error messages --- src/MDagModifier.inl | 46 +++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/src/MDagModifier.inl b/src/MDagModifier.inl index f003029..7af9350 100644 --- a/src/MDagModifier.inl +++ b/src/MDagModifier.inl @@ -87,32 +87,56 @@ None of the newly created nodes will be added to the DAG until the modifier's do .def("reparentNode", [](MDagModifier & self, MObject node, MObject newParent = MObject::kNullObj) { validate::is_not_null(node, "Cannot reparent a null object."); - validate::has_fn( - node, MFn::kDagNode, - "Cannot reparent - 'node' must be a 'kDagNode' object , not a '^1s' object." - ); + if (!node.hasFn(MFn::kDagNode)) + { + MString error_msg("Cannot parent '^1s' to '^2s' - must specify a 'kDagNode' object , not a '^3s' object."); + error_msg.format( + error_msg, + MFnDependencyNode(node).name(), + newParent.isNull() ? "the world" : MFnDependencyNode(newParent).name(), + node.apiTypeStr() + ); + throw pybind11::type_error(error_msg.asChar()); + } if (!newParent.isNull()) { - validate::has_fn( - newParent, MFn::kDagNode, - "Cannot reparent - 'parent' must be a 'kDagNode' object , not a '^1s' object." - ); + if (!newParent.hasFn(MFn::kDagNode)) + { + MString error_msg("Cannot parent '^1s' to '^2s' - must specify a 'kDagNode' object , not a '^3s' object."); + error_msg.format( + error_msg, + MFnDependencyNode(node).name(), + newParent.isNull() ? "the world" : MFnDependencyNode(newParent).name(), + newParent.apiTypeStr() + ); + throw pybind11::type_error(error_msg.asChar()); + } MFnDagNode fn(newParent); if (fn.isChildOf(node)) { - throw std::invalid_argument("Cannot parent a transform to one of its children."); + MString error_msg("Cannot parent '^1s' to one of its children, '^2s'."); + error_msg.format( + error_msg, + MFnDagNode(node).partialPathName(), + MFnDagNode(newParent).partialPathName() + ); + throw std::invalid_argument(error_msg.asChar()); } } if (node == newParent) { - throw std::invalid_argument("Cannot parent a transform to itself."); + MString error_msg("Cannot parent '^1s' to itself."); + error_msg.format( + error_msg, + MFnDagNode(node).partialPathName() + ); + throw std::invalid_argument(error_msg.asChar()); } - MStatus status = self.reparentNode(node, newParent); CHECK_STATUS(status) From c1b8778e7e62f28fbde1340cc775efd6a9d62bb1 Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sun, 6 Jun 2021 19:57:27 -0700 Subject: [PATCH 09/14] Fix typo in build_linux.sh --- build_linux.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_linux.sh b/build_linux.sh index fd7b281..ad8a7ee 100644 --- a/build_linux.sh +++ b/build_linux.sh @@ -68,7 +68,7 @@ echo "(2) Finished in $compile_duration ms" echo "(2) ----------------------------" echo "(3) Cleaning.." -python ./scripts/mfn.py clean +python ./scripts/mfn.py clear t3=$(date +%s.%N) clean_duration=$(echo "(($t3 - $t2) * 1000)/1" | bc) From 41d91d792bff868160683c2bd838caa5f595b64f Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Fri, 11 Jun 2021 20:33:09 -0700 Subject: [PATCH 10/14] Fix missing endif --- src/MDGModifier.inl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/MDGModifier.inl b/src/MDGModifier.inl index 51c4399..c1ad237 100644 --- a/src/MDGModifier.inl +++ b/src/MDGModifier.inl @@ -707,3 +707,4 @@ py::class_(m, "DGModifier") }, py::arg("plugin"), py::arg("attribute"), _doc_DGModifier_unlinkExtensionAttributeFromPlugin); +#endif DGMODIFIER_INL \ No newline at end of file From ceb5bf63c11c4a9aa927a729ef4a93460f37ebae Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sat, 12 Jun 2021 11:50:36 -0700 Subject: [PATCH 11/14] Fix duplication declaration of DGModifier binding --- src/MDGModifier.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MDGModifier.inl b/src/MDGModifier.inl index c1ad237..62f8d34 100644 --- a/src/MDGModifier.inl +++ b/src/MDGModifier.inl @@ -1,6 +1,5 @@ #ifndef DGMODIFIER_INL #define DGMODIFIER_INL -py::class_(m, "DGModifier") #define _doc_DGModifier_addAttribute \ "Adds an operation to the modifier to add a new dynamic attribute to\n"\ "the given dependency node.\n"\ @@ -199,6 +198,7 @@ py::class_(m, "DGModifier") "Note that the link is broken immediately and is not affected by\n"\ "the modifier's doIt() or undoIt() methods." +DGModifier .def(py::init<>()) .def("addAttribute", [](MDGModifier & self, MObject node, MObject attribute) { From 80ee3d0728cf282d550a7f78aab286d33ffd1780 Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sat, 12 Jun 2021 11:51:34 -0700 Subject: [PATCH 12/14] Update docker build linux script to use maya{version} environment --- docker_build_linux.ps1 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docker_build_linux.ps1 b/docker_build_linux.ps1 index 1cc04ae..97430d3 100644 --- a/docker_build_linux.ps1 +++ b/docker_build_linux.ps1 @@ -1,9 +1,8 @@ param ( - [string]$maya_version = "2020" + [string]$maya_version = "2022" ) docker run -ti --rm ` - -v ${env:DEVKIT_LOCATION}:/devkit ` -v ${PWD}:/workspace ` - -e DEVKIT_LOCATION=/devkit ` - cmdc ./build_linux.sh $maya_version + cmdc:${maya_version} ` + ./build_linux.sh $maya_version From 255cc019bab9e2d683ed1b27612f68ee76f4c1eb Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sat, 12 Jun 2021 11:51:44 -0700 Subject: [PATCH 13/14] Add docker test linux script --- docker_test_linux.ps1 | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 docker_test_linux.ps1 diff --git a/docker_test_linux.ps1 b/docker_test_linux.ps1 new file mode 100644 index 0000000..c778f9a --- /dev/null +++ b/docker_test_linux.ps1 @@ -0,0 +1,8 @@ +param ( + [string]$maya_version = "2022" +) + +docker run -ti --rm ` + -v ${PWD}:/workspace ` + cmdc:${maya_version} ` + mayapy -m nose -xv --exe ./tests \ No newline at end of file From d37b487a9ec604acab30597f33d279ee78573cb1 Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sat, 12 Jun 2021 11:51:54 -0700 Subject: [PATCH 14/14] Rename test script for windows to match build script --- test.ps1 => test_win32.ps1 | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test.ps1 => test_win32.ps1 (100%) diff --git a/test.ps1 b/test_win32.ps1 similarity index 100% rename from test.ps1 rename to test_win32.ps1