From 9d152e12a4c7175a67b0b8ce5daec6fbf6e6e54a Mon Sep 17 00:00:00 2001 From: Yixing Lao Date: Fri, 29 Mar 2019 02:42:50 -0700 Subject: [PATCH] Python docs with side-panel navigation and links (#881) * python api docs with side-panel navigation and links * Fix typo * fix non-ascii char --- .gitignore | 1 + docs/Makefile | 18 +- docs/_static/theme_overrides.css | 14 + docs/conf.py | 11 +- docs/index.rst | 18 +- docs/make.bat | 10 +- docs/make.py | 284 ++++++++++++++++++ docs/python_api/camera.rst | 12 - docs/python_api/color_map.rst | 11 - docs/python_api/geometry.rst | 11 - docs/python_api/integration.rst | 11 - docs/python_api/io.rst | 11 - docs/python_api/odometry.rst | 11 - docs/python_api/registration.rst | 11 - docs/python_api/utility.rst | 11 - docs/python_api/visualization.rst | 11 - src/Python/color_map/color_map.cpp | 5 +- src/Python/open3d_pybind.h | 2 +- .../registration/global_optimization.cpp | 24 +- src/Python/utility/eigen.cpp | 143 +++++++-- src/Python/visualization/renderoption.cpp | 3 +- src/Python/visualization/visualizer.cpp | 6 +- 22 files changed, 469 insertions(+), 170 deletions(-) create mode 100644 docs/_static/theme_overrides.css create mode 100644 docs/make.py delete mode 100644 docs/python_api/camera.rst delete mode 100644 docs/python_api/color_map.rst delete mode 100644 docs/python_api/geometry.rst delete mode 100644 docs/python_api/integration.rst delete mode 100644 docs/python_api/io.rst delete mode 100644 docs/python_api/odometry.rst delete mode 100644 docs/python_api/registration.rst delete mode 100644 docs/python_api/utility.rst delete mode 100644 docs/python_api/visualization.rst diff --git a/.gitignore b/.gitignore index 2cacd43a3f2..76608041e45 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ node_modules/ static/ **/package-lock.json *.old* +docs/python_api/ diff --git a/docs/Makefile b/docs/Makefile index d89dc4208ce..a97f0eb7334 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,20 +1,6 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXPROJ = Open3D -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile +.PHONY: Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @python make.py $@ diff --git a/docs/_static/theme_overrides.css b/docs/_static/theme_overrides.css new file mode 100644 index 00000000000..29efa6f4274 --- /dev/null +++ b/docs/_static/theme_overrides.css @@ -0,0 +1,14 @@ +/* Force table wrap: https://rackerlabs.github.io/docs-rackspace/tools/rtd-tables.html */ +/* override table width restrictions */ +@media screen and (min-width: 767px) { + + .wy-table-responsive table td { + /* !important prevents the common CSS stylesheets from overriding + this as on RTD they are loaded after this stylesheet */ + white-space: normal !important; + } + + .wy-table-responsive { + overflow: visible !important; + } + } diff --git a/docs/conf.py b/docs/conf.py index a941ec9b088..f1ee93746f1 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -66,8 +66,8 @@ # General information about the project. project = u'Open3D' -copyright = u'2018, Qianyi Zhou and Jaesik Park' -author = u'Qianyi Zhou and Jaesik Park' +copyright = u'2018 - 2019, www.open3d.org' +author = u'www.open3d.org' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -117,6 +117,13 @@ # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] +# Force table wrap: https://rackerlabs.github.io/docs-rackspace/tools/rtd-tables.html +html_context = { + 'css_files': [ + '_static/theme_overrides.css', # override wide tables in RTD theme + ], + } + # added by Jaesik to hide "View page source" html_show_sourcelink = False diff --git a/docs/index.rst b/docs/index.rst index 518b5f47cff..decbeb827f4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -41,12 +41,12 @@ Open3D: A Modern Library for 3D Data Processing :maxdepth: 1 :caption: Python API - python_api/camera - python_api/color_map - python_api/geometry - python_api/io - python_api/integration - python_api/odometry - python_api/registration - python_api/utility - python_api/visualization + python_api/open3d.camera + python_api/open3d.color_map + python_api/open3d.geometry + python_api/open3d.io + python_api/open3d.integration + python_api/open3d.odometry + python_api/open3d.registration + python_api/open3d.utility + python_api/open3d.visualization diff --git a/docs/make.bat b/docs/make.bat index 4118e308bd3..f21f1e1cf7a 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -7,11 +7,6 @@ REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) -set SOURCEDIR=. -set BUILDDIR=_build -set SPHINXPROJ=Open3D - -if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( @@ -26,11 +21,8 @@ if errorlevel 9009 ( exit /b 1 ) -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +python make.py %1 goto end -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - :end popd diff --git a/docs/make.py b/docs/make.py new file mode 100644 index 00000000000..68601262dfb --- /dev/null +++ b/docs/make.py @@ -0,0 +1,284 @@ +# ---------------------------------------------------------------------------- +# - Open3D: www.open3d.org - +# ---------------------------------------------------------------------------- +# The MIT License (MIT) +# +# Copyright (c) 2018 www.open3d.org +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ---------------------------------------------------------------------------- + +# Sphinx makefile with api docs generation +# (1) The user call `make *` (e.g. `make html`) gets forwarded to make.py +# (2) make.py generate python api docs, one ".rst" file per class / function +# (3) make.py calls the actual `sphinx-build` + +from __future__ import print_function +import argparse +import subprocess +import sys +import multiprocessing +import importlib +import os +from inspect import getmembers, isbuiltin, isclass, ismodule +import shutil + +# Global sphinx options +SPHINX_BUILD = "sphinx-build" +SOURCE_DIR = "." +BUILD_DIR = "_build" + + +class PyDocsBuilder: + """ + Generate Python API *.rst files, per (sub) module, per class, per function. + The file name is the full module name. + + E.g. If output_dir == "python_api", the following files are generated: + python_api/open3d.camera.rst + python_api/open3d.camera.PinholeCameraIntrinsic.rst + ... + """ + + def __init__(self, output_dir, c_module, c_module_relative): + self.output_dir = output_dir + self.c_module = c_module + self.c_module_relative = c_module_relative + print("Generating *.rst Python API docs in directory: %s" % self.output_dir) + + def generate_rst(self): + if not os.path.exists(self.output_dir): + print("Creating directory %s" % self.output_dir) + os.makedirs(self.output_dir) + + main_c_module = importlib.import_module(self.c_module) + sub_module_names = sorted( + [obj[0] for obj in getmembers(main_c_module) if ismodule(obj[1])] + ) + for sub_module_name in sub_module_names: + PyDocsBuilder._generate_sub_module_class_function_docs( + sub_module_name, self.output_dir + ) + + @staticmethod + def _generate_function_doc(sub_module_full_name, function_name, output_path): + # print("Generating docs: %s" % (output_path,)) + out_string = "" + out_string += "%s.%s" % (sub_module_full_name, function_name) + out_string += "\n" + "-" * len(out_string) + out_string += "\n\n" + ".. currentmodule:: %s" % sub_module_full_name + out_string += "\n\n" + ".. autofunction:: %s" % function_name + out_string += "\n" + + with open(output_path, "w") as f: + f.write(out_string) + + @staticmethod + def _generate_class_doc(sub_module_full_name, class_name, output_path): + # print("Generating docs: %s" % (output_path,)) + out_string = "" + out_string += "%s.%s" % (sub_module_full_name, class_name) + out_string += "\n" + "-" * len(out_string) + out_string += "\n\n" + ".. currentmodule:: %s" % sub_module_full_name + out_string += "\n\n" + ".. autoclass:: %s" % class_name + out_string += "\n :members:" + out_string += "\n :undoc-members:" + out_string += "\n :inherited-members:" + out_string += "\n" + + with open(output_path, "w") as f: + f.write(out_string) + + @staticmethod + def _generate_sub_module_doc( + sub_module_name, class_names, function_names, sub_module_doc_path + ): + # print("Generating docs: %s" % (sub_module_doc_path,)) + class_names = sorted(class_names) + function_names = sorted(function_names) + sub_module_full_name = "open3d.%s" % (sub_module_name,) + out_string = "" + out_string += sub_module_full_name + out_string += "\n" + "-" * len(out_string) + out_string += "\n\n" + ".. currentmodule:: %s" % sub_module_full_name + + if len(class_names) > 0: + out_string += "\n\n**Classes**" + out_string += "\n\n.. autosummary::" + out_string += "\n" + for class_name in class_names: + out_string += "\n " + "%s" % (class_name,) + out_string += "\n" + + if len(function_names) > 0: + out_string += "\n\n**Functions**" + out_string += "\n\n.. autosummary::" + out_string += "\n" + for function_name in function_names: + out_string += "\n " + "%s" % (function_name,) + out_string += "\n" + + obj_names = class_names + function_names + if len(obj_names) > 0: + out_string += "\n\n.. toctree::" + out_string += "\n :hidden:" + out_string += "\n" + for obj_name in obj_names: + out_string += "\n %s <%s.%s>" % ( + obj_name, + sub_module_full_name, + obj_name, + ) + out_string += "\n" + + with open(sub_module_doc_path, "w") as f: + f.write(out_string) + + @staticmethod + def _generate_sub_module_class_function_docs(sub_module_name, output_dir): + sub_module = importlib.import_module("open3d.open3d.%s" % (sub_module_name,)) + sub_module_full_name = "open3d.%s" % (sub_module_name,) + print("Generating docs for submodule: %s" % sub_module_full_name) + + # Class docs + class_names = [obj[0] for obj in getmembers(sub_module) if isclass(obj[1])] + for class_name in class_names: + file_name = "%s.%s.rst" % (sub_module_full_name, class_name) + output_path = os.path.join(output_dir, file_name) + PyDocsBuilder._generate_class_doc( + sub_module_full_name, class_name, output_path + ) + + # Function docs + function_names = [obj[0] for obj in getmembers(sub_module) if isbuiltin(obj[1])] + for function_name in function_names: + file_name = "%s.%s.rst" % (sub_module_full_name, function_name) + output_path = os.path.join(output_dir, file_name) + PyDocsBuilder._generate_function_doc( + sub_module_full_name, function_name, output_path + ) + + # Submodule docs + sub_module_doc_path = os.path.join(output_dir, sub_module_full_name + ".rst") + PyDocsBuilder._generate_sub_module_doc( + sub_module_name, class_names, function_names, sub_module_doc_path + ) + + +class SphinxDocsBuilder: + """ + # SphinxDocsBuilder calls python api docs generation and then calls + # sphinx-build: + # + # (1) The user call `make *` (e.g. `make html`) gets forwarded to make.py + # (2) Calls PyDocsBuilder to generate python api docs rst files + # (3) Calls `sphinx-build` with the user argument + """ + + valid_makefile_args = { + "changes", + "clean", + "coverage", + "devhelp", + "dirhtml", + "doctest", + "epub", + "gettext", + "help", + "html", + "htmlhelp", + "info", + "json", + "latex", + "latexpdf", + "latexpdfja", + "linkcheck", + "man", + "pickle", + "pseudoxml", + "qthelp", + "singlehtml", + "texinfo", + "text", + "xml", + } + + def __init__(self, makefile_arg): + if makefile_arg not in self.valid_makefile_args: + print('Invalid make argument: "%s", displaying help.' % makefile_arg) + self.is_valid_arg = False + else: + self.is_valid_arg = True + self.makefile_arg = makefile_arg + + # Hard-coded parameters for python API docs generation for now + # Directory structure for the Open3D python package: + # open3d + # - __init__.py + # - open3d.so # Actual name depends on OS and python version + self.c_module = "open3d.open3d" # Points to the open3d.so + self.c_module_relative = "open3d" # The relative module reference to open3d.so + self.python_api_output_dir = "python_api" + + def run(self): + if not self.is_valid_arg: + self.makefile_arg = "help" + elif self.makefile_arg == "clean": + print("Removing directory %s" % self.python_api_output_dir) + shutil.rmtree(self.python_api_output_dir, ignore_errors=True) + elif self.makefile_arg == "help": + pass # Do not call self._gen_python_api_docs() + else: + self._gen_python_api_docs() + self._run_sphinx() + + def _gen_python_api_docs(self): + """ + Generate python docs. + Each module, class and function gets one .rst file. + """ + pd = PyDocsBuilder( + self.python_api_output_dir, self.c_module, self.c_module_relative + ) + pd.generate_rst() + + def _run_sphinx(self): + """ + Call Sphinx command with self.makefile_arg + """ + cmd = [ + SPHINX_BUILD, + "-M", + self.makefile_arg, + SOURCE_DIR, + BUILD_DIR, + "-j", + str(multiprocessing.cpu_count()), + ] + print('Calling: "%s"' % " ".join(cmd)) + subprocess.check_call(cmd, stdout=sys.stdout, stderr=sys.stderr) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("makefile_arg", nargs="?") + args = parser.parse_args() + + sdb = SphinxDocsBuilder(args.makefile_arg) + sdb.run() diff --git a/docs/python_api/camera.rst b/docs/python_api/camera.rst deleted file mode 100644 index 334c3c3c1c1..00000000000 --- a/docs/python_api/camera.rst +++ /dev/null @@ -1,12 +0,0 @@ -open3d.camera -------------- - -.. currentmodule:: open3d - -.. autosummary:: - open3d.camera - -.. automodule:: open3d.camera - :members: - :undoc-members: - diff --git a/docs/python_api/color_map.rst b/docs/python_api/color_map.rst deleted file mode 100644 index c0a0603d56b..00000000000 --- a/docs/python_api/color_map.rst +++ /dev/null @@ -1,11 +0,0 @@ -open3d.color_map ----------------- - -.. currentmodule:: open3d - -.. autosummary:: - open3d.color_map - -.. automodule:: open3d.color_map - :members: - :undoc-members: diff --git a/docs/python_api/geometry.rst b/docs/python_api/geometry.rst deleted file mode 100644 index d96b74df197..00000000000 --- a/docs/python_api/geometry.rst +++ /dev/null @@ -1,11 +0,0 @@ -open3d.geometry ---------------- - -.. currentmodule:: open3d - -.. autosummary:: - open3d.geometry - -.. automodule:: open3d.geometry - :members: - :undoc-members: diff --git a/docs/python_api/integration.rst b/docs/python_api/integration.rst deleted file mode 100644 index 62724088443..00000000000 --- a/docs/python_api/integration.rst +++ /dev/null @@ -1,11 +0,0 @@ -open3d.integration ------------------- - -.. currentmodule:: open3d - -.. autosummary:: - open3d.integration - -.. automodule:: open3d.integration - :members: - :undoc-members: diff --git a/docs/python_api/io.rst b/docs/python_api/io.rst deleted file mode 100644 index 5ff4d2d26e5..00000000000 --- a/docs/python_api/io.rst +++ /dev/null @@ -1,11 +0,0 @@ -open3d.io ---------- - -.. currentmodule:: open3d - -.. autosummary:: - open3d.io - -.. automodule:: open3d.io - :members: - :undoc-members: diff --git a/docs/python_api/odometry.rst b/docs/python_api/odometry.rst deleted file mode 100644 index 2f1c92c3c67..00000000000 --- a/docs/python_api/odometry.rst +++ /dev/null @@ -1,11 +0,0 @@ -open3d.odometry ---------------- - -.. currentmodule:: open3d - -.. autosummary:: - open3d.odometry - -.. automodule:: open3d.odometry - :members: - :undoc-members: diff --git a/docs/python_api/registration.rst b/docs/python_api/registration.rst deleted file mode 100644 index 0b47d4b0aea..00000000000 --- a/docs/python_api/registration.rst +++ /dev/null @@ -1,11 +0,0 @@ -open3d.registration -------------------- - -.. currentmodule:: open3d - -.. autosummary:: - open3d.registration - -.. automodule:: open3d.registration - :members: - :undoc-members: diff --git a/docs/python_api/utility.rst b/docs/python_api/utility.rst deleted file mode 100644 index 37e91593287..00000000000 --- a/docs/python_api/utility.rst +++ /dev/null @@ -1,11 +0,0 @@ -open3d.utility --------------- - -.. currentmodule:: open3d - -.. autosummary:: - open3d.utility - -.. automodule:: open3d.utility - :members: - :undoc-members: diff --git a/docs/python_api/visualization.rst b/docs/python_api/visualization.rst deleted file mode 100644 index 06a3515179b..00000000000 --- a/docs/python_api/visualization.rst +++ /dev/null @@ -1,11 +0,0 @@ -open3d.visualization --------------------- - -.. currentmodule:: open3d - -.. autosummary:: - open3d.visualization - -.. automodule:: open3d.visualization - :members: - :undoc-members: diff --git a/src/Python/color_map/color_map.cpp b/src/Python/color_map/color_map.cpp index 5ff7f05228e..abbed81000f 100644 --- a/src/Python/color_map/color_map.cpp +++ b/src/Python/color_map/color_map.cpp @@ -36,8 +36,9 @@ using namespace open3d; void pybind_color_map_classes(py::module &m) { py::class_ - color_map_optimization_option(m, "ColorMapOptimizationOption", - "ColorMapOptimizationOption"); + color_map_optimization_option( + m, "ColorMapOptimizationOption", + "Defines options for color map optimization."); py::detail::bind_default_constructor( color_map_optimization_option); color_map_optimization_option diff --git a/src/Python/open3d_pybind.h b/src/Python/open3d_pybind.h index 12b9adcf8be..9f712829f69 100644 --- a/src/Python/open3d_pybind.h +++ b/src/Python/open3d_pybind.h @@ -26,8 +26,8 @@ #pragma once +#include // Include first to suppress compiler warnings #include -#include #include #include #include diff --git a/src/Python/registration/global_optimization.cpp b/src/Python/registration/global_optimization.cpp index 8563c67d321..1fd7ca2f429 100644 --- a/src/Python/registration/global_optimization.cpp +++ b/src/Python/registration/global_optimization.cpp @@ -71,8 +71,14 @@ void pybind_global_optimization(py::module &m) { }); // open3d.registration.PoseGraphNodeVector - py::bind_vector>( - m, "PoseGraphNodeVector"); + auto pose_graph_node_vector = + py::bind_vector>( + m, "PoseGraphNodeVector"); + pose_graph_node_vector.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Vector of PoseGraphNode"; + }), + py::none(), py::none(), ""); // open3d.registration.PoseGraphEdge py::class_>( - m, "PoseGraphEdgeVector"); + auto pose_graph_edge_vector = + py::bind_vector>( + m, "PoseGraphEdgeVector"); + pose_graph_edge_vector.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Vector of PoseGraphEdge"; + }), + py::none(), py::none(), ""); // open3d.registration.PoseGraph py::class_ criteria( m, "GlobalOptimizationConvergenceCriteria", - "GlobalOptimizationConvergenceCriteria"); + "Convergence criteria of GlobalOptimization."); py::detail::bind_default_constructor< registration::GlobalOptimizationConvergenceCriteria>(criteria); py::detail::bind_copy_functions< @@ -282,7 +294,7 @@ void pybind_global_optimization(py::module &m) { }); py::class_ option( - m, "GlobalOptimizationOption", "GlobalOptimizationOption"); + m, "GlobalOptimizationOption", "Option for GlobalOptimization."); py::detail::bind_default_constructor< registration::GlobalOptimizationOption>(option); py::detail::bind_copy_functions( diff --git a/src/Python/utility/eigen.cpp b/src/Python/utility/eigen.cpp index de7594f8b6c..38ec3a6b9d5 100644 --- a/src/Python/utility/eigen.cpp +++ b/src/Python/utility/eigen.cpp @@ -91,9 +91,11 @@ std::vector py_array_to_vectors_int( namespace { -template -void pybind_eigen_vector_of_scalar(py::module &m, - const std::string &bind_name) { +template , + typename holder_type = std::unique_ptr> +py::class_ pybind_eigen_vector_of_scalar( + py::module &m, const std::string &bind_name) { auto vec = py::bind_vector>(m, bind_name, py::buffer_protocol()); vec.def_buffer([](std::vector &v) -> py::buffer_info { @@ -116,13 +118,18 @@ void pybind_eigen_vector_of_scalar(py::module &m, // new (&v) std::vector(info.shape[0]); // memcpy(v.data(), info.ptr, sizeof(Scalar) * v.size()); //}); + return vec; } -template -void pybind_eigen_vector_of_vector(py::module &m, - const std::string &bind_name, - const std::string &repr_name, - InitFunc init_func) { +template , + typename holder_type = std::unique_ptr, + typename InitFunc> +py::class_ pybind_eigen_vector_of_vector( + py::module &m, + const std::string &bind_name, + const std::string &repr_name, + InitFunc init_func) { typedef typename EigenVector::Scalar Scalar; auto vec = py::bind_vector_without_repr>( m, bind_name, py::buffer_protocol()); @@ -147,13 +154,14 @@ void pybind_eigen_vector_of_vector(py::module &m, }); // py::detail must be after custom constructor - using Vector = std::vector; using Class_ = py::class_>; py::detail::vector_if_copy_constructible(vec); py::detail::vector_if_equal_operator(vec); py::detail::vector_modifiers(vec); py::detail::vector_accessor(vec); + return vec; + // Bare bones interface // We choose to disable them because they do not support slice indices // such as [:,:]. It is recommended to convert it to numpy.asarray() @@ -184,12 +192,15 @@ void pybind_eigen_vector_of_vector(py::module &m, //}); } -template -void pybind_eigen_vector_of_matrix(py::module &m, - const std::string &bind_name, - const std::string &repr_name) { +template , + typename Vector = std::vector, + typename holder_type = std::unique_ptr> +py::class_ pybind_eigen_vector_of_matrix( + py::module &m, + const std::string &bind_name, + const std::string &repr_name) { typedef typename EigenMatrix::Scalar Scalar; - typedef typename Eigen::aligned_allocator EigenAllocator; auto vec = py::bind_vector_without_repr< std::vector>(m, bind_name, py::buffer_protocol()); @@ -220,28 +231,118 @@ void pybind_eigen_vector_of_matrix(py::module &m, }); // py::detail must be after custom constructor - using Vector = std::vector; using Class_ = py::class_>; py::detail::vector_if_copy_constructible(vec); py::detail::vector_if_equal_operator(vec); py::detail::vector_modifiers(vec); py::detail::vector_accessor(vec); + + return vec; } } // unnamed namespace void pybind_eigen(py::module &m) { - pybind_eigen_vector_of_scalar(m, "IntVector"); - pybind_eigen_vector_of_scalar(m, "DoubleVector"); - pybind_eigen_vector_of_vector( + auto intvector = pybind_eigen_vector_of_scalar(m, "IntVector"); + intvector.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return R"(Convert int32 numpy array of shape ``(n,)`` to Open3D format.)"; + }), + py::none(), py::none(), ""); + + auto doublevector = + pybind_eigen_vector_of_scalar(m, "DoubleVector"); + doublevector.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return R"(Convert float64 numpy array of shape ``(n,)`` to Open3D format.)"; + }), + py::none(), py::none(), ""); + + auto vector3dvector = pybind_eigen_vector_of_vector( m, "Vector3dVector", "std::vector", py::py_array_to_vectors_double); - pybind_eigen_vector_of_vector( + vector3dvector.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return R"(Convert float64 numpy array of shape ``(n, 3)`` to Open3D format.. + +Example usage + +.. code-block:: python + + import open3d + import numpy as np + + pcd = open3d.geometry.PointCloud() + np_points = np.random.rand(100, 3) + + # From numpy to Open3D + pcd.points = open3d.utility.Vector3dVector(np_points) + + # From Open3D to numpy + np_points = np.asarray(pcd.points) +)"; + }), + py::none(), py::none(), ""); + + auto vector3ivector = pybind_eigen_vector_of_vector( m, "Vector3iVector", "std::vector", py::py_array_to_vectors_int); - pybind_eigen_vector_of_vector( + vector3ivector.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return R"(Convert int32 numpy array of shape ``(n, 3)`` to Open3D format.. + +Example usage + +.. code-block:: python + + import open3d + import numpy as np + + # Example mesh + # x, y coordinates: + # [0: (-1, 2)]__________[1: (1, 2)] + # \ /\ + # \ (0) / \ + # \ / (1)\ + # \ / \ + # [2: (0, 0)]\/________\[3: (2, 0)] + # + # z coordinate: 0 + + mesh = open3d.geometry.TriangleMesh() + np_vertices = np.array([[-1, 2, 0], + [1, 2, 0], + [0, 0, 0], + [2, 0, 0]]) + np_triangles = np.array([[0, 2, 1], + [1, 2, 3]]).astype(np.int32) + mesh.vertices = open3d.Vector3dVector(np_vertices) + + # From numpy to Open3D + mesh.triangles = open3d.Vector3iVector(np_triangles) + + # From Open3D to numpy + np_triangles = np.asarray(mesh.triangles) +)"; + }), + py::none(), py::none(), ""); + + auto vector2ivector = pybind_eigen_vector_of_vector( m, "Vector2iVector", "std::vector", py::py_array_to_vectors_int); - pybind_eigen_vector_of_matrix( + vector2ivector.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Convert int32 numpy array of shape ``(n, 2)`` to " + "Open3D format."; + }), + py::none(), py::none(), ""); + + auto matrix4dvector = pybind_eigen_vector_of_matrix( m, "Matrix4dVector", "std::vector"); + matrix4dvector.attr("__doc__") = docstring::static_property( + py::cpp_function([](py::handle arg) -> std::string { + return "Convert float64 numpy array of shape ``(n, 4, 4)`` to " + "Open3D format."; + }), + py::none(), py::none(), ""); } diff --git a/src/Python/visualization/renderoption.cpp b/src/Python/visualization/renderoption.cpp index da71570912c..d48dc35e750 100644 --- a/src/Python/visualization/renderoption.cpp +++ b/src/Python/visualization/renderoption.cpp @@ -36,7 +36,8 @@ void pybind_renderoption(py::module &m) { // open3d.visualization.RenderOption py::class_> - renderoption(m, "RenderOption"); + renderoption(m, "RenderOption", + "Defines rendering options for visulizer."); py::detail::bind_default_constructor( renderoption); renderoption diff --git a/src/Python/visualization/visualizer.cpp b/src/Python/visualization/visualizer.cpp index 898d5c1bebd..a87355373a9 100644 --- a/src/Python/visualization/visualizer.cpp +++ b/src/Python/visualization/visualizer.cpp @@ -55,7 +55,7 @@ static const std::unordered_map void pybind_visualizer(py::module &m) { py::class_, std::shared_ptr> - visualizer(m, "Visualizer", "Visualizer"); + visualizer(m, "Visualizer", "The main Visualizer class."); py::detail::bind_default_constructor(visualizer); visualizer .def("__repr__", @@ -121,7 +121,7 @@ void pybind_visualizer(py::module &m) { PyVisualizer, std::shared_ptr> visualizer_key(m, "VisualizerWithKeyCallback", visualizer, - "VisualizerWithKeyCallback"); + "Visualizer with custom key callack capabilities."); py::detail::bind_default_constructor< visualization::VisualizerWithKeyCallback>(visualizer_key); visualizer_key @@ -141,7 +141,7 @@ void pybind_visualizer(py::module &m) { PyVisualizer, std::shared_ptr> visualizer_edit(m, "VisualizerWithEditing", visualizer, - "VisualizerWithEditing"); + "Visualizer with editing capabilities."); py::detail::bind_default_constructor( visualizer_edit); visualizer_edit.def(py::init())