diff --git a/blenderproc/python/loader/CCMaterialLoader.py b/blenderproc/python/loader/CCMaterialLoader.py index 0779228e1..7e9ae4564 100644 --- a/blenderproc/python/loader/CCMaterialLoader.py +++ b/blenderproc/python/loader/CCMaterialLoader.py @@ -29,7 +29,7 @@ def load_ccmaterials(folder_path: str = "resources/cctextures", used_assets: lis :param add_custom_properties: A dictionary of materials and the respective properties. :param use_all_materials: If this is false only a selection of probably useful textures is used. This excludes \ some see through texture and non tileable texture. - :return a list of all loaded materials, if preload is active these materials do not contain any textures yet + :return: a list of all loaded materials, if preload is active these materials do not contain any textures yet and have to be filled before rendering (by calling this function again, no need to save the prior returned list) """ diff --git a/blenderproc/python/loader/HavenEnvironmentLoader.py b/blenderproc/python/loader/HavenEnvironmentLoader.py index 14a7b8bef..4a15d5dd1 100644 --- a/blenderproc/python/loader/HavenEnvironmentLoader.py +++ b/blenderproc/python/loader/HavenEnvironmentLoader.py @@ -58,7 +58,7 @@ def get_random_world_background_hdr_img_path_from_haven(data_path: str) -> str: """ Sets the world background to a random .hdr file from the given directory. :param data_path: A path pointing to a directory containing .hdr files. - :return The path to a random selected path + :return: The path to a random selected path """ if os.path.exists(data_path): diff --git a/blenderproc/python/loader/HavenMaterialLoader.py b/blenderproc/python/loader/HavenMaterialLoader.py index 78ad8a0b6..05b3d593f 100644 --- a/blenderproc/python/loader/HavenMaterialLoader.py +++ b/blenderproc/python/loader/HavenMaterialLoader.py @@ -1,16 +1,17 @@ """ Loading the haven materials, which then can be assigned to objects. -Haven textures are stored as a directory with several texture maps .jpgs e.g: -textures -|- rock_01 -| |- rock_01_ao_1k.jpg -| |- rock_01_diff_1k.jpg -| |- rock_01_disp_1k.jpg -| |- rock_01_nor_gl_1k.jpg -| |- rock_01_rough_1k.jpg -|- rock_02 -| ... +Haven textures are stored as a directory with several texture maps .jpgs e.g:: + + textures + |- rock_01 + | |- rock_01_ao_1k.jpg + | |- rock_01_diff_1k.jpg + | |- rock_01_disp_1k.jpg + | |- rock_01_nor_gl_1k.jpg + | |- rock_01_rough_1k.jpg + |- rock_02 + | ... The general naming pattern of the texture maps is: {name}_{type}_{resolution}.jpg However, the type abbreviation is not consistent for all textures. E.g. for some textures the base color map is @@ -111,7 +112,7 @@ def load_haven_mat(folder_path: Union[str, Path] = "resources/haven", used_asset :param return_random_element: If this is True only a single Material is loaded and returned, if you want to sample many materials load them all with the preload option, use them and then fill the used empty materials instead of calling this function multiple times. - :return a list of all loaded materials, if preload is active these materials do not contain any textures yet + :return: a list of all loaded materials, if preload is active these materials do not contain any textures yet and have to be filled before rendering (by calling this function again, there is no need to save the prior returned list) or if return_random_element is True only a single Material is returned """ diff --git a/blenderproc/python/object/ObjectReplacer.py b/blenderproc/python/object/ObjectReplacer.py index 8c89652b0..db4dd0402 100644 --- a/blenderproc/python/object/ObjectReplacer.py +++ b/blenderproc/python/object/ObjectReplacer.py @@ -91,7 +91,7 @@ def bb_ratio(bb1: np.ndarray, bb2: np.ndarray) -> list: :param bb1: bounding box 1. Type: float multi-dimensional array of 8 * 3. :param bb2: bounding box 2. Type: float multi-dimensional array of 8 * 3. - returns the ratio between each side of the bounding box. Type: a list of floats. + :return: the ratio between each side of the bounding box. Type: a list of floats. """ ratio_a = (bb1[0, 0] - bb1[4, 0]) / (bb2[0, 0] - bb2[4, 0]) ratio_b = (bb1[0, 1] - bb1[3, 1]) / (bb2[0, 1] - bb2[3, 1]) diff --git a/blenderproc/python/utility/BlenderUtility.py b/blenderproc/python/utility/BlenderUtility.py index b3ec82854..69df5cf69 100644 --- a/blenderproc/python/utility/BlenderUtility.py +++ b/blenderproc/python/utility/BlenderUtility.py @@ -349,7 +349,7 @@ def add_nodes_to_group(nodes: bpy.types.Node, group_name: str) -> bpy.types.Shad :param nodes: Nodes, which should be used :param group_name: Name of the group - :return bpy.types.ShaderNodeTree: the group which can be used inside of a bpy.types.ShaderNodeGroup + :return: bpy.types.ShaderNodeTree: the group which can be used inside of a bpy.types.ShaderNodeGroup """ # create new node group group = bpy.data.node_groups.new(name=group_name, type="ShaderNodeTree") diff --git a/blenderproc/python/writer/BopWriterUtility.py b/blenderproc/python/writer/BopWriterUtility.py index b6ec492cb..54788447f 100644 --- a/blenderproc/python/writer/BopWriterUtility.py +++ b/blenderproc/python/writer/BopWriterUtility.py @@ -299,6 +299,7 @@ def write_camera(camera_path: str, depth_scale: float = 1.0): def get_frame_gt(dataset_objects: List[bpy.types.Mesh], unit_scaling: float, ignore_dist_thres: float, destination_frame: Optional[List[str]] = None): """ Returns GT pose annotations between active camera and objects. + :param dataset_objects: Save annotations for these objects. :param unit_scaling: 1000. for outputting poses in mm :param ignore_dist_thres: Distance between camera and object after which object is ignored. @@ -350,6 +351,7 @@ def get_frame_gt(dataset_objects: List[bpy.types.Mesh], unit_scaling: float, ign def get_frame_camera(save_world2cam: bool, depth_scale: float = 1.0, unit_scaling: float = 1000., destination_frame: Optional[List[str]] = None): """ Returns camera parameters for the active camera. + :param save_world2cam: If true, camera to world transformations "cam_R_w2c", "cam_t_w2c" are saved in scene_camera.json :param depth_scale: Multiply the uint16 output depth image with this factor to get depth in mm. diff --git a/docs/Makefile b/docs/Makefile index 182c977ef..f0bcaa63f 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,11 +1,10 @@ # Minimal makefile for Sphinx documentation # -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXBUILD = /home/${USER}/blender/blender-2.93.0-linux-x64/blender --background --python cli.py -- -SPHINXPROJ = BlenderProc +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= /home_local/${USER}/blender/blender-3.3.1-linux-x64/blender --background --python run.py -- SOURCEDIR = source BUILDDIR = build diff --git a/docs/Readme.md b/docs/Readme.md index 94ac4f504..31440349c 100644 --- a/docs/Readme.md +++ b/docs/Readme.md @@ -1,13 +1,17 @@ # Documentation -The scripts assume that blender is installed at `/home_local/${USER}/blender/blender-2.92.0-linux64`. +The scripts assume that blender is installed at `/home_local/$USER/blender/blender-3.3.1-linux-x64/blender`. If this is not the case please change it in the [Makefile](Makefile). -First run `config_for_pip_install.yaml` to make sure to install all packages used in our code: +First make sure to install all necessary packages: ```bash -python cli.py docs/config_for_pip_install.yaml +blenderproc pip install sphinx pygments packaging sphinx-autodoc-typehints sphinx-autodoc-typehints m2r2 sphinx-rtd-theme docutils +``` + +```bash +blenderproc pip install git+https://github.com/abahnasy/smplx git+https://github.com/abahnasy/human_body_prior git+https://github.com/wboerdijk/urdfpy.git git+https://github.com/thodan/bop_toolkit PyOpenGL==3.1.0 ``` Now copy markdowns files from examples and main directory: diff --git a/docs/change_csv_tables_docu.py b/docs/change_csv_tables_docu.py deleted file mode 100644 index 439d4fc79..000000000 --- a/docs/change_csv_tables_docu.py +++ /dev/null @@ -1,326 +0,0 @@ - -import os -import argparse - -def find_all_py_files(folder_path): - res = [] - for file_path in os.listdir(folder_path): - current_file = os.path.join(folder_path, file_path) - if os.path.isfile(current_file) and current_file.endswith(".py"): - res.append(current_file) - elif os.path.isdir(current_file): - res.extend(find_all_py_files(current_file)) - return res - -def get_config_element_from_line(line, line_nr): - line = line.strip() - if "own_config" in line: - return None - config_ele = line[line.find("config.get") + len("config.get"):] - ele_type = config_ele[1:config_ele.find("(")] - if not ele_type: - return None - if config_ele.count("(") == 1: - between_parenthesis = config_ele[config_ele.find("(")+1: config_ele.find(")")] - else: - between_parenthesis = config_ele[config_ele.find("(")+1:] - if ")" in between_parenthesis: - next_closing_pos, next_opening_pos = 0, 0 - for _ in range(config_ele.count("(")): - next_closing_pos = between_parenthesis.find(")", next_closing_pos+1) - next_opening_pos = between_parenthesis.find("(", next_opening_pos+1) - if next_closing_pos < next_opening_pos: - between_parenthesis = between_parenthesis[1:next_closing_pos] - break - default_val = None - if "," in between_parenthesis: - # has a default value - split_pos = between_parenthesis.find(",") - key_word, default_val = between_parenthesis[:split_pos], between_parenthesis[split_pos+1:] - default_val = default_val.strip() - else: - key_word = between_parenthesis - key_word = key_word.strip() - if "\"" in key_word: - sep = "\"" - elif "\'" in key_word: - sep = "'" - else: - return None - key_word = key_word.replace(sep, "") - if key_word: - return ConfigElement(key_word, ele_type, line, line_nr, default_val) - else: - return None - -def get_config_value_from_csv_line(line, line_nr): - if line.count("\"") < 2: - return None - line = line.strip() - key_word = line[line.find("\"") + 1:] - description = key_word - next_pos = key_word.find("\"") - key_word = key_word[:next_pos].strip() - description = description[next_pos + 1:] - description = description[description.find("\"") + 1:description.rfind("\"")].strip() - if key_word: - return ConfigElement(key_word, None, line, line_nr, None, description) - else: - return None - -def convert_to_list_style(list_of_config_elements, list_name): - text = " .. list-table:: " + list_name + "\n :widths: 25 100 10\n :header-rows: 1\n\n" - text += " * - Parameter\n - Description\n - Type\n" - spacing_value = 105 - for ele in list_of_config_elements: - text += " * - " + ele.key_word + "\n" - description = ele.description.strip() - if len(description) < spacing_value: - text += " - " + description + "\n" - else: - last_pos = 0 - amount_of_splits = len(description) // spacing_value - for i in range(amount_of_splits): - next_split = description.rfind(" ", 0, last_pos + spacing_value) - current_des = description[last_pos:next_split] - last_pos = next_split + 1 - if i == 0: - text += " - " + current_des + "\n" - else: - text += " " + current_des + "\n" - if last_pos < len(description): - current_des = description[last_pos:].strip() - if len(current_des) > 0: - text += " " + current_des + "\n" - if text[-2] == "\"": - text = text[:-2] + "\n" - if ele.ele_type: - text += " - " + ele.ele_type + "\n" - else: - print("Warning: {}".format(ele.key_word)) - text += " -\n" - - - return text[:-1] - -class ConfigElement(object): - - def __init__(self, key_word, ele_type, line, line_nr, default_val, description=None): - self.key_word = key_word.strip() - self.ele_type = ele_type - self.default_value = default_val - self.description = description - if self.description and "Type:" in self.description: - self.set_type(line) - first_part = self.description[self.description.find("\"")+1:self.description.find("Type:")] - self.description = first_part + \ - self.description[self.description.rfind(self.ele_type) + len(self.ele_type)+1:] - self.description = self.description.replace(" ", " ") - self.line = line - self.line_nr = line_nr - self.found_usage = None - - def __repr__(self): - if self.default_value: - return str("{}({}): {}, description: {}".format(self.key_word, self.ele_type, self.default_value, self.description)) - else: - return str("{}({}), description: {}".format(self.key_word, self.ele_type, self.description)) - - def set_type(self, line): - if "Type:" in line: - ele_type = line[line.find("Type:") + len("Type:"):] - ele_type = ele_type.strip() - if "Default" in ele_type: - poses = [ele_type.find("Default"), ele_type.find(".Default"), ele_type.find(". Default"), - ele_type.find(",Default"), ele_type.find(", Default")] - else: - poses = [max([ele_type.find(". "), ele_type.find("."), ele_type.find(".\"")]), - ele_type.find(" "), ele_type.find(", ")] - # eleminate not found - poses = [ele for ele in poses if ele > 0] - if poses: - end_pos = min(poses) - ele_type = ele_type[:end_pos] - if ele_type: - self.ele_type = ele_type - - def add_description(self, line): - if line.count("\"") < 2: - return - line = line.strip() - if "Type:" in line: - self.set_type(line) - first_part = line[line.find("\"")+1:line.find("Type:")] - line = first_part + line[line.rfind(self.ele_type) + len(self.ele_type)+1:] - else: - line = line[line.find("\"")+1: line.rfind("\"")].strip() - self.description += " " + line - self.description = self.description.replace(" ", " ") - - def set_default(self, line): - if "Default:" in line: - default_val = line[line.find("Default:") + len("Default:"):] - default_val = default_val.strip() - float_mode = default_val[0].isnumeric() - list_mode = default_val[0] == "[" - end_pos = -1 - first_point = True - if float_mode or list_mode: - for index, ele in enumerate(default_val): - end_pos = index - if float_mode and ele.isnumeric(): - continue - if float_mode and ele == "." and first_point: - first_point = False - continue - elif float_mode: - break - elif list_mode and ele == "]": - end_pos += 1 - break - else: - poses = [max([default_val.find(". "), default_val.find("."), default_val.find(".\"")]), - default_val.find("\""), default_val.find(" "), default_val.find(", ")] - poses = [ele for ele in poses if ele > 0] - if poses: - end_pos = min(poses) - if end_pos != -1: - default_val = default_val[:end_pos] - if default_val: - self.default_value = default_val - -def convert_element_to_type(element, ele_type): - convert_str = "{}({})".format(ele_type, element) - return eval(convert_str) - - -def check_if_element_is_of_type(element, ele_type): - try: - convert_str = "{}({})".format(ele_type, element) - eval(convert_str) - except ValueError: - return False - except NameError: - return False - except TypeError: - return False - except SyntaxError as e: - print(convert_str, ele_type, element) - raise e - return True - -def check_if_element_is_correct(current_element): - errors = [] - if current_element.ele_type is None: - if current_element.found_usage: - errors.append( - "This key '{}' does not have a Type, used type in code: {}".format(current_element.key_word, - [ele.ele_type for ele in - current_element.found_usage])) - else: - errors.append("This key '{}' does not have a Type".format(current_element.key_word)) - if current_element.default_value and current_element.found_usage: - for found_value in current_element.found_usage: - f_default_v = found_value.default_value - if f_default_v != current_element.default_value: - ele_type = current_element.ele_type.lower() - if ele_type == "int" or ele_type == "float": - current_def_val = current_element.default_value - if check_if_element_is_of_type(f_default_v, found_value.ele_type) and \ - check_if_element_is_of_type(current_def_val, current_element.ele_type): - found_val = convert_element_to_type(f_default_v, found_value.ele_type) - current_val = convert_element_to_type(current_def_val, current_element.ele_type) - if found_val != current_val: - errors.append("The default value does not match the value in the docu for key: {} " - "({}!={})".format(current_element.key_word, - current_element.default_value, found_value.default_value)) - - elif current_element.found_usage: - for found_value in current_element.found_usage: - if check_if_element_is_of_type(found_value.default_value, found_value.ele_type): - errors.append("The key '{}' misses the default value used in " - "the code: {}".format(current_element.key_word, found_value.default_value)) - - return errors - - -if __name__ == "__main__": - - parser = argparse.ArgumentParser("Finds missing documentation in BlenderProc") - parser.add_argument("-s", "--src", help="You can specify a certain source folder to see only modules " - "from this folder", type=str) - args = parser.parse_args() - - exempt_files = ["Utility.py"] - - all_py_files = find_all_py_files(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "blenderproc")) - for py_file in all_py_files: - base_name = os.path.basename(py_file) - if args.src and args.src not in py_file: - continue - skip_this_file = False - for exempt_file in exempt_files: - if exempt_file in base_name: - skip_this_file = True - if skip_this_file: - continue - if "scripts" not in os.path.abspath(py_file): - with open(py_file, "r") as file: - errors = [] - lines = file.read().split("\n") - list_of_splits = [] - notDone = True - last_start_lines = 0 - while notDone: - notDone = False - found_config_values = [] - start_csv_table = False - line_nr_span = [-1, -1] - for line_nr, line in enumerate(lines[last_start_lines:]): - current_line_nr = line_nr + last_start_lines - if "csv-table" in line and not start_csv_table: - start_csv_table = True - line_nr_span[0] = current_line_nr - elif (line.strip().startswith("**") or "csv-table" in line \ - or '"""' in line) and start_csv_table: - line_nr_span[1] = current_line_nr - last_start_lines = current_line_nr - notDone = True - break - elif start_csv_table and "__init__" in line: - break - if start_csv_table: - line = line.strip() - org_line = line - first_mark = line.find('"') - second_mark = line.find('"', first_mark + 1) - space_pos = line.find(" ") - available = line.find("Available:") - if line and "\", \"" in line and not ":header:" in line and \ - (space_pos > second_mark and (available != -1 and space_pos < available or available == -1) ): - config_element = get_config_value_from_csv_line(line, current_line_nr) - if config_element: - found_config_values.append(config_element) - elif "\"" in line and not ":header" in line: - found_config_values[-1].add_description(line) - if line_nr_span[0] > 0: - list_of_splits.append((line_nr_span, found_config_values)) - - last_used = 0 - final_text = [] - for line_nr_span, found_config_values in list_of_splits: - final_text.extend(lines[last_used:line_nr_span[0]]) - final_text.extend(convert_to_list_style(found_config_values, "").split("\n")) - last_used = line_nr_span[1] - final_text.extend(lines[last_used:]) - final_text = "\n".join(final_text) - final_text.replace("\n\n", "\n") - - with open(py_file, "w") as file: - file.write(final_text) - - - - - - diff --git a/docs/cleanup_api_imports.py b/docs/cleanup_api_imports.py index cf9d00cab..2cf8e58ff 100644 --- a/docs/cleanup_api_imports.py +++ b/docs/cleanup_api_imports.py @@ -5,7 +5,7 @@ if ".api." in rst_file.name: with open(rst_file) as f: test = f.read() - test = test.replace(":show-inheritance:", ":show-inheritance:\n :imported-members:") + test = test.replace(":show-inheritance:", ":show-inheritance:\n :imported-members:") test = test.replace(".api.", ".") test = test.replace("\\.api\\.", "\\.") with open(rst_file, "w") as f: diff --git a/docs/config_for_pip_install.yaml b/docs/config_for_pip_install.yaml deleted file mode 100644 index e667e2866..000000000 --- a/docs/config_for_pip_install.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Args: -{ - "version": 3, - "setup": { - "blender_install_path": "/home_local//blender/", - "pip": [ - "h5py", - "numpy", - "Pillow", - "opencv-contrib-python", - "scipy", - "git+https://github.com/abahnasy/smplx", - "git+https://github.com/abahnasy/human_body_prior", - "scikit-image", - "pypng==0.0.20", - - "m2r2==0.2.7", - "sphinx-autodoc-typehints==1.6.0", - "sphinx-rtd-theme==0.4.3", - "sphinx==1.8.5" - ] - }, - "modules": [ - ] -} diff --git a/docs/find_missing_docu.py b/docs/find_missing_docu.py deleted file mode 100644 index 9c4ac3b06..000000000 --- a/docs/find_missing_docu.py +++ /dev/null @@ -1,284 +0,0 @@ - -import os -import argparse - -def find_all_py_files(folder_path): - res = [] - for file_path in os.listdir(folder_path): - current_file = os.path.join(folder_path, file_path) - if os.path.isfile(current_file) and current_file.endswith(".py"): - res.append(current_file) - elif os.path.isdir(current_file): - res.extend(find_all_py_files(current_file)) - return res - -def get_config_element_from_line(line, line_nr): - line = line.strip() - if "own_config" in line: - return None - config_ele = line[line.find("config.get") + len("config.get"):] - ele_type = config_ele[1:config_ele.find("(")] - if not ele_type: - return None - if config_ele.count("(") == 1: - between_parenthesis = config_ele[config_ele.find("(")+1: config_ele.find(")")] - else: - between_parenthesis = config_ele[config_ele.find("(")+1:] - if ")" in between_parenthesis: - next_closing_pos, next_opening_pos = 0, 0 - for _ in range(config_ele.count("(")): - next_closing_pos = between_parenthesis.find(")", next_closing_pos+1) - next_opening_pos = between_parenthesis.find("(", next_opening_pos+1) - if next_closing_pos < next_opening_pos: - between_parenthesis = between_parenthesis[1:next_closing_pos] - break - default_val = None - if "," in between_parenthesis: - # has a default value - split_pos = between_parenthesis.find(",") - key_word, default_val = between_parenthesis[:split_pos], between_parenthesis[split_pos+1:] - default_val = default_val.strip() - else: - key_word = between_parenthesis - key_word = key_word.strip() - if "\"" in key_word: - sep = "\"" - elif "\'" in key_word: - sep = "'" - else: - return None - key_word = key_word.replace(sep, "") - if key_word: - return ConfigElement(key_word, ele_type, line, line_nr, default_val) - else: - return None - -def get_config_value_from_csv_line(line, line_nr): - if line.count("\"") < 2: - return None - line = line.strip() - key_word = line[line.find("\"") + 1:] - key_word = key_word[:key_word.find("\"")].strip() - if key_word: - return ConfigElement(key_word, None, line, line_nr, None) - else: - return None - -class ConfigElement(object): - - def __init__(self, key_word, ele_type, line, line_nr, default_val): - self.key_word = key_word.strip() - self.ele_type = ele_type - self.default_value = default_val - self.line = line - self.line_nr = line_nr - self.found_usage = None - - def __repr__(self): - if self.default_value: - return str("{}({}): {}".format(self.key_word, self.ele_type, self.default_value)) - else: - return str("{}({})".format(self.key_word, self.ele_type)) - - def set_type(self, line): - if "Type:" in line: - ele_type = line[line.find("Type:") + len("Type:"):] - ele_type = ele_type.strip() - if "Default" in ele_type: - poses = [ele_type.find("Default"), ele_type.find(".Default"), ele_type.find(". Default"), - ele_type.find(",Default"), ele_type.find(", Default")] - else: - poses = [max([ele_type.find(". "), ele_type.find("."), ele_type.find(".\"")]), - ele_type.find(" "), ele_type.find(", ")] - poses = [ele for ele in poses if ele > 0] - if poses: - end_pos = min(poses) - ele_type = ele_type[:end_pos] - if ele_type: - self.ele_type = ele_type - - def set_default(self, line): - if "Default:" in line: - default_val = line[line.find("Default:") + len("Default:"):] - default_val = default_val.strip() - float_mode = default_val[0].isnumeric() - list_mode = default_val[0] == "[" - end_pos = -1 - first_point = True - if float_mode or list_mode: - for index, ele in enumerate(default_val): - end_pos = index - if float_mode and ele.isnumeric(): - continue - if float_mode and ele == "." and first_point: - first_point = False - continue - elif float_mode: - break - elif list_mode and ele == "]": - end_pos += 1 - break - else: - poses = [max([default_val.find(". "), default_val.find("."), default_val.find(".\"")]), - default_val.find("\""), default_val.find(" "), default_val.find(", ")] - poses = [ele for ele in poses if ele > 0] - if poses: - end_pos = min(poses) - if end_pos != -1: - default_val = default_val[:end_pos] - if default_val: - self.default_value = default_val - -def convert_element_to_type(element, ele_type): - convert_str = "{}({})".format(ele_type, element) - return eval(convert_str) - - -def check_if_element_is_of_type(element, ele_type): - try: - convert_str = "{}({})".format(ele_type, element) - eval(convert_str) - except ValueError: - return False - except NameError: - return False - except TypeError: - return False - except SyntaxError as e: - print(convert_str, ele_type, element) - raise e - return True - -def check_if_element_is_correct(current_element): - errors = [] - if current_element.ele_type is None: - if current_element.found_usage: - errors.append( - "This key '{}' does not have a Type, used type in code: {}".format(current_element.key_word, - [ele.ele_type for ele in - current_element.found_usage])) - else: - errors.append("This key '{}' does not have a Type".format(current_element.key_word)) - if current_element.default_value and current_element.found_usage: - for found_value in current_element.found_usage: - f_default_v = found_value.default_value - if f_default_v != current_element.default_value: - ele_type = current_element.ele_type.lower() - if ele_type == "int" or ele_type == "float": - current_def_val = current_element.default_value - if check_if_element_is_of_type(f_default_v, found_value.ele_type) and \ - check_if_element_is_of_type(current_def_val, current_element.ele_type): - found_val = convert_element_to_type(f_default_v, found_value.ele_type) - current_val = convert_element_to_type(current_def_val, current_element.ele_type) - if found_val != current_val: - errors.append("The default value does not match the value in the docu for key: {} " - "({}!={})".format(current_element.key_word, - current_element.default_value, found_value.default_value)) - - elif current_element.found_usage: - for found_value in current_element.found_usage: - if check_if_element_is_of_type(found_value.default_value, found_value.ele_type): - errors.append("The key '{}' misses the default value used in " - "the code: {}".format(current_element.key_word, found_value.default_value)) - - return errors - - -if __name__ == "__main__": - - parser = argparse.ArgumentParser("Finds missing documentation in BlenderProc") - parser.add_argument("-s", "--src", help="You can specify a certain source folder to see only modules " - "from this folder", type=str) - args = parser.parse_args() - - exempt_files = ["Utility.py"] - - all_py_files = find_all_py_files(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) - for py_file in all_py_files: - base_name = os.path.basename(py_file) - if args.src and args.src not in py_file: - continue - skip_this_file = False - for exempt_file in exempt_files: - if exempt_file in base_name: - skip_this_file = True - if skip_this_file: - continue - if "scripts" not in os.path.abspath(py_file): - with open(py_file, "r") as file: - errors = [] - lines = file.read().split("\n") - found_config_values = [] - start_csv_table = False - for line in lines: - if "csv-table" in line: - start_csv_table = True - elif start_csv_table and "__init__" in line: - break - if start_csv_table: - line = line.strip() - if line and "\", \"" in line and not ":header:" in line: - line = line[line.find("\"") + 1:] - line = line[:line.find("\"")].strip() - if line: - found_config_values.append(line) - list_of_used_config_get = [] - # checks if there are config values not defined at the top - for line_nr, line in enumerate(lines): - org_line = line - if "config.get" in line: - org_line = org_line[org_line.find("config.get"):] - org_line = org_line[:org_line.find(")")+1] - config_element = get_config_element_from_line(line, line_nr) - if config_element: - key_word = config_element.key_word - if " " not in key_word and key_word != "key": - list_of_used_config_get.append(config_element) - if key_word not in found_config_values: - errors.append("Not found at the top: '{}' " \ - "in L:{} '{}'".format(key_word, line_nr, org_line.strip())) - start_csv_table = False - start_new_config_value = False - current_element = None - # check if each key_word has a Type: - for line_nr, line in enumerate(lines): - if ":header:" in line: - continue - if "csv-table" in line: - start_csv_table = True - elif start_csv_table and ("__init__" in line or line.strip() == '"""'): - if current_element: - errors.extend(check_if_element_is_correct(current_element)) - break - if start_csv_table: - config_element = get_config_value_from_csv_line(line, line_nr) - if config_element and " " not in config_element.key_word and config_element.key_word != "key": - found_values = [ele for ele in list_of_used_config_get if - ele.key_word == config_element.key_word] - if found_values: - config_element.found_usage = found_values.copy() - if current_element: - # found a new key_word, check the last one - errors.extend(check_if_element_is_correct(current_element)) - current_element = config_element - if current_element: - # there is no new key found, and there was an old key there - if "Type:" in line: - current_element.set_type(line) - if "Default:" in line: - current_element.set_default(line) - if errors: - print("In {}: \n{}".format(base_name, "\n".join([" "+ele for ele in errors]))) - - - - - - - - - - - - - diff --git a/docs/generate.sh b/docs/generate.sh index ee39611bf..494bfbf03 100755 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -3,7 +3,7 @@ rm build -r export INSIDE_OF_THE_INTERNAL_BLENDER_PYTHON_ENVIRONMENT=1 find source/*.rst ! -name 'index.rst' -type f -exec rm -f {} + -sphinx-apidoc -e -P -f -o source/ ../blenderproc ../blenderproc/command_line ../blenderproc/resources ../blenderproc/external ../blenderproc/scripts ../blenderproc/cli.py ../blenderproc/debug.py ../blenderproc/debug_startup.py +sphinx-apidoc -e -P -f -o source/ ../blenderproc ../blenderproc/command_line ../blenderproc/resources ../blenderproc/external ../blenderproc/scripts ../blenderproc/cli.py ../blenderproc/debug.py ../blenderproc/debug_startup.py ../blenderproc/__main__.py ../blenderproc/run.py ../blenderproc/python/tests/TestsPathManager.py python3 cleanup_api_imports.py make html diff --git a/docs/make.bat b/docs/make.bat index 21ae9c280..dc1312ab0 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -9,9 +9,6 @@ if "%SPHINXBUILD%" == "" ( ) set SOURCEDIR=source set BUILDDIR=build -set SPHINXPROJ=BlenderProc - -if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( @@ -22,15 +19,17 @@ if errorlevel 9009 ( echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ + echo.https://www.sphinx-doc.org/ exit /b 1 ) -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% goto end :help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end popd diff --git a/docs/run.py b/docs/run.py index 11bef6ebb..3c12934dc 100644 --- a/docs/run.py +++ b/docs/run.py @@ -4,10 +4,11 @@ from shutil import which # Add path to custom packages inside the blender main directory -sys.path.append(os.path.join(os.path.dirname(sys.executable), "..", "..", "..", "custom-python-packages")) +sys.path.append(os.path.join(os.path.dirname(sys.executable), "..", "..", "..", "custom-python-packages/lib/python3.10/site-packages/")) # Determine abs path to sphinx-build -sphinx_build_bin_path = which('sphinx-build') +sphinx_build_bin_path = os.path.join(os.path.dirname(sys.executable), "..", "..", "..", "custom-python-packages", "bin", "sphinx-build") +print(sphinx_build_bin_path) # Read args sys.argv = [sphinx_build_bin_path] + sys.argv[sys.argv.index("--") + 1:] diff --git a/docs/source/conf.py b/docs/source/conf.py index 07eeb7d68..03c0460d6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,192 +1,56 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# BlenderPipeline documentation build configuration file, created by -# sphinx-quickstart on Mon Oct 7 13:44:00 2019. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# import os import sys sys.path.insert(0, os.path.abspath('../..')) -print(os.path.abspath("./ext")) sys.path.append(os.path.abspath("./ext")) +from blenderproc.version import __version__ -autodoc_default_flags = ['members', 'undoc-members', 'private-members', 'show-inheritance'] -print(sys.argv, os.getcwd()) +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html -# -- General configuration ------------------------------------------------ +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' +project = 'BlenderProc' +copyright = '2023, DLR RMC' +author = 'DLR RMC' +release = '2023' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. extensions = ['sphinx.ext.autodoc', 'sphinx_autodoc_typehints', 'sphinx.ext.intersphinx', 'sphinx.ext.imgmath', 'sphinx.ext.viewcode', 'moduleoverview', - #'recommonmark', + 'sphinx_rtd_theme', 'm2r2' ] -# Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = ['.rst', '.md'] -m2r_parse_relative_links = True - - - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = 'BlenderProc' -copyright = '2021, DLR RMC' -author = 'DLR RMC' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '' -# The full version, including alpha/beta/rc tags. -release = '2.0.0' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This patterns also effect to html_static_path and html_extra_path exclude_patterns = [] +source_suffix = ['.rst', '.md'] +release = __version__ +version = __version__ -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False -# -- Options for HTML output ---------------------------------------------- +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# html_theme = 'sphinx_rtd_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". +html_theme_options = { + 'display_version': True +} html_static_path = ['_static'] -html_context = { - 'css_files': [ - '_static/css/theme_overrides.css', # override wide tables in RTD theme - ], - } - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# This is required for the alabaster theme -# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars +html_css_files = [ + 'css/theme_overrides.css', +] html_sidebars = { '**': [ - 'relations.html', # needs 'show_related': True theme option to display - 'searchbox.html', + 'localtoc.html', ] -} - - -# -- Options for HTMLHelp output ------------------------------------------ - -# Output file base name for HTML help builder. -htmlhelp_basename = 'BlenderProcdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'BlenderProc.tex', 'BlenderProc Documentation', - 'DLR RMC', 'manual'), -] - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'blenderproc', 'BlenderProc Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'BlenderProc', 'BlenderProc Documentation', - author, 'BlenderProc', 'One line description of project.', - 'Miscellaneous'), -] - - - - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} +} \ No newline at end of file diff --git a/docs/source/ext/moduleoverview.py b/docs/source/ext/moduleoverview.py index e24cc31f9..983f444ff 100644 --- a/docs/source/ext/moduleoverview.py +++ b/docs/source/ext/moduleoverview.py @@ -1,9 +1,11 @@ +import traceback from docutils.parsers.rst import Directive from docutils import nodes import pdb from collections import defaultdict from sphinx import addnodes from pathlib import Path +from sphinx import addnodes class classlist(nodes.General, nodes.Element): pass @@ -29,12 +31,11 @@ def generate_classlist(app, fromdocname, subtree, class_list, prefix, level=2): subtree += class_item def generate_collapsible_classlist(app, fromdocname, classes, container, caption, module_index, label_prefix): + container += nodes.title(caption, '', *[nodes.Text(caption)]) toc = nodes.bullet_list() - toc += nodes.caption(caption, '', *[nodes.Text(caption)]) if module_index is not None: entries = defaultdict(list) - #print("test", classes, fromdocname, caption) prefix = ".".join(classes[0][0].split(".")[:module_index]) + "." for e in classes: module = e[0].split(".")[module_index] @@ -72,7 +73,7 @@ def generate_tutorials_sidebar(app, fromdocname, container): ("Positioning objects via the physics simulator", "physics"), ] - container += nodes.caption("Tutorials", '', *[nodes.Text("Tutorials")]) + container += nodes.title("Tutorials", '', *[nodes.Text("Tutorials")]) for tutorial in tutorials: toc = nodes.bullet_list() @@ -88,7 +89,7 @@ def generate_tutorials_sidebar(app, fromdocname, container): def generate_examples_sidebar(app, fromdocname, container): examples = Path(__file__).absolute().parent.parent / "examples" - container += nodes.caption("Examples", '', *[nodes.Text("Examples")]) + container += nodes.title("Examples", '', *[nodes.Text("Examples")]) for example_groups in [examples / group for group in ["basics", "advanced", "datasets"]]: if example_groups.is_dir(): toc = nodes.bullet_list() @@ -118,30 +119,29 @@ def generate_examples_sidebar(app, fromdocname, container): def generate_sidebar(app, fromdocname): env = app.builder.env - container = nodes.compound(classes=['toctree-wrapper'])#addnodes.compact_paragraph('', '', classes=['toctree-wrapper']) + #container = nodes.compound(classes=['toctree-wrapper'])#addnodes.compact_paragraph('', '', classes=['toctree-wrapper']) + container = addnodes.compact_paragraph() + container['toctree'] = True py = env.get_domain('py') classes = py.get_objects() #print("classes", classes, [_[2] for _ in py.get_objects()]) - classes_per_group = {"api": ([], None, "bproc."), "internal": ([], 2, "bproc.python."), "modules (deprecated)": ([], 3, "")}#"modules": ([], 1), "provider": ([], 2), + classes_per_group = {"Entities (bproc.types)": ([], None, ""), "api": ([], None, "bproc."), "internal": ([], 2, "bproc.python.")}#, "modules (deprecated)": ([], 3, "")}#"modules": ([], 1), "provider": ([], 2), for e in classes: if e[2] == 'module' and e[3].startswith("blenderproc.api.") or e[2] == 'class' and not e[3].startswith("blenderproc.api."): - #print(e) if e[3].startswith("blenderproc.api."): - group = "api" - elif e[0].startswith("blenderproc.python.modules."): - group = "modules (deprecated)" + classes_per_group["api"][0].append(e) else: - group = "internal" - #print(group, e) - - classes_per_group[group][0].append(e) + if e[3].startswith("blenderproc.python.types"): + classes_per_group["Entities (bproc.types)"][0].append(e) + classes_per_group["internal"][0].append(e) generate_tutorials_sidebar(app, fromdocname, container) generate_examples_sidebar(app, fromdocname, container) for key, items in classes_per_group.items(): generate_collapsible_classlist(app, fromdocname, items[0], container, key.capitalize(), items[1], items[2]) + return container def process_classlist(app, doctree, fromdocname): @@ -150,7 +150,7 @@ def process_classlist(app, doctree, fromdocname): ctx = app.env.config['html_context'] ctx['classlist'] = container for node in doctree.traverse(classlist): - node.replace_self([container]) + node.replace_self(container) continue @@ -171,9 +171,14 @@ def make_toctree(collapse=True, maxdepth=-1, includehidden=True, titles_only=Fal fromdocname = "" if "title" not in context else context["title"] fulltoc = generate_sidebar(app, fromdocname) - rendered_toc = app.builder.render_partial(fulltoc)['fragment'] + try: + rendered_toc = app.builder.render_partial(fulltoc)['fragment'] + except: + print(traceback.format_exc()) + return rendered_toc + context['toctree'] = make_toctree def setup(app):