From abc2edbf12f82d4a5feab45588a94f9ed2acd5fd Mon Sep 17 00:00:00 2001 From: Stefan Habel <19556655+StefanHabel@users.noreply.github.com> Date: Tue, 17 Oct 2023 23:48:34 -0700 Subject: [PATCH] Sphinx Python Documentation (#1567) - Added docstrings to PyMaterialXCore. --- .../PyMaterialXCore/PyDefinition.cpp | 76 ++++- .../PyMaterialXCore/PyDocument.cpp | 14 +- .../PyMaterialX/PyMaterialXCore/PyElement.cpp | 116 +++++++- .../PyMaterialXCore/PyException.cpp | 6 + source/PyMaterialX/PyMaterialXCore/PyGeom.cpp | 60 +++- .../PyMaterialXCore/PyInterface.cpp | 39 ++- source/PyMaterialX/PyMaterialXCore/PyLook.cpp | 46 ++- .../PyMaterialXCore/PyMaterial.cpp | 21 +- .../PyMaterialX/PyMaterialXCore/PyModule.cpp | 263 +++++++++++++++++- source/PyMaterialX/PyMaterialXCore/PyNode.cpp | 38 ++- .../PyMaterialXCore/PyProperty.cpp | 28 +- .../PyMaterialXCore/PyTraversal.cpp | 43 ++- .../PyMaterialX/PyMaterialXCore/PyTypes.cpp | 41 ++- .../PyMaterialXCore/PyUnitConverter.cpp | 24 +- source/PyMaterialX/PyMaterialXCore/PyUtil.cpp | 141 +++++++++- .../PyMaterialX/PyMaterialXCore/PyValue.cpp | 34 ++- .../PyMaterialX/PyMaterialXCore/PyVariant.cpp | 21 +- 17 files changed, 911 insertions(+), 100 deletions(-) diff --git a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp index d4ccdfb13c..ef5f0ecedb 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp @@ -32,7 +32,15 @@ void bindPyDefinition(py::module& mod) .def_readonly_static("GEOMETRIC_NODE_GROUP", &mx::NodeDef::GEOMETRIC_NODE_GROUP) .def_readonly_static("ADJUSTMENT_NODE_GROUP", &mx::NodeDef::ADJUSTMENT_NODE_GROUP) .def_readonly_static("CONDITIONAL_NODE_GROUP", &mx::NodeDef::CONDITIONAL_NODE_GROUP) - .def_readonly_static("ORGANIZATION_NODE_GROUP", &mx::NodeDef::ORGANIZATION_NODE_GROUP); + .def_readonly_static("ORGANIZATION_NODE_GROUP", &mx::NodeDef::ORGANIZATION_NODE_GROUP) + .doc() = R"docstring( + Class representing a node definition element within a `Document`. + + A `NodeDef` provides the declaration of a node interface, which may then + be instantiated as a `Node`. + + :see: https://materialx.org/docs/api/class_node_def.html +)docstring"; py::class_(mod, "Implementation") .def("setFile", &mx::Implementation::setFile) @@ -48,7 +56,16 @@ void bindPyDefinition(py::module& mod) .def("getNodeGraph", &mx::Implementation::getNodeGraph) .def_readonly_static("CATEGORY", &mx::Implementation::CATEGORY) .def_readonly_static("FILE_ATTRIBUTE", &mx::Implementation::FILE_ATTRIBUTE) - .def_readonly_static("FUNCTION_ATTRIBUTE", &mx::Implementation::FUNCTION_ATTRIBUTE); + .def_readonly_static("FUNCTION_ATTRIBUTE", &mx::Implementation::FUNCTION_ATTRIBUTE) + .doc() = R"docstring( + Class representing an implementation element within a `Document`. + + An `Implementation` is used to associate external source code with a specific + `NodeDef`, providing a definition for the node that may either be universal or + restricted to a specific target. + + :see: https://materialx.org/docs/api/class_implementation.html +)docstring"; py::class_(mod, "TypeDef") .def("setSemantic", &mx::TypeDef::setSemantic) @@ -64,13 +81,28 @@ void bindPyDefinition(py::module& mod) .def("removeMember", &mx::TypeDef::removeMember) .def_readonly_static("CATEGORY", &mx::TypeDef::CATEGORY) .def_readonly_static("SEMANTIC_ATTRIBUTE", &mx::TypeDef::SEMANTIC_ATTRIBUTE) - .def_readonly_static("CONTEXT_ATTRIBUTE", &mx::TypeDef::CONTEXT_ATTRIBUTE); + .def_readonly_static("CONTEXT_ATTRIBUTE", &mx::TypeDef::CONTEXT_ATTRIBUTE) + .doc() = R"docstring( + Class representing a type definition element within a `Document`. + + :see: https://materialx.org/docs/api/class_type_def.html +)docstring"; py::class_(mod, "Member") - .def_readonly_static("CATEGORY", &mx::TypeDef::CATEGORY); + .def_readonly_static("CATEGORY", &mx::TypeDef::CATEGORY) + .doc() = R"docstring( + Class representing a member element within a `TypeDef`. + + :see: https://materialx.org/docs/api/class_member.html +)docstring"; py::class_(mod, "Unit") - .def_readonly_static("CATEGORY", &mx::Unit::CATEGORY); + .def_readonly_static("CATEGORY", &mx::Unit::CATEGORY) + .doc() = R"docstring( + Class representing a unit declaration element within a `UnitDef`. + + :see: https://materialx.org/docs/api/class_unit.html +)docstring"; py::class_(mod, "UnitDef") .def("setUnitType", &mx::UnitDef::hasUnitType) @@ -80,11 +112,21 @@ void bindPyDefinition(py::module& mod) .def("getUnit", &mx::UnitDef::getUnit) .def("getUnits", &mx::UnitDef::getUnits) .def_readonly_static("CATEGORY", &mx::UnitDef::CATEGORY) - .def_readonly_static("UNITTYPE_ATTRIBUTE", &mx::UnitDef::UNITTYPE_ATTRIBUTE); + .def_readonly_static("UNITTYPE_ATTRIBUTE", &mx::UnitDef::UNITTYPE_ATTRIBUTE) + .doc() = R"docstring( + Class representing a unit definition element within a `Document`. + + :see: https://materialx.org/docs/api/class_unit_def.html +)docstring"; py::class_(mod, "UnitTypeDef") .def("getUnitDefs", &mx::UnitTypeDef::getUnitDefs) - .def_readonly_static("CATEGORY", &mx::UnitTypeDef::CATEGORY); + .def_readonly_static("CATEGORY", &mx::UnitTypeDef::CATEGORY) + .doc() = R"docstring( + Class representing a unit type definition element within a `Document`. + + :see: https://materialx.org/docs/api/class_unit_type_def.html +)docstring"; py::class_(mod, "AttributeDef") .def("setAttrName", &mx::AttributeDef::setAttrName) @@ -92,12 +134,22 @@ void bindPyDefinition(py::module& mod) .def("getAttrName", &mx::AttributeDef::getAttrName) .def("setValueString", &mx::AttributeDef::setValueString) .def("hasValueString", &mx::AttributeDef::hasValueString) - .def("getValueString", &mx::AttributeDef::getValueString) - .def("setExportable", &mx::AttributeDef::setExportable) - .def("getExportable", &mx::AttributeDef::getExportable) - .def_readonly_static("CATEGORY", &mx::AttributeDef::CATEGORY); + .def("getValueString", &mx::AttributeDef::getValueString) + .def("setExportable", &mx::AttributeDef::setExportable) + .def("getExportable", &mx::AttributeDef::getExportable) + .def_readonly_static("CATEGORY", &mx::AttributeDef::CATEGORY) + .doc() = R"docstring( + Class representing an attribute definition element within a `Document`. + + :see: https://materialx.org/docs/api/class_attribute_def.html +)docstring"; py::class_(mod, "TargetDef") .def("getMatchingTargets", &mx::TargetDef::getMatchingTargets) - .def_readonly_static("CATEGORY", &mx::TargetDef::CATEGORY); + .def_readonly_static("CATEGORY", &mx::TargetDef::CATEGORY) + .doc() = R"docstring( + Class representing the definition of an implementation target as a `TypedElement`. + + :see: https://materialx.org/docs/api/class_target_def.html +)docstring"; } diff --git a/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp b/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp index 101e1bea64..9a17907ed3 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp @@ -12,7 +12,11 @@ namespace mx = MaterialX; void bindPyDocument(py::module& mod) { - mod.def("createDocument", &mx::createDocument); + mod.def("createDocument", &mx::createDocument, + R"docstring( + Create a MaterialX `Document` instance, which represents the top-level + element in the MaterialX ownership hierarchy. +)docstring"); py::class_(mod, "Document") .def("initialize", &mx::Document::initialize) @@ -102,5 +106,11 @@ void bindPyDocument(py::module& mod) .def("getColorManagementSystem", &mx::Document::getColorManagementSystem) .def("setColorManagementConfig", &mx::Document::setColorManagementConfig) .def("hasColorManagementConfig", &mx::Document::hasColorManagementConfig) - .def("getColorManagementConfig", &mx::Document::getColorManagementConfig); + .def("getColorManagementConfig", &mx::Document::getColorManagementConfig) + .doc() = R"docstring( + Class representing the top-level element in the MaterialX ownership + hierarchy. + + :see: https://materialx.org/docs/api/class_document.html +)docstring"; } diff --git a/source/PyMaterialX/PyMaterialXCore/PyElement.cpp b/source/PyMaterialX/PyMaterialXCore/PyElement.cpp index d89cbc81a4..6ac4418af5 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyElement.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyElement.cpp @@ -101,8 +101,11 @@ void bindPyElement(py::module& mod) .def("createValidChildName", &mx::Element::createValidChildName) .def("createStringResolver", &mx::Element::createStringResolver, py::arg("geom") = mx::EMPTY_STRING) - .def("asString", &mx::Element::asString) + .def("asString", &mx::Element::asString, + "Return a single-line description of this element, including its " + "category, name, and attributes.") .def("__str__", &mx::Element::asString) + BIND_ELEMENT_FUNC_INSTANCE(Collection) BIND_ELEMENT_FUNC_INSTANCE(Document) BIND_ELEMENT_FUNC_INSTANCE(GeomInfo) @@ -118,7 +121,16 @@ void bindPyElement(py::module& mod) BIND_ELEMENT_FUNC_INSTANCE(PropertySetAssign) BIND_ELEMENT_FUNC_INSTANCE(Token) BIND_ELEMENT_FUNC_INSTANCE(TypeDef) - BIND_ELEMENT_FUNC_INSTANCE(Visibility); + BIND_ELEMENT_FUNC_INSTANCE(Visibility) + + .doc() = R"docstring( + Base class for MaterialX elements. + + An `Element` is a named object within a `Document`, which may possess any + number of child elements and attributes. + + :see: https://materialx.org/docs/api/class_element.html +)docstring"; py::class_(mod, "TypedElement") .def("setType", &mx::TypedElement::setType) @@ -126,7 +138,12 @@ void bindPyElement(py::module& mod) .def("getType", &mx::TypedElement::getType) .def("isMultiOutputType", &mx::TypedElement::isMultiOutputType) .def("getTypeDef", &mx::TypedElement::getTypeDef) - .def_readonly_static("TYPE_ATTRIBUTE", &mx::TypedElement::TYPE_ATTRIBUTE); + .def_readonly_static("TYPE_ATTRIBUTE", &mx::TypedElement::TYPE_ATTRIBUTE) + .doc() = R"docstring( + Base class for typed elements. + + :see: https://materialx.org/docs/api/class_typed_element.html +)docstring"; py::class_(mod, "ValueElement") .def("setValueString", &mx::ValueElement::setValueString) @@ -181,19 +198,53 @@ void bindPyElement(py::module& mod) BIND_VALUE_ELEMENT_FUNC_INSTANCE(integerarray, mx::IntVec) BIND_VALUE_ELEMENT_FUNC_INSTANCE(booleanarray, mx::BoolVec) BIND_VALUE_ELEMENT_FUNC_INSTANCE(floatarray, mx::FloatVec) - BIND_VALUE_ELEMENT_FUNC_INSTANCE(stringarray, mx::StringVec); + BIND_VALUE_ELEMENT_FUNC_INSTANCE(stringarray, mx::StringVec) + + .doc() = R"docstring( + Base class for elements that support typed values. + + :see: https://materialx.org/docs/api/class_value_element.html +)docstring"; py::class_(mod, "Token") - .def_readonly_static("CATEGORY", &mx::Token::CATEGORY); + .def_readonly_static("CATEGORY", &mx::Token::CATEGORY) + .doc() = R"docstring( + A token element representing a string value. + + Token elements are used to define input and output values for string + substitutions in image filenames. + + :see: https://materialx.org/docs/api/class_token.html +)docstring"; py::class_(mod, "CommentElement") - .def_readonly_static("CATEGORY", &mx::CommentElement::CATEGORY); + .def_readonly_static("CATEGORY", &mx::CommentElement::CATEGORY) + .doc() = R"docstring( + Class representing a block of descriptive text within a `Document`, + which will be stored a comment when the document is written out. + + The comment text may be accessed with the methods `Element.getDocString()` + and `Element.setDocString()`. + + :see: https://materialx.org/docs/api/class_comment_element.html +)docstring"; py::class_(mod, "NewlineElement") - .def_readonly_static("CATEGORY", &mx::NewlineElement::CATEGORY); + .def_readonly_static("CATEGORY", &mx::NewlineElement::CATEGORY) + .doc() = R"docstring( + Class representing a newline within a `Document`. + + :see: https://materialx.org/docs/api/class_newline_element.html +)docstring"; py::class_(mod, "GenericElement") - .def_readonly_static("CATEGORY", &mx::GenericElement::CATEGORY); + .def_readonly_static("CATEGORY", &mx::GenericElement::CATEGORY) + .doc() = R"docstring( + A generic element subclass, for instantiating elements with unrecognized + categories. + + :see: https://materialx.org/docs/api/class_generic_element.html +)docstring"; py::class_(mod, "StringResolver") .def("setFilePrefix", &mx::StringResolver::setFilePrefix) @@ -206,12 +257,51 @@ void bindPyElement(py::module& mod) .def("getFilenameSubstitutions", &mx::StringResolver::getFilenameSubstitutions) .def("setGeomNameSubstitution", &mx::StringResolver::setGeomNameSubstitution) .def("getGeomNameSubstitutions", &mx::StringResolver::getGeomNameSubstitutions) - .def("resolve", &mx::StringResolver::resolve); + .def("resolve", &mx::StringResolver::resolve) + .doc() = R"docstring( + A helper class for applying string modifiers to data values in the context + of a specific element and geometry. + + A `StringResolver` may be constructed through the `Element.createStringResolver()` + method, which initializes it in the context of a specific `Element`, geometry, + and material. + + Calling the `StringResolver.resolve()` method applies all modifiers to a + particular string value. + + Methods such as `StringResolver.setFilePrefix()` may be used to edit the + stored string modifiers before calling `StringResolver.resolve()`. + + :see: https://materialx.org/docs/api/class_string_resolver.html +)docstring"; + + py::class_(mod, "ElementPredicate") + .doc() = R"docstring( + Class representing a function that takes an `Element` and returns a `bool`, + to check whether some criteria has passed. +)docstring"; + + py::register_exception(mod, "ExceptionOrphanedElement") + .doc() = R"docstring( + A type of exception that is raised when an `Element` is used after its owning + `Document` has gone out of scope. - py::class_(mod, "ElementPredicate"); + :see: https://materialx.org/docs/api/class_exception_orphaned_element.html +)docstring"; - py::register_exception(mod, "ExceptionOrphanedElement"); + mod.def("targetStringsMatch", &mx::targetStringsMatch, + py::arg("target1"), + py::arg("target2"), + R"docstring( + Given two target strings, each containing a string array of target names, + return `True` if they have any targets in common. - mod.def("targetStringsMatch", &mx::targetStringsMatch); - mod.def("prettyPrint", &mx::prettyPrint); + An empty target string matches all targets. +)docstring"); + mod.def("prettyPrint", &mx::prettyPrint, + py::arg("element"), + R"docstring( + Pretty-print the given `element` tree, calling `Element.asString()` + recursively on each element in depth-first order. +)docstring"); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyException.cpp b/source/PyMaterialX/PyMaterialXCore/PyException.cpp index 6f953ffc06..0b92d89ccd 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyException.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyException.cpp @@ -13,6 +13,12 @@ namespace mx = MaterialX; void bindPyException(py::module& mod) { static py::exception pyException(mod, "Exception"); + pyException.doc() = R"docstring( + The base class for exceptions that are propagated from the MaterialX library + to the client application. + + :see: https://materialx.org/docs/api/class_exception.html +)docstring"; py::register_exception_translator( [](std::exception_ptr errPtr) diff --git a/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp b/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp index 712851fac2..c0af7409cb 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp @@ -23,7 +23,13 @@ void bindPyGeom(py::module& mod) .def("hasCollectionString", &mx::GeomElement::hasCollectionString) .def("getCollectionString", &mx::GeomElement::getCollectionString) .def("setCollection", &mx::GeomElement::setCollection) - .def("getCollection", &mx::GeomElement::getCollection); + .def("getCollection", &mx::GeomElement::getCollection) + .doc() = R"docstring( + Base class for geometric elements, which support bindings to geometries + and geometric collections. + + :see: https://materialx.org/docs/api/class_geom_element.html +)docstring"; py::class_(mod, "GeomInfo") .def("addGeomProp", &mx::GeomInfo::addGeomProp) @@ -36,6 +42,7 @@ void bindPyGeom(py::module& mod) .def("getTokens", &mx::GeomInfo::getTokens) .def("removeToken", &mx::GeomInfo::removeToken) .def("setTokenValue", &mx::GeomInfo::setTokenValue) + BIND_GEOMINFO_FUNC_INSTANCE(integer, int) BIND_GEOMINFO_FUNC_INSTANCE(boolean, bool) BIND_GEOMINFO_FUNC_INSTANCE(float, float) @@ -51,10 +58,21 @@ void bindPyGeom(py::module& mod) BIND_GEOMINFO_FUNC_INSTANCE(booleanarray, mx::BoolVec) BIND_GEOMINFO_FUNC_INSTANCE(floatarray, mx::FloatVec) BIND_GEOMINFO_FUNC_INSTANCE(stringarray, mx::StringVec) - .def_readonly_static("CATEGORY", &mx::GeomInfo::CATEGORY); + + .def_readonly_static("CATEGORY", &mx::GeomInfo::CATEGORY) + .doc() = R"docstring( + Class representing a geometry info element within a `Document`. + + :see: https://materialx.org/docs/api/class_geom_info.html +)docstring"; py::class_(mod, "GeomProp") - .def_readonly_static("CATEGORY", &mx::GeomProp::CATEGORY); + .def_readonly_static("CATEGORY", &mx::GeomProp::CATEGORY) + .doc() = R"docstring( + Class representing a geometric property element within a `GeomInfo`. + + :see: https://materialx.org/docs/api/class_geom_prop.html +)docstring"; py::class_(mod, "GeomPropDef") .def("setGeomProp", &mx::GeomPropDef::setGeomProp) @@ -69,7 +87,18 @@ void bindPyGeom(py::module& mod) .def("setGeomProp", &mx::GeomPropDef::setGeomProp) .def("hasGeomProp", &mx::GeomPropDef::hasGeomProp) .def("getGeomProp", &mx::GeomPropDef::getGeomProp) - .def_readonly_static("CATEGORY", &mx::GeomPropDef::CATEGORY); + .def_readonly_static("CATEGORY", &mx::GeomPropDef::CATEGORY) + .doc() = R"docstring( + Class representing a declaration of geometric property data. + + A `GeomPropDef` element contains a reference to a geometric node and a set of + modifiers for that node. For example, a world-space normal can be declared + as a reference to the `"normal"` geometric node with a space setting of + `"world"`, or a specific set of texture coordinates can be declared as a + reference to the `"texcoord"` geometric node with an index setting of `"1"`. + + :see: https://materialx.org/docs/api/class_geom_prop_def.html +)docstring"; py::class_(mod, "Collection") .def("setIncludeGeom", &mx::Collection::setIncludeGeom) @@ -86,9 +115,28 @@ void bindPyGeom(py::module& mod) .def("getIncludeCollections", &mx::Collection::getIncludeCollections) .def("hasIncludeCycle", &mx::Collection::hasIncludeCycle) .def("matchesGeomString", &mx::Collection::matchesGeomString) - .def_readonly_static("CATEGORY", &mx::Collection::CATEGORY); + .def_readonly_static("CATEGORY", &mx::Collection::CATEGORY) + .doc() = R"docstring( + Class representing a collection element within a `Document`. + + :see: https://materialx.org/docs/api/class_geom_prop_def.html +)docstring"; + + mod.def( + "geomStringsMatch", &mx::geomStringsMatch, + py::arg("geom1"), py::arg("geom2"), py::arg("contains"), + R"docstring( + Given two geometry strings, each containing a list of geom names, return + `True` if they have any geometries in common. + + An empty geometry string matches no geometries, while the universal geometry + string `"/"` matches all non-empty geometries. + + If the `contains` argument is set to `True`, then we require that a geom path + in the first string completely contains a geom path in the second string. - mod.def("geomStringsMatch", &mx::geomStringsMatch); + :todo: Geometry name expressions are not yet supported. +)docstring"); mod.attr("GEOM_PATH_SEPARATOR") = mx::GEOM_PATH_SEPARATOR; mod.attr("UNIVERSAL_GEOM_NAME") = mx::UNIVERSAL_GEOM_NAME; diff --git a/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp b/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp index 2e9b264381..caf4f9b67b 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp @@ -31,7 +31,14 @@ void bindPyInterface(py::module& mod) .def("setConnectedNode", &mx::PortElement::setConnectedNode) .def("getConnectedNode", &mx::PortElement::getConnectedNode) .def("setConnectedOutput", &mx::PortElement::setConnectedOutput) - .def("getConnectedOutput", &mx::PortElement::getConnectedOutput); + .def("getConnectedOutput", &mx::PortElement::getConnectedOutput) + .doc() = R"docstring( + Base class for port elements such as `Input` and `Output`. + + Port elements support spatially-varying upstream connections to nodes. + + :see: https://materialx.org/docs/api/class_port_element.html +)docstring"; py::class_(mod, "Input") .def("setDefaultGeomPropString", &mx::Input::setDefaultGeomPropString) @@ -40,12 +47,26 @@ void bindPyInterface(py::module& mod) .def("getDefaultGeomProp", &mx::Input::getDefaultGeomProp) .def("getConnectedNode", &mx::Input::getConnectedNode) .def("getInterfaceInput", &mx::Input::getInterfaceInput) - .def_readonly_static("CATEGORY", &mx::Input::CATEGORY); + .def_readonly_static("CATEGORY", &mx::Input::CATEGORY) + .doc() = R"docstring( + Class representing an input element within a `Node` or `NodeDef`. + + An `Input` holds either a uniform value or a connection to a spatially-varying + `Output`, either of which may be modified within the scope of a `Material`. + + :see: https://materialx.org/docs/api/class_input.html +)docstring"; py::class_(mod, "Output") .def("hasUpstreamCycle", &mx::Output::hasUpstreamCycle) .def_readonly_static("CATEGORY", &mx::Output::CATEGORY) - .def_readonly_static("DEFAULT_INPUT_ATTRIBUTE", &mx::Output::DEFAULT_INPUT_ATTRIBUTE); + .def_readonly_static("DEFAULT_INPUT_ATTRIBUTE", &mx::Output::DEFAULT_INPUT_ATTRIBUTE) + .doc() = R"docstring( + Class representing a spatially-varying output element within a `NodeGraph` + or `NodeDef`. + + :see: https://materialx.org/docs/api/class_output.html +)docstring"; py::class_(mod, "InterfaceElement") .def("setNodeDefString", &mx::InterfaceElement::setNodeDefString) @@ -96,6 +117,7 @@ void bindPyInterface(py::module& mod) .def("clearContent", &mx::InterfaceElement::clearContent) .def("hasExactInputMatch", &mx::InterfaceElement::hasExactInputMatch, py::arg("declaration"), py::arg("message") = nullptr) + BIND_INTERFACE_TYPE_INSTANCE(integer, int) BIND_INTERFACE_TYPE_INSTANCE(boolean, bool) BIND_INTERFACE_TYPE_INSTANCE(float, float) @@ -111,5 +133,14 @@ void bindPyInterface(py::module& mod) BIND_INTERFACE_TYPE_INSTANCE(booleanarray, mx::BoolVec) BIND_INTERFACE_TYPE_INSTANCE(floatarray, mx::FloatVec) BIND_INTERFACE_TYPE_INSTANCE(stringarray, mx::StringVec) - .def_readonly_static("NODE_DEF_ATTRIBUTE", &mx::InterfaceElement::NODE_DEF_ATTRIBUTE); + + .def_readonly_static("NODE_DEF_ATTRIBUTE", &mx::InterfaceElement::NODE_DEF_ATTRIBUTE) + .doc() = R"docstring( + Base class for interface elements such as `Node`, `NodeDef`, and `NodeGraph`. + + An `InterfaceElement` supports a set of `Input` and `Output` elements, with an API + for setting their values. + + :see: https://materialx.org/docs/api/class_interface_element.html +)docstring"; } diff --git a/source/PyMaterialX/PyMaterialXCore/PyLook.cpp b/source/PyMaterialX/PyMaterialXCore/PyLook.cpp index 5d1e1e8af3..def6fd1a04 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyLook.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyLook.cpp @@ -46,7 +46,12 @@ void bindPyLook(py::module& mod) .def("getVisibilities", &mx::Look::getVisibilities) .def("getActiveVisibilities", &mx::Look::getActiveVisibilities) .def("removeVisibility", &mx::Look::removeVisibility) - .def_readonly_static("CATEGORY", &mx::Look::CATEGORY); + .def_readonly_static("CATEGORY", &mx::Look::CATEGORY) + .doc() = R"docstring( + Class representing a look element within a `Document`. + + :see: https://materialx.org/docs/api/class_look.html +)docstring"; py::class_(mod, "LookGroup") .def("getLooks", &mx::LookGroup::getLooks) @@ -55,7 +60,12 @@ void bindPyLook(py::module& mod) .def("setActiveLook", &mx::LookGroup::setActiveLook) .def_readonly_static("CATEGORY", &mx::LookGroup::CATEGORY) .def_readonly_static("LOOKS_ATTRIBUTE", &mx::LookGroup::LOOKS_ATTRIBUTE) - .def_readonly_static("ACTIVE_ATTRIBUTE", &mx::LookGroup::ACTIVE_ATTRIBUTE); + .def_readonly_static("ACTIVE_ATTRIBUTE", &mx::LookGroup::ACTIVE_ATTRIBUTE) + .doc() = R"docstring( + Class representing a look group element within a `Document`. + + :see: https://materialx.org/docs/api/class_look_group.html +)docstring"; py::class_(mod, "MaterialAssign") .def("setMaterial", &mx::MaterialAssign::setMaterial) @@ -65,7 +75,12 @@ void bindPyLook(py::module& mod) .def("setExclusive", &mx::MaterialAssign::setExclusive) .def("getExclusive", &mx::MaterialAssign::getExclusive) .def("getReferencedMaterial", &mx::MaterialAssign::getReferencedMaterial) - .def_readonly_static("CATEGORY", &mx::MaterialAssign::CATEGORY); + .def_readonly_static("CATEGORY", &mx::MaterialAssign::CATEGORY) + .doc() = R"docstring( + Class representing a material assignment element within a `Look`. + + :see: https://materialx.org/docs/api/class_material_assign.html +)docstring"; py::class_(mod, "Visibility") .def("setViewerGeom", &mx::Visibility::setViewerGeom) @@ -79,8 +94,29 @@ void bindPyLook(py::module& mod) .def("getVisibilityType", &mx::Visibility::getVisibilityType) .def("setVisible", &mx::Visibility::setVisible) .def("getVisible", &mx::Visibility::getVisible) - .def_readonly_static("CATEGORY", &mx::Visibility::CATEGORY); + .def_readonly_static("CATEGORY", &mx::Visibility::CATEGORY) + .doc() = R"docstring( + Class representing a visibility element within a `Look`. + + A `Visibility` describes the visibility relationship between two geometries + or geometric collections. + + :see: https://materialx.org/docs/api/class_visibility.html + :todo: Add a `Look.geomIsVisible()` method that computes the visibility between + two geometries in the context of a specific `Look`. +)docstring"; mod.def("getGeometryBindings", &mx::getGeometryBindings, - py::arg("materialNode") , py::arg("geom") = mx::UNIVERSAL_GEOM_NAME); + py::arg("materialNode"), + py::arg("geom") = mx::UNIVERSAL_GEOM_NAME, + R"docstring( + Return a list of all `MaterialAssign` elements that bind the given + `materialNode` to the given geometry string. + + :param materialNode: The node to examine. + :param geom: The geometry for which material bindings should be returned. + By default, this argument is the universal geometry string `"/"`, + and all material bindings are returned. + :return: List of `MaterialAssign` elements. +)docstring"); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyMaterial.cpp b/source/PyMaterialX/PyMaterialXCore/PyMaterial.cpp index 31656c67f9..62506f949e 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyMaterial.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyMaterial.cpp @@ -14,6 +14,23 @@ namespace mx = MaterialX; void bindPyMaterial(py::module& mod) { mod.def("getShaderNodes", &mx::getShaderNodes, - py::arg("materialNode"), py::arg("nodeType") = mx::SURFACE_SHADER_TYPE_STRING, py::arg("target") = mx::EMPTY_STRING); - mod.def("getConnectedOutputs", &mx::getConnectedOutputs); + py::arg("materialNode"), + py::arg("nodeType") = mx::SURFACE_SHADER_TYPE_STRING, + py::arg("target") = mx::EMPTY_STRING, + R"docstring( + Return a list of all shader nodes connected to the given `materialNode`'s inputs, + filtered by the given shader `nodeType` and `target`. + + By default, all surface shader nodes are returned. + + :param materialNode: The node to examine. + :param nodeType: The shader node type to return. Defaults to the surface shader type. + :param target: An optional target name, which will be used to filter the returned nodes. +)docstring"); + + mod.def("getConnectedOutputs", &mx::getConnectedOutputs, + py::arg("node"), + R"docstring( + Return a list of all outputs connected to the given `node`'s inputs. +)docstring"); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyModule.cpp b/source/PyMaterialX/PyMaterialXCore/PyModule.cpp index 6185da225f..7029a87e9e 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyModule.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyModule.cpp @@ -26,7 +26,268 @@ void bindPyVariant(py::module& mod); PYBIND11_MODULE(PyMaterialXCore, mod) { - mod.doc() = "Core functionality of MaterialX"; + mod.doc() = R"docstring( + Core MaterialX elements and graph traversal. + + Library Version + --------------- + + .. autofunction:: getVersionIntegers + .. autofunction:: getVersionString + + Document Element Tree + --------------------- + + .. autofunction:: createDocument + .. autofunction:: getConnectedOutputs + .. autofunction:: prettyPrint + + Element Classes + --------------- + + **Class Hierarchy** + + * `Element` + * `TypedElement` + * `InterfaceElement` + * `GraphElement` + * `Document` + * `NodeGraph` + * `Implementation` + * `Node` + * `NodeDef` + * `Variant` + * `ValueElement` + * `PortElement` + * `Input` + * `Output` + * `GeomProp` + * `Property` + * `PropertyAssign` + * `Token` + * `AttributeDef` + * `GeomPropDef` + * `Member` + * `TargetDef` + * `GeomElement` + * `GeomInfo` + * `MaterialAssign` + * `PropertySetAssign` + * `Visibility` + * `Backdrop` + * `Collection` + * `CommentElement` + * `GenericElement` + * `Look` + * `LookGroup` + * `NewlineElement` + * `PropertySet` + * `TypeDef` + * `Unit` + * `UnitDef` + * `UnitTypeDef` + * `VariantSet` + * `VariantAssign` + * `Edge` + * `StringResolver` + + **Alphabetical Index** + + .. autosummary:: + :toctree: elements + + AttributeDef + Backdrop + Collection + CommentElement + Document + Edge + Element + GenericElement + GeomElement + GeomInfo + GeomProp + GeomPropDef + GraphElement + Implementation + Input + InterfaceElement + Look + LookGroup + MaterialAssign + Member + NewlineElement + Node + NodeDef + NodeGraph + Output + PortElement + Property + PropertyAssign + PropertySet + PropertySetAssign + StringResolver + TargetDef + Token + TypeDef + TypedElement + Unit + UnitDef + UnitTypeDef + ValueElement + Variant + VariantAssign + VariantSet + Visibility + + Value Classes + ------------- + + **Class Hierarchy** + + * `Value` + * `TypedValued` -- The class template for typed subclasses of `Value` + * `TypedValue_boolean` + * `TypedValue_booleanarray` + * `TypedValue_color3` + * `TypedValue_color4` + * `TypedValue_float` + * `TypedValue_floatarray` + * `TypedValue_integer` + * `TypedValue_integerarray` + * `TypedValue_matrix33` + * `TypedValue_matrix44` + * `TypedValue_string` + * `TypedValue_stringarray` + * `TypedValue_vector2` + * `TypedValue_vector3` + * `TypedValue_vector4` + * `MatrixBase` + * `Matrix33` + * `Matrix44` + * `VectorBase` + * `Vector2` + * `Vector3` + * `Vector4` + * `Color3` + * `Color4` + + **Alphabetical Index** + + .. autosummary:: + :toctree: value-classes + + Color3 + Color4 + Matrix33 + Matrix44 + MatrixBase + TypedValue_boolean + TypedValue_booleanarray + TypedValue_color3 + TypedValue_color4 + TypedValue_float + TypedValue_floatarray + TypedValue_integer + TypedValue_integerarray + TypedValue_matrix33 + TypedValue_matrix44 + TypedValue_string + TypedValue_stringarray + TypedValue_vector2 + TypedValue_vector3 + TypedValue_vector4 + Value + Vector2 + Vector3 + Vector4 + VectorBase + + Material Node Utilities + ----------------------- + + .. autofunction:: getGeometryBindings + .. autofunction:: getShaderNodes + + Iterator Classes + ---------------- + + .. autosummary:: + :toctree: iterators + + GraphIterator + InheritanceIterator + TreeIterator + + Predicate Classes + ----------------- + + .. autosummary:: + :toctree: predicates + + ElementPredicate + NodePredicate + + Unit Converter Classes + ---------------------- + + **Class Hierarchy** + + * `UnitConverter` + * `LinearUnitConverter` + * `UnitConverterRegistry` + + **Alphabetical Index** + + .. autosummary:: + :toctree: unit-converters + + LinearUnitConverter + UnitConverter + UnitConverterRegistry + + Exception Classes + ----------------- + + **Class Hierarchy** + + * `Exception` + * `ExceptionFoundCycle` + * `ExceptionOrphanedElement` + * `PyMaterialXFormat.ExceptionFileMissing` + * `PyMaterialXFormat.ExceptionParseError` + * `PyMaterialXRender.ExceptionRenderError` + + **Alphabetical Index** + + .. autosummary:: + :toctree: exceptions + + Exception + ExceptionFoundCycle + ExceptionOrphanedElement + + Name/Path Utilities + ------------------- + + .. autofunction:: createNamePath + .. autofunction:: createValidName + .. autofunction:: incrementName + .. autofunction:: isValidName + .. autofunction:: parentNamePath + .. autofunction:: splitNamePath + + String Utilities + ---------------- + + .. autofunction:: joinStrings + .. autofunction:: replaceSubstrings + .. autofunction:: splitString + .. autofunction:: stringEndsWith + .. autofunction:: stringStartsWith + .. autofunction:: geomStringsMatch + .. autofunction:: targetStringsMatch +)docstring"; bindPyElement(mod); bindPyTraversal(mod); diff --git a/source/PyMaterialX/PyMaterialXCore/PyNode.cpp b/source/PyMaterialX/PyMaterialXCore/PyNode.cpp index 55448dee80..e6bd1619ca 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyNode.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyNode.cpp @@ -12,7 +12,11 @@ namespace mx = MaterialX; void bindPyNode(py::module& mod) { - py::class_(mod, "NodePredicate"); + py::class_(mod, "NodePredicate") + .doc() = R"docstring( + Class representing a function that takes a `Node` and returns a `bool`, + to check whether some criteria has passed. +)docstring"; py::class_(mod, "Node") .def("setConnectedNode", &mx::Node::setConnectedNode) @@ -26,7 +30,15 @@ void bindPyNode(py::module& mod) .def("getDownstreamPorts", &mx::Node::getDownstreamPorts) .def("addInputFromNodeDef", &mx::Node::addInputFromNodeDef) .def("addInputsFromNodeDef", &mx::Node::addInputsFromNodeDef) - .def_readonly_static("CATEGORY", &mx::Node::CATEGORY); + .def_readonly_static("CATEGORY", &mx::Node::CATEGORY) + .doc() = R"docstring( + A node element within a `NodeGraph` or `Document`. + + A `Node` represents an instance of a `NodeDef` within a graph, and its `Input` + elements apply specific values and connections to that instance. + + :see: https://materialx.org/docs/api/class_node.html +)docstring"; py::class_(mod, "GraphElement") .def("addNode", &mx::GraphElement::addNode, @@ -49,7 +61,12 @@ void bindPyNode(py::module& mod) py::arg("target") = mx::EMPTY_STRING, py::arg("filter") = nullptr) .def("topologicalSort", &mx::GraphElement::topologicalSort) .def("addGeomNode", &mx::GraphElement::addGeomNode) - .def("asStringDot", &mx::GraphElement::asStringDot); + .def("asStringDot", &mx::GraphElement::asStringDot) + .doc() = R"docstring( + Base class for graph elements such as `NodeGraph` and `Document`. + + :see: https://materialx.org/docs/api/class_graph_element.html +)docstring"; py::class_(mod, "NodeGraph") .def("getMaterialOutputs", &mx::NodeGraph::getMaterialOutputs) @@ -60,7 +77,12 @@ void bindPyNode(py::module& mod) .def("removeInterfaceName", &mx::NodeGraph::removeInterfaceName) .def("modifyInterfaceName", &mx::NodeGraph::modifyInterfaceName) .def("getDownstreamPorts", &mx::NodeGraph::getDownstreamPorts) - .def_readonly_static("CATEGORY", &mx::NodeGraph::CATEGORY); + .def_readonly_static("CATEGORY", &mx::NodeGraph::CATEGORY) + .doc() = R"docstring( + Class representing a node graph element within a `Document`. + + :see: https://materialx.org/docs/api/class_node_graph.html +)docstring"; py::class_(mod, "Backdrop") .def("setContainsString", &mx::Backdrop::setContainsString) @@ -77,5 +99,11 @@ void bindPyNode(py::module& mod) .def_readonly_static("CATEGORY", &mx::Backdrop::CATEGORY) .def_readonly_static("CONTAINS_ATTRIBUTE", &mx::Backdrop::CONTAINS_ATTRIBUTE) .def_readonly_static("WIDTH_ATTRIBUTE", &mx::Backdrop::WIDTH_ATTRIBUTE) - .def_readonly_static("HEIGHT_ATTRIBUTE", &mx::Backdrop::HEIGHT_ATTRIBUTE); + .def_readonly_static("HEIGHT_ATTRIBUTE", &mx::Backdrop::HEIGHT_ATTRIBUTE) + .doc() = R"docstring( + Class representing a layout element used to contain, group, and document + nodes within a graph. + + :see: https://materialx.org/docs/api/class_backdrop.html +)docstring"; } diff --git a/source/PyMaterialX/PyMaterialXCore/PyProperty.cpp b/source/PyMaterialX/PyMaterialXCore/PyProperty.cpp index a9b213a19b..e9570b6d6c 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyProperty.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyProperty.cpp @@ -16,7 +16,12 @@ namespace mx = MaterialX; void bindPyProperty(py::module& mod) { py::class_(mod, "Property") - .def_readonly_static("CATEGORY", &mx::Property::CATEGORY); + .def_readonly_static("CATEGORY", &mx::Property::CATEGORY) + .doc() = R"docstring( + Class representing a property element within a `PropertySet`. + + :see: https://materialx.org/docs/api/class_property.html +)docstring"; py::class_(mod, "PropertyAssign") .def("setProperty", &mx::PropertyAssign::setProperty) @@ -30,7 +35,12 @@ void bindPyProperty(py::module& mod) .def("getCollectionString", &mx::PropertyAssign::getCollectionString) .def("setCollection", &mx::PropertyAssign::setCollection) .def("getCollection", &mx::PropertyAssign::getCollection) - .def_readonly_static("CATEGORY", &mx::PropertyAssign::CATEGORY); + .def_readonly_static("CATEGORY", &mx::PropertyAssign::CATEGORY) + .doc() = R"docstring( + Class representing a property assignment element within a `Look`. + + :see: https://materialx.org/docs/api/class_property_assign.html +)docstring"; py::class_(mod, "PropertySet") .def("addProperty", &mx::PropertySet::addProperty) @@ -52,7 +62,12 @@ void bindPyProperty(py::module& mod) BIND_PROPERTYSET_TYPE_INSTANCE(booleanarray, mx::BoolVec) BIND_PROPERTYSET_TYPE_INSTANCE(floatarray, mx::FloatVec) BIND_PROPERTYSET_TYPE_INSTANCE(stringarray, mx::StringVec) - .def_readonly_static("CATEGORY", &mx::Property::CATEGORY); + .def_readonly_static("CATEGORY", &mx::Property::CATEGORY) + .doc() = R"docstring( + Class representing a property set element within a `Document`. + + :see: https://materialx.org/docs/api/class_property_set.html +)docstring"; py::class_(mod, "PropertySetAssign") .def("setPropertySetString", &mx::PropertySetAssign::setPropertySetString) @@ -60,5 +75,10 @@ void bindPyProperty(py::module& mod) .def("getPropertySetString", &mx::PropertySetAssign::getPropertySetString) .def("setPropertySet", &mx::PropertySetAssign::setPropertySet) .def("getPropertySet", &mx::PropertySetAssign::getPropertySet) - .def_readonly_static("CATEGORY", &mx::PropertySetAssign::CATEGORY); + .def_readonly_static("CATEGORY", &mx::PropertySetAssign::CATEGORY) + .doc() = R"docstring( + Class representing a property set assignment element within a `Look`. + + :see: https://materialx.org/docs/api/class_property_set_assign.html +)docstring"; } diff --git a/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp b/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp index f83200b680..4654fc7ff4 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp @@ -18,7 +18,19 @@ void bindPyTraversal(py::module& mod) .def("getDownstreamElement", &mx::Edge::getDownstreamElement) .def("getConnectingElement", &mx::Edge::getConnectingElement) .def("getUpstreamElement", &mx::Edge::getUpstreamElement) - .def("getName", &mx::Edge::getName); + .def("getName", &mx::Edge::getName) + .doc() = R"docstring( + Class representing an edge between two connected `Element` objects, + returned during graph traversal. + + A valid `Edge` consists of a downstream element, an upstream element, and + optionally a connecting element that binds them. As an example, the edge + between two `Node` elements will contain a connecting element for the `Input` + of the downstream `Node`. + + :see: `Element.traverseGraph()` + :see: https://materialx.org/docs/api/class_edge.html +)docstring"; py::class_(mod, "TreeIterator") .def("getElement", &mx::TreeIterator::getElement) @@ -34,7 +46,13 @@ void bindPyTraversal(py::module& mod) if (++it == it.end()) throw py::stop_iteration(); return *it; - }); + }) + .doc() = R"docstring( + Class implementing an iterator representing the state of a tree traversal. + + :see: `Element.traverseTree()` + :see: https://materialx.org/docs/api/class_tree_iterator.html +)docstring"; py::class_(mod, "GraphIterator") .def("getDownstreamElement", &mx::GraphIterator::getDownstreamElement) @@ -54,7 +72,13 @@ void bindPyTraversal(py::module& mod) if (++it == it.end()) throw py::stop_iteration(); return *it; - }); + }) + .doc() = R"docstring( + Class implementing an iterator representing the state of an upstream graph traversal. + + :see: `Element.traverseGraph()` + :see: https://materialx.org/docs/api/class_graph_iterator.html +)docstring"; py::class_(mod, "InheritanceIterator") .def("__iter__", [](mx::InheritanceIterator& it) -> mx::InheritanceIterator& @@ -66,7 +90,16 @@ void bindPyTraversal(py::module& mod) if (++it == it.end()) throw py::stop_iteration(); return *it; - }); + }) + .doc() = R"docstring( + Class implementing an iterator representing the current state of an inheritance traversal. + + :see: `Element.traverseInheritance()` + :see: https://materialx.org/docs/api/class_inheritance_iterator.html +)docstring"; - py::register_exception(mod, "ExceptionFoundCycle"); + py::register_exception(mod, "ExceptionFoundCycle") + .doc() = R"docstring( + A type of exception that is raised when a traversal call encounters a cycle. +)docstring"; } diff --git a/source/PyMaterialX/PyMaterialXCore/PyTypes.cpp b/source/PyMaterialX/PyMaterialXCore/PyTypes.cpp index ce11951259..a16063451b 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyTypes.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyTypes.cpp @@ -72,37 +72,44 @@ using IndexPair = std::pair; void bindPyTypes(py::module& mod) { - py::class_(mod, "VectorBase"); - py::class_(mod, "MatrixBase"); + py::class_(mod, "VectorBase", + "Base class for vectors of scalar values."); + py::class_(mod, "MatrixBase", + "Base class for square matrices of scalar values."); py::class_(mod, "Vector2") BIND_VECTOR_SUBCLASS(mx::Vector2, 2) .def(py::init()) .def("cross", &mx::Vector2::cross) - .def("asTuple", [](const mx::Vector2& v) { return std::make_tuple(v[0], v[1]); }); + .def("asTuple", [](const mx::Vector2& v) { return std::make_tuple(v[0], v[1]); }) + .doc() = "A vector of two floating-point values."; py::class_(mod, "Vector3") BIND_VECTOR_SUBCLASS(mx::Vector3, 3) .def(py::init()) .def("cross", &mx::Vector3::cross) - .def("asTuple", [](const mx::Vector3& v) { return std::make_tuple(v[0], v[1], v[2]); }); + .def("asTuple", [](const mx::Vector3& v) { return std::make_tuple(v[0], v[1], v[2]); }) + .doc() = "A vector of three floating-point values."; py::class_(mod, "Vector4") BIND_VECTOR_SUBCLASS(mx::Vector4, 4) .def(py::init()) - .def("asTuple", [](const mx::Vector4& v) { return std::make_tuple(v[0], v[1], v[2], v[3]); }); + .def("asTuple", [](const mx::Vector4& v) { return std::make_tuple(v[0], v[1], v[2], v[3]); }) + .doc() = "A vector of four floating-point values."; py::class_(mod, "Color3") BIND_VECTOR_SUBCLASS(mx::Color3, 3) .def(py::init()) .def("linearToSrgb", &mx::Color3::linearToSrgb) .def("srgbToLinear", &mx::Color3::srgbToLinear) - .def("asTuple", [](const mx::Color3& v) { return std::make_tuple(v[0], v[1], v[2]); }); + .def("asTuple", [](const mx::Color3& v) { return std::make_tuple(v[0], v[1], v[2]); }) + .doc() = "A three-component RGB color value."; py::class_(mod, "Color4") BIND_VECTOR_SUBCLASS(mx::Color4, 4) .def(py::init()) - .def("asTuple", [](const mx::Color4& v) { return std::make_tuple(v[0], v[1], v[2], v[3]); }); + .def("asTuple", [](const mx::Color4& v) { return std::make_tuple(v[0], v[1], v[2], v[3]); }) + .doc() = "A four-component RGBA color value"; py::class_(mod, "Matrix33") BIND_MATRIX_SUBCLASS(mx::Matrix33, 3) @@ -114,7 +121,15 @@ void bindPyTypes(py::module& mod) .def("transformVector", &mx::Matrix33::transformVector) .def("transformNormal", &mx::Matrix33::transformNormal) .def_static("createRotation", &mx::Matrix33::createRotation) - .def_readonly_static("IDENTITY", &mx::Matrix33::IDENTITY); + .def_readonly_static("IDENTITY", &mx::Matrix33::IDENTITY) + .doc() = R"docstring( + A 3x3 matrix of floating-point values. + + Vector transformation methods follow the row-vector convention, + with matrix-vector multiplication computed as `v' = vM`. + + :see: https://materialx.org/docs/api/class_matrix33.html +)docstring"; py::class_(mod, "Matrix44") BIND_MATRIX_SUBCLASS(mx::Matrix44, 4) @@ -129,7 +144,15 @@ void bindPyTypes(py::module& mod) .def_static("createRotationX", &mx::Matrix44::createRotationX) .def_static("createRotationY", &mx::Matrix44::createRotationY) .def_static("createRotationZ", &mx::Matrix44::createRotationZ) - .def_readonly_static("IDENTITY", &mx::Matrix44::IDENTITY); + .def_readonly_static("IDENTITY", &mx::Matrix44::IDENTITY) + .doc() = R"docstring( + A 4x4 matrix of floating-point values. + + Vector transformation methods follow the row-vector convention, + with matrix-vector multiplication computed as `v' = vM`. + + :see: https://materialx.org/docs/api/class_matrix44.html +)docstring"; mod.attr("DEFAULT_TYPE_STRING") = mx::DEFAULT_TYPE_STRING; mod.attr("FILENAME_TYPE_STRING") = mx::FILENAME_TYPE_STRING; diff --git a/source/PyMaterialX/PyMaterialXCore/PyUnitConverter.cpp b/source/PyMaterialX/PyMaterialXCore/PyUnitConverter.cpp index bf3bcbf0f9..5262b87d90 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyUnitConverter.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyUnitConverter.cpp @@ -72,7 +72,14 @@ void bindPyUnitConverters(py::module& mod) .def("convert", (mx::Vector3 (mx::UnitConverter::*)(const mx::Vector3&, const std::string&, const std::string&)const) &mx::UnitConverter::convert) .def("convert", (mx::Vector4 (mx::UnitConverter::*)(const mx::Vector4&, const std::string&, const std::string&)const) &mx::UnitConverter::convert) .def("getUnitAsInteger", &mx::UnitConverter::getUnitAsInteger) - .def("getUnitFromInteger", &mx::UnitConverter::getUnitFromInteger); + .def("getUnitFromInteger", &mx::UnitConverter::getUnitFromInteger) + .doc() = R"docstring( + Abstract base class for unit converters. + + Each unit converter instance is responsible for a single unit type. + + :see: https://materialx.org/docs/api/class_unit_converter.html +)docstring"; py::class_(mod, "LinearUnitConverter") .def_static("create", &mx::LinearUnitConverter::create) @@ -82,12 +89,23 @@ void bindPyUnitConverters(py::module& mod) .def("convert", (mx::Vector3 (mx::LinearUnitConverter::*)(const mx::Vector3&, const std::string&, const std::string&)const) &mx::LinearUnitConverter::convert) .def("convert", (mx::Vector4 (mx::LinearUnitConverter::*)(const mx::Vector4&, const std::string&, const std::string&)const) &mx::LinearUnitConverter::convert) .def("getUnitAsInteger", &mx::LinearUnitConverter::getUnitAsInteger) - .def("getUnitFromInteger", &mx::LinearUnitConverter::getUnitFromInteger); + .def("getUnitFromInteger", &mx::LinearUnitConverter::getUnitFromInteger) + .doc() = R"docstring( + A `UnitConverter` class for linear units that require only a scalar + multiplication. + + :see: https://materialx.org/docs/api/class_linear_unit_converter.html +)docstring"; py::class_(mod, "UnitConverterRegistry") .def_static("create", &mx::UnitConverterRegistry::create) .def("addUnitConverter", &mx::UnitConverterRegistry::addUnitConverter) .def("removeUnitConverter", &mx::UnitConverterRegistry::removeUnitConverter) .def("getUnitConverter", &mx::UnitConverterRegistry::getUnitConverter) - .def("clearUnitConverters", &mx::UnitConverterRegistry::clearUnitConverters); + .def("clearUnitConverters", &mx::UnitConverterRegistry::clearUnitConverters) + .doc() = R"docstring( + Class implementing a registry for `UnitConverter` objects. + + :see: https://materialx.org/docs/api/class_unit_converter_registry.html +)docstring"; } diff --git a/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp b/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp index 826c5adf84..8367b28275 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp @@ -15,17 +15,132 @@ namespace mx = MaterialX; void bindPyUtil(py::module& mod) { - mod.def("getVersionString", &mx::getVersionString); - mod.def("getVersionIntegers", &mx::getVersionIntegers); - mod.def("createValidName", &mx::createValidName, py::arg("name"), py::arg("replaceChar") = '_'); - mod.def("isValidName", &mx::isValidName); - mod.def("incrementName", &mx::incrementName); - mod.def("splitString", &mx::splitString); - mod.def("joinStrings", &mx::joinStrings); - mod.def("replaceSubstrings", &mx::replaceSubstrings); - mod.def("stringStartsWith", &mx::stringStartsWith); - mod.def("stringEndsWith", &mx::stringEndsWith); - mod.def("splitNamePath", &mx::splitNamePath); - mod.def("createNamePath", &mx::createNamePath); - mod.def("parentNamePath", &mx::parentNamePath); + mod.def("getVersionString", &mx::getVersionString, + R"docstring( + Return the version of the MaterialX library as a string. + + >>> PyMaterialXCore.getVersionString() + '1.38.9' +)docstring"); + + mod.def("getVersionIntegers", &mx::getVersionIntegers, + R"docstring( + Return the major, minor, and build versions of the MaterialX library as an + integer tuple. + + >>> PyMaterialXCore.getVersionIntegers() + (1, 38, 9) +)docstring"); + + mod.def("createValidName", &mx::createValidName, + py::arg("name"), py::arg("replaceChar") = '_', + R"docstring( + Create a valid MaterialX name from the given `name`, + replacing invalid characters with the given `replaceChar`. + + >>> PyMaterialXCore.createValidName('left arm #123') + 'left_arm__123' +)docstring"); + + mod.def("isValidName", &mx::isValidName, + py::arg("name"), + R"docstring( + Return `True` if the given `name` is a valid MaterialX name, + otherwise False. + + >>> PyMaterialXCore.isValidName('left arm #123') + False +)docstring"); + + mod.def("incrementName", &mx::incrementName, + py::arg("name"), + R"docstring( + Increment the numeric suffix of the given `name`. + + >>> PyMaterialXCore.incrementName('left arm #123') + 'left arm #124' +)docstring"); + + mod.def("splitString", &mx::splitString, + py::arg("string"), py::arg("sep"), + R"docstring( + Split the given `string` into a list of substrings using the + given set of separator characters. + + >>> PyMaterialXCore.splitString('MaterialX', 'aeiou') + ['M', 't', 'r', 'lX'] +)docstring"); + + mod.def("joinStrings", &mx::joinStrings, + py::arg("strings"), py::arg("sep"), + R"docstring( + Join a list of substrings into a single string, placing the + given separator between each substring. + + >>> PyMaterialXCore.joinStrings(['M', 'teri', 'lX'], 'a') + 'MaterialX' +)docstring"); + + mod.def("replaceSubstrings", &mx::replaceSubstrings, + py::arg("string"), py::arg("substitutions"), + R"docstring( + Apply the given substring `substitutions` to the input `string`. + + >>> PyMaterialXCore.replaceSubstrings( + ... '/bob/alice/guido', + ... {'bob': 'b', 'alice': 'a', 'guido': 'g'} + ... ) + '/b/a/g' +)docstring"); + + mod.def("stringStartsWith", &mx::stringStartsWith, + py::arg("string"), py::arg("prefix"), + R"docstring( + Return `True` if the given `string` starts with the given `prefix`, + otherwise `False`. + + >>> PyMaterialXCore.stringStartsWith('turbulence3d', 'turbulence') + True + >>> PyMaterialXCore.stringStartsWith('turbulence3d', 'Turbulence') + False +)docstring"); + + mod.def("stringEndsWith", &mx::stringEndsWith, + py::arg("string"), py::arg("suffix"), + R"docstring( + Return `True` if the given `string` ends with the given `suffix`, + otherwise `False`. + + >>> PyMaterialXCore.stringEndsWith('turbulence3d', '3d') + True + >>> PyMaterialXCore.stringEndsWith('turbulence3d', '3D') + False +)docstring"); + + mod.def("splitNamePath", &mx::splitNamePath, + py::arg("namePath"), + R"docstring( + Split the given `namePath` into a list of names. + + >>> PyMaterialXCore.splitNamePath('/robot2/right_arm') + ['robot2', 'right_arm'] +)docstring"); + + mod.def("createNamePath", &mx::createNamePath, + py::arg("names"), + R"docstring( + Create a name path from the given list of `names`. + + >>> PyMaterialXCore.createNamePath(['robot2', 'right_arm']) + 'robot2/right_arm' +)docstring"); + + mod.def("parentNamePath", &mx::parentNamePath, + py::arg("namePath"), + R"docstring( + Given a `namePath`, return the parent name path. + + >>> PyMaterialXCore.parentNamePath('/robot2/right_arm') + 'robot2' +)docstring"); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyValue.cpp b/source/PyMaterialX/PyMaterialXCore/PyValue.cpp index 501f9d5f04..4c5a1fe45c 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyValue.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyValue.cpp @@ -12,7 +12,8 @@ py::class_, std::shared_ptr< mx::TypedValue >, mx::Value>(m .def("getData", &mx::TypedValue::getData) \ .def("getValueString", &mx::TypedValue::getValueString) \ .def_static("createValue", &mx::Value::createValue) \ - .def_readonly_static("TYPE", &mx::TypedValue::TYPE); + .def_readonly_static("TYPE", &mx::TypedValue::TYPE) \ + .doc() = "A `TypedValue` of type `" #T "`."; namespace py = pybind11; namespace mx = MaterialX; @@ -22,21 +23,28 @@ void bindPyValue(py::module& mod) py::class_(mod, "Value") .def("getValueString", &mx::Value::getValueString) .def("getTypeString", &mx::Value::getTypeString) - .def_static("createValueFromStrings", &mx::Value::createValueFromStrings); + .def_static("createValueFromStrings", &mx::Value::createValueFromStrings) + .doc() = R"docstring( + Class representing a generic, discriminated value, whose type may be + queried dynamically. + + :see: https://materialx.org/docs/api/class_value.html +)docstring"; BIND_TYPE_INSTANCE(integer, int) BIND_TYPE_INSTANCE(boolean, bool) BIND_TYPE_INSTANCE(float, float) - BIND_TYPE_INSTANCE(color3, mx::Color3) - BIND_TYPE_INSTANCE(color4, mx::Color4) - BIND_TYPE_INSTANCE(vector2, mx::Vector2) - BIND_TYPE_INSTANCE(vector3, mx::Vector3) - BIND_TYPE_INSTANCE(vector4, mx::Vector4) - BIND_TYPE_INSTANCE(matrix33, mx::Matrix33) - BIND_TYPE_INSTANCE(matrix44, mx::Matrix44) + // Use the full `MaterialX::` prefix here so that it appears in the docstrings + BIND_TYPE_INSTANCE(color3, MaterialX::Color3) + BIND_TYPE_INSTANCE(color4, MaterialX::Color4) + BIND_TYPE_INSTANCE(vector2, MaterialX::Vector2) + BIND_TYPE_INSTANCE(vector3, MaterialX::Vector3) + BIND_TYPE_INSTANCE(vector4, MaterialX::Vector4) + BIND_TYPE_INSTANCE(matrix33, MaterialX::Matrix33) + BIND_TYPE_INSTANCE(matrix44, MaterialX::Matrix44) BIND_TYPE_INSTANCE(string, std::string) - BIND_TYPE_INSTANCE(integerarray, mx::IntVec) - BIND_TYPE_INSTANCE(booleanarray, mx::BoolVec) - BIND_TYPE_INSTANCE(floatarray, mx::FloatVec) - BIND_TYPE_INSTANCE(stringarray, mx::StringVec) + BIND_TYPE_INSTANCE(integerarray, MaterialX::IntVec) + BIND_TYPE_INSTANCE(booleanarray, MaterialX::BoolVec) + BIND_TYPE_INSTANCE(floatarray, MaterialX::FloatVec) + BIND_TYPE_INSTANCE(stringarray, MaterialX::StringVec) } diff --git a/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp b/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp index 13c7fa572c..956b83663e 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp @@ -13,7 +13,12 @@ namespace mx = MaterialX; void bindPyVariant(py::module& mod) { py::class_(mod, "Variant") - .def_readonly_static("CATEGORY", &mx::Variant::CATEGORY); + .def_readonly_static("CATEGORY", &mx::Variant::CATEGORY) + .doc() = R"docstring( + Class representing a variant element within a `VariantSet`. + + :see: https://materialx.org/docs/api/class_variant.html +)docstring"; py::class_(mod, "VariantSet") .def("addVariant", &mx::VariantSet::addVariant, @@ -21,7 +26,12 @@ void bindPyVariant(py::module& mod) .def("getVariant", &mx::VariantSet::getVariant) .def("getVariants", &mx::VariantSet::getVariants) .def("removeVariant", &mx::VariantSet::removeVariant) - .def_readonly_static("CATEGORY", &mx::VariantSet::CATEGORY); + .def_readonly_static("CATEGORY", &mx::VariantSet::CATEGORY) + .doc() = R"docstring( + Class representing a variant set element within a `Document`. + + :see: https://materialx.org/docs/api/class_variant_set.html +)docstring"; py::class_(mod, "VariantAssign") .def("setVariantSetString", &mx::VariantAssign::setVariantSetString) @@ -30,5 +40,10 @@ void bindPyVariant(py::module& mod) .def("setVariantString", &mx::VariantAssign::setVariantString) .def("hasVariantString", &mx::VariantAssign::hasVariantString) .def("getVariantString", &mx::VariantAssign::getVariantString) - .def_readonly_static("CATEGORY", &mx::VariantAssign::CATEGORY); + .def_readonly_static("CATEGORY", &mx::VariantAssign::CATEGORY) + .doc() = R"docstring( + Class representing a variant assignment element within a `Look`. + + :see: https://materialx.org/docs/api/class_variant_assign.html +)docstring"; }