diff --git a/README.md b/README.md index fad8482..8d6be62 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,34 @@ You should see RTX Remix nodes: -![RTX Remix](images/remix_nodes.png "RTX Remix") \ No newline at end of file +![RTX Remix](images/remix_nodes.png "RTX Remix") + +## Nodes +### Textures +- **RTX Remix Get Textures**: Read the textures matching provided criteria from the currently open project +- **RTX Remix Texture Type To USD Attribute**: Use this node to get the proper texture attribute on the same asset but for a different texture type +- **RTX Remix Set Texture**: Set the texture path on an asset +- **RTX Remix Texture Type**: Select from a list of supported texture types. +- **RTX Remix Texture Types**: Select multiple texture types from a list of supported texture types. + +### Ingestion +- **RTX Remix Ingest Texture**: Ingest an image as a texture and save it to disk + +### Common +- **RTX Remix Rest API Details**: Provide the port information to connect to the RTX Remix Toolkit +- **RTX Remix String Constant**: Declare a string constant +- **RTX Remix Start Context**: Use this node to begin a graph, then pass context along to determine execution order. +- **RTX Remix End Context**: Put this node at the end of your graph to evaluate prior nodes + +### Layers +- **RTX Remix Define Layer ID**: Helper node to define a layer path relative to project or another layer +- **RTX Remix Create Layer**: Create or Insert a sublayer in the current stage +- **RTX Remix Get Layers**: Query layer ids from the currently open project +- **RTX Remix Remove Layer**: Remove a layer from the project +- **RTX Remix Save Layer**: Save a project layer +- **RTX Remix Mute Layer**: Mute or unmute a project layer +- **RTX Remix Set Edit Target**: Designate the edit target on the open project to receive modifications +- **RTX Remix Get Edit Target**: Get the edit target from the currently open project +- **RTX Remix Layer Types**: Select multiple layer types from a list of supported layer types. +- **RTX Remix Layer Type**: Select from a list of supported layer types. + diff --git a/nodes/__init__.py b/nodes/__init__.py index d733ebb..5621457 100644 --- a/nodes/__init__.py +++ b/nodes/__init__.py @@ -72,7 +72,7 @@ NODE_DISPLAY_NAME_MAPPINGS = { "RTXRemixStringConstant": "RTX Remix String Constant", "RTXRemixTexturesType": "RTX Remix Texture Type", "RTXRemixTexturesTypes": "RTX Remix Texture Types", - "RTXRemixDefineLayerId": "RTX Remix Define Layer", + "RTXRemixDefineLayerId": "RTX Remix Define Layer ID", "RTXRemixCreateLayer": "RTX Remix Create Layer", "RTXRemixGetLayers": "RTX Remix Get Layers", "RTXRemixRemoveLayer": "RTX Remix Remove Layer", diff --git a/nodes/common.py b/nodes/common.py index f14cf78..a876ca6 100644 --- a/nodes/common.py +++ b/nodes/common.py @@ -92,6 +92,7 @@ def add_context_input_and_output(cls): class RestAPIDetails: + """Provide the port information to connect to the RTX Remix Toolkit""" @classmethod def INPUT_TYPES(cls): # noqa N802 @@ -124,6 +125,7 @@ class RestAPIDetails: @add_context_outputs class StartContext: + """Use this node to begin a graph, then pass context along to determine execution order.""" @classmethod def INPUT_TYPES(cls): # noqa N802 @@ -143,6 +145,7 @@ class StartContext: class EndContext: + """Put this node at the end of your graph to evaluate prior nodes""" @classmethod def INPUT_TYPES(cls): # noqa N802 @@ -162,6 +165,8 @@ class EndContext: class StringConstant: + """Declare a string constant""" + @classmethod def INPUT_TYPES(cls): # noqa N802 return { diff --git a/nodes/ingestion.py b/nodes/ingestion.py index 4d8170b..9ab58fe 100644 --- a/nodes/ingestion.py +++ b/nodes/ingestion.py @@ -34,6 +34,7 @@ _file_name = pathlib.Path(__file__).stem @add_context_input_and_output class IngestTexture: + """Ingest an image as a texture and save it to disk""" @classmethod def INPUT_TYPES(cls): # noqa N802 diff --git a/nodes/layers.py b/nodes/layers.py index ac83c9e..b0a1657 100644 --- a/nodes/layers.py +++ b/nodes/layers.py @@ -119,6 +119,7 @@ class DefineLayerId: @add_context_input_and_output class CreateLayer: + """Create or Insert a sublayer in the current stage""" @classmethod def INPUT_TYPES(cls): # noqa N802 @@ -198,6 +199,7 @@ class CreateLayer: @add_context_input_and_output class LayerType: + """Select from a list of supported layer types.""" @classmethod def INPUT_TYPES(cls): # noqa N802 @@ -223,6 +225,7 @@ class LayerType: @add_context_input_and_output class LayerTypes: + """Select multiple layer types from a list of supported layer types.""" @classmethod def INPUT_TYPES(cls): # noqa N802 @@ -255,6 +258,7 @@ class LayerTypes: @add_context_input_and_output class GetLayers: + """Query layer ids from the currently open project""" @classmethod def INPUT_TYPES(cls): # noqa N802 @@ -385,6 +389,7 @@ class _LayerOp: @add_context_input_and_output class MuteLayer(_LayerOp): + """Mute or unmute a project layer""" @classmethod def INPUT_TYPES(cls): @@ -416,6 +421,7 @@ class MuteLayer(_LayerOp): @add_context_input_and_output class RemoveLayer(_LayerOp): + """Remove a layer from the project""" @classmethod def INPUT_TYPES(cls): # noqa N802 @@ -447,6 +453,7 @@ class RemoveLayer(_LayerOp): @add_context_input_and_output class SaveLayer(_LayerOp): + """Save a project layer""" def execute(self, layer_id: str) -> tuple[str]: address, port = self.context @@ -460,6 +467,7 @@ class SaveLayer(_LayerOp): @add_context_input_and_output class GetEditTarget: + """Get the edit target from the currently open project""" @classmethod def INPUT_TYPES(cls): # noqa N802 @@ -493,6 +501,7 @@ class GetEditTarget: @add_context_input_and_output class SetEditTarget(_LayerOp): + """Designate the edit target on the open project to receive modifications""" def execute(self, layer_id: str) -> tuple[str]: address, port = self.context diff --git a/nodes/textures.py b/nodes/textures.py index cbd9a4b..f0c31ca 100644 --- a/nodes/textures.py +++ b/nodes/textures.py @@ -61,6 +61,7 @@ def validate_texture_types(texture_types: list[str], address: str, port: str): @add_context_input_and_output class GetTextures: + """Read the textures matching provided criteria from the currently open project""" @classmethod def INPUT_TYPES(cls): # noqa N802 @@ -185,6 +186,7 @@ class GetTextures: @add_context_input_and_output class TexturesTypes: + """Select multiple texture types from a list of supported texture types.""" @classmethod def INPUT_TYPES(cls): # noqa N802 @@ -218,6 +220,7 @@ class TexturesTypes: @add_context_input_and_output class TexturesType: + """Select from a list of supported texture types.""" @classmethod def INPUT_TYPES(cls): # noqa N802 @@ -242,6 +245,7 @@ class TexturesType: @add_context_input_and_output class SetTexture: + """Set the texture path on an asset""" @classmethod def INPUT_TYPES(cls): # noqa N802 @@ -284,6 +288,7 @@ class SetTexture: @add_context_input_and_output class TextureTypeToUSDAttribute: + """Use this node to get the proper texture attribute on the same asset but for a different texture type""" @classmethod def INPUT_TYPES(cls): # noqa N802 diff --git a/repo_tools.toml b/repo_tools.toml new file mode 100644 index 0000000..80170e2 --- /dev/null +++ b/repo_tools.toml @@ -0,0 +1,14 @@ +[repo_class_docs] +# Tool name +command = "class_docs" +# [python module name or path]:[function name] +entry_point = "${root}/tools/repoman/class_docs.py:setup_repo_tool" +# Python install to use +python_install = "${root}/../../../python_embeded/python.exe" +# Root module to import +module_name = "nodes" +module = "${root}/nodes/__init__.py" +# README to modify +file_path = "${root}/README.md" +# Header of section where change is required +section_header = "## Nodes" \ No newline at end of file diff --git a/tools/repoman/class_docs.py b/tools/repoman/class_docs.py new file mode 100644 index 0000000..eb7e190 --- /dev/null +++ b/tools/repoman/class_docs.py @@ -0,0 +1,115 @@ +""" +* SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +""" +import os +import re +import subprocess +import sys +import textwrap + + +def update_comfyui_rtx_remix_readme(module, readme_path, section_header): + """Generate docs for node types""" + section_header_style = section_header.split(' ')[0] + header_num = section_header_style.count('#') + + output = [] + nodes_by_module = {} + for name, node in module.NODE_CLASS_MAPPINGS.items(): + nodes_by_module.setdefault(node.__module__, []).append((name, node)) + + for node_module, nodes in nodes_by_module.items(): + output.append(f"{'#' * (header_num + 1)} {node_module.split('.')[-1].capitalize()}\n") + for node_info in nodes: + name, node_class = node_info + display_name = module.NODE_DISPLAY_NAME_MAPPINGS[name] + if node_class.__doc__: + # first line only + doc = node_class.__doc__.strip("\n") + line_break_index = doc.find("\n") + if line_break_index > 0: + doc = doc[:] + output.append(f"- **{display_name}**: {doc}\n") + else: + output.append(f"**{display_name}**\n") + + output.append("\n") + + replace_section(readme_path, section_header, output) + + +def replace_section(readme_path, section_header, contents): + """Replace a section under a specific markdown header with new contents""" + section_header_style = section_header.split(' ')[0] + with open(readme_path, 'r') as f: + data = f.readlines() + + new = [] + start = -1 + end = None + found = False + for i, line in enumerate(data): + # search for the first occurence of the special section header string + if not found: + if line.strip() == section_header: + found = True + start = i + 1 + # search for the next header of the same magnitude + elif re.match(f"{section_header_style}[^#].*", line): + end = i + break + + final = data[:start] + contents + if end: + final += data[end:] + with open(readme_path, 'w') as f: + f.writelines(final) + + +def update_readme(module, readme_path, section_header): + """Generic `class_docs` entry point. Add repo-specific implementation here!""" + update_comfyui_rtx_remix_readme(module, readme_path, section_header) + + +def setup_repo_tool(parser, _): + parser.prog = "class_docs" + parser.description = "Generate summary documentation for a bunch of classes" + + def run_repo_tool(options, config): + import subprocess + settings = config["repo_class_docs"] + + # Some projects will require using their own python in order to be able to import and inspect + # classes so we use a subprocess + python_code = textwrap.dedent(f"""\ + import sys + sys.path.append("{os.path.dirname(__file__)}") + import class_docs + import importlib.util + module_name = "{settings["module_name"]}" + spec = importlib.util.spec_from_file_location(module_name, "{settings["module"]}") + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + spec.loader.exec_module(module) + + class_docs.update_readme(module, "{settings["file_path"]}", "{settings["section_header"]}") + """) + python_cmd = settings["python_install"] + + if subprocess.call([python_cmd, "-c", python_code]) == 0: + print("Success!") + + return run_repo_tool