diff --git a/cpp/pybind/core/nns/nearest_neighbor_search.cpp b/cpp/pybind/core/nns/nearest_neighbor_search.cpp index ad8de540b93..6ed494767c0 100644 --- a/cpp/pybind/core/nns/nearest_neighbor_search.cpp +++ b/cpp/pybind/core/nns/nearest_neighbor_search.cpp @@ -21,10 +21,35 @@ namespace nns { void pybind_core_nns_declarations(py::module &m_nns) { py::class_> nns(m_nns, "NearestNeighborSearch", - "NearestNeighborSearch class for nearest neighbor search. " - "Construct a NearestNeighborSearch object with input " - "dataset_points of shape {n_dataset, d}."); + R"(NearestNeighborSearch class for nearest neighbor search. + +This class holds multiple index types to accelerate various nearest neighbor +search operations for a dataset of points with shape {n,d} with `n` as the number +of points and `d` as the dimension of the points. The class supports knn search, +fixed-radius search, multi-radius search, and hybrid search. + +Example: + The following example demonstrates how to perform knn search using the class:: + + import open3d as o3d + import numpy as np + + dataset = np.random.rand(10,3) + query_points = np.random.rand(5,3) + + nns = o3d.core.nns.NearestNeighborSearch(dataset) + + # initialize the knn_index before we can use knn_search + nns.knn_index() + + # perform knn search to get the 3 closest points with respect to the + # Euclidean distance. The returned distance is given as the squared + # distances + indices, squared_distances = nns.knn_search(query_points, knn=3) + + )"); } + void pybind_core_nns_definitions(py::module &m_nns) { auto nns = static_cast>>( @@ -35,7 +60,14 @@ void pybind_core_nns_definitions(py::module &m_nns) { // Index functions. nns.def("knn_index", &NearestNeighborSearch::KnnIndex, - "Set index for knn search."); + R"(Initialize the index for knn search. + +This function needs to be called once before performing search operations. + +Returns: + True on success. + )"); + nns.def( "fixed_radius_index", [](NearestNeighborSearch &self, utility::optional radius) { @@ -45,9 +77,28 @@ void pybind_core_nns_definitions(py::module &m_nns) { return self.FixedRadiusIndex(radius.value()); } }, - py::arg("radius") = py::none()); + py::arg("radius") = py::none(), + R"(Initialize the index for fixed-radius search. + +This function needs to be called once before performing search operations. + +Args: + radius (float, optional): Radius value for fixed-radius search. Required + for GPU fixed radius index. + +Returns: + True on success. + )"); + nns.def("multi_radius_index", &NearestNeighborSearch::MultiRadiusIndex, - "Set index for multi-radius search."); + R"(Initialize the index for multi-radius search. + +This function needs to be called once before performing search operations. + +Returns: + True on success. + )"); + nns.def( "hybrid_index", [](NearestNeighborSearch &self, utility::optional radius) { @@ -57,11 +108,57 @@ void pybind_core_nns_definitions(py::module &m_nns) { return self.HybridIndex(radius.value()); } }, - py::arg("radius") = py::none()); + py::arg("radius") = py::none(), + R"(Initialize the index for hybrid search. + +This function needs to be called once before performing search operations. + +Args: + radius (float, optional): Radius value for hybrid search. Required + for GPU hybrid index. + +Returns: + True on success. + )"); // Search functions. nns.def("knn_search", &NearestNeighborSearch::KnnSearch, "query_points"_a, - "knn"_a, "Perform knn search."); + "knn"_a, + R"(Perform knn search. + +Note: + To use knn_search initialize the index using knn_index before calling this function. + +Args: + query_points (open3d.core.Tensor): Query points with shape {n, d}. + knn (int): Number of neighbors to search per query point. + +Example: + The following searches the 3 nearest neighbors for random dataset and query points:: + + import open3d as o3d + import numpy as np + + dataset = np.random.rand(10,3) + query_points = np.random.rand(5,3) + + nns = o3d.core.nns.NearestNeighborSearch(dataset) + + # initialize the knn_index before we can use knn_search + nns.knn_index() + + # perform knn search to get the 3 closest points with respect to the + # Euclidean distance. The returned distance is given as the squared + # distances + indices, squared_distances = nns.knn_search(query_points, knn=3) + +Returns: + Tuple of Tensors (indices, squared_distances). + - indices: Tensor of shape {n, knn}. + - squared_distances: Tensor of shape {n, knn}. The distances are squared L2 distances. + + )"); + nns.def( "fixed_radius_search", [](NearestNeighborSearch &self, Tensor query_points, double radius, @@ -74,38 +171,141 @@ void pybind_core_nns_definitions(py::module &m_nns) { } }, py::arg("query_points"), py::arg("radius"), - py::arg("sort") = py::none()); + py::arg("sort") = py::none(), + R"(Perform fixed-radius search. + +Note: + To use fixed_radius_search initialize the index using fixed_radius_index before calling this function. + +Args: + query_points (open3d.core.Tensor): Query points with shape {n, d}. + radius (float): Radius value for fixed-radius search. Note that this + parameter can differ from the radius used to initialize the index + for convenience, which may cause the index to be rebuilt for GPU + devices. + sort (bool, optional): Sort the results by distance. Default is True. + +Returns: + Tuple of Tensors (indices, splits, distances). + - indices: The indices of the neighbors. + - distances: The squared L2 distances. + - splits: The splits of the indices and distances defining the start + and exclusive end of each query point's neighbors. The shape is {num_queries+1} + +Example: + The following searches the neighbors within a radius of 1.0 a set of data and query points:: + + # define data and query points + points = np.array([ + [0.1,0.1,0.1], + [0.9,0.9,0.9], + [0.5,0.5,0.5], + [1.7,1.7,1.7], + [1.8,1.8,1.8], + [0.3,2.4,1.4]], dtype=np.float32) + + queries = np.array([ + [1.0,1.0,1.0], + [0.5,2.0,2.0], + [0.5,2.1,2.1], + [100,100,100], + ], dtype=np.float32) + + nns = o3d.core.nns.NearestNeighborSearch(points) + nns.fixed_radius_index(radius=1.0) + + neighbors_index, neighbors_distance, neighbors_splits = nns.fixed_radius_search(queries, radius=1.0, sort=True) + # returns neighbors_index = [1, 2, 5, 5] + # neighbors_distance = [0.03 0.75 0.56000006 0.62] + # neighbors_splits = [0, 2, 3, 4, 4] + + for i, (start_i, end_i) in enumerate(zip(neighbors_splits, neighbors_splits[1:])): + start_i = start_i.item() + end_i = end_i.item() + print(f"query_point {i} has the neighbors {neighbors_index[start_i:end_i].numpy()} " + f"with squared distances {neighbors_distance[start_i:end_i].numpy()}") + )"); + nns.def("multi_radius_search", &NearestNeighborSearch::MultiRadiusSearch, "query_points"_a, "radii"_a, - "Perform multi-radius search. Each query point has an independent " - "radius."); + R"(Perform multi-radius search. Each query point has an independent radius. + +Note: + To use multi_radius_search initialize the index using multi_radius_index before calling this function. + +Args: + query_points (open3d.core.Tensor): Query points with shape {n, d}. + radii (open3d.core.Tensor): Radii of query points. Each query point has one radius. + +Returns: + Tuple of Tensors (indices, splits, distances). + - indices: The indices of the neighbors. + - distances: The squared L2 distances. + - splits: The splits of the indices and distances defining the start + and exclusive end of each query point's neighbors. The shape is {num_queries+1} + +Example: + The following searches the neighbors with an individual radius for each query point:: + + # define data, query points and radii + points = np.array([ + [0.1,0.1,0.1], + [0.9,0.9,0.9], + [0.5,0.5,0.5], + [1.7,1.7,1.7], + [1.8,1.8,1.8], + [0.3,2.4,1.4]], dtype=np.float32) + + queries = np.array([ + [1.0,1.0,1.0], + [0.5,2.0,2.0], + [0.5,2.1,2.1], + [100,100,100], + ], dtype=np.float32) + + radii = np.array([0.5, 1.0, 1.5, 2], dtype=np.float32) + + nns = o3d.core.nns.NearestNeighborSearch(points) + + nns.multi_radius_index() + + neighbors_index, neighbors_distance, neighbors_splits = nns.multi_radius_search(queries, radii) + # returns neighbors_index = [1 5 5 3 4] + # neighbors_distance = [0.03 0.56 0.62 1.76 1.87] + # neighbors_splits = [0 1 2 5 5] + + + for i, (start_i, end_i) in enumerate(zip(neighbors_splits, neighbors_splits[1:])): + start_i = start_i.item() + end_i = end_i.item() + print(f"query_point {i} has the neighbors {neighbors_index[start_i:end_i].numpy()} " + f"with squared distances {neighbors_distance[start_i:end_i].numpy()}") + )"); + nns.def("hybrid_search", &NearestNeighborSearch::HybridSearch, "query_points"_a, "radius"_a, "max_knn"_a, - "Perform hybrid search."); - - // Docstrings. - static const std::unordered_map - map_nearest_neighbor_search_method_docs = { - {"query_points", "The query tensor of shape {n_query, d}."}, - {"radii", - "Tensor of shape {n_query,} containing multiple radii, " - "one for each query point."}, - {"radius", "Radius value for radius search."}, - {"max_knn", - "Maximum number of neighbors to search per query point."}, - {"knn", "Number of neighbors to search per query point."}}; - docstring::ClassMethodDocInject(m_nns, "NearestNeighborSearch", - "knn_search", - map_nearest_neighbor_search_method_docs); - docstring::ClassMethodDocInject(m_nns, "NearestNeighborSearch", - "multi_radius_search", - map_nearest_neighbor_search_method_docs); - docstring::ClassMethodDocInject(m_nns, "NearestNeighborSearch", - "fixed_radius_search", - map_nearest_neighbor_search_method_docs); - docstring::ClassMethodDocInject(m_nns, "NearestNeighborSearch", - "hybrid_search", - map_nearest_neighbor_search_method_docs); + R"(Perform hybrid search. + +Hybrid search behaves similarly to fixed-radius search, but with a maximum number of neighbors to search per query point. + +Note: + To use hybrid_search initialize the index using hybrid_index before calling this function. + +Args: + query_points (open3d.core.Tensor): Query points with shape {n, d}. + radius (float): Radius value for hybrid search. + max_knn (int): Maximum number of neighbor to search per query. + +Returns: + Tuple of Tensors (indices, distances, counts) + - indices: The indices of the neighbors with shape {n, max_knn}. + If there are less than max_knn neighbors within the radius then the + last entries are padded with -1. + - distances: The squared L2 distances with shape {n, max_knn}. + If there are less than max_knn neighbors within the radius then the + last entries are padded with 0. + - counts: Counts of neighbour for each query points with shape {n}. + )"); } } // namespace nns diff --git a/cpp/pybind/t/geometry/pointcloud.cpp b/cpp/pybind/t/geometry/pointcloud.cpp index 10a5735389c..0aecc64aa44 100644 --- a/cpp/pybind/t/geometry/pointcloud.cpp +++ b/cpp/pybind/t/geometry/pointcloud.cpp @@ -769,17 +769,29 @@ the partition id for each point. )"); - pointcloud.def( - "compute_metrics", &PointCloud::ComputeMetrics, "pcd2"_a, - "metrics"_a, "params"_a, - R"(Compute various metrics between two point clouds. Currently, Chamfer distance, Hausdorff distance and F-Score [[Knapitsch2017]] are supported. The Chamfer distance is the sum of the mean distance to the nearest neighbor from the points of the first point cloud to the second point cloud. The F-Score at a fixed threshold radius is the harmonic mean of the Precision and Recall. Recall is the percentage of surface points from the first point cloud that have the second point cloud points within the threshold radius, while Precision is the percentage of points from the second point cloud that have the first point cloud points within the threhold radius. - - .. math: - \text{Chamfer Distance: } d_{CD}(X,Y) = \frac{1}{|X|}\sum_{i \in X} || x_i - n(x_i, Y) || + \frac{1}{|Y|}\sum_{i \in Y} || y_i - n(y_i, X) ||\\ - \text{Hausdorff distance: } d_H(X,Y) = \max \left{ \max_{i \in X} || x_i - n(x_i, Y) ||, \max_{i \in Y} || y_i - n(y_i, X) || \right}\\ - \text{Precision: } P(X,Y|d) = \frac{100}{|X|} \sum_{i \in X} || x_i - n(x_i, Y) || < d \\ - \text{Recall: } R(X,Y|d) = \frac{100}{|Y|} \sum_{i \in Y} || y_i - n(y_i, X) || < d \\ - \text{F-Score: } F(X,Y|d) = \frac{2 P(X,Y|d) R(X,Y|d)}{P(X,Y|d) + R(X,Y|d)} \\ + pointcloud.def("compute_metrics", &PointCloud::ComputeMetrics, "pcd2"_a, + "metrics"_a, "params"_a, + R"(Compute various metrics between two point clouds. + +Currently, Chamfer distance, Hausdorff distance and F-Score `[Knapitsch2017] <../tutorial/reference.html#Knapitsch2017>`_ are supported. +The Chamfer distance is the sum of the mean distance to the nearest neighbor +from the points of the first point cloud to the second point cloud. The F-Score +at a fixed threshold radius is the harmonic mean of the Precision and Recall. +Recall is the percentage of surface points from the first point cloud that have +the second point cloud points within the threshold radius, while Precision is +the percentage of points from the second point cloud that have the first point +cloud points within the threhold radius. + +.. math:: + :nowrap: + + \begin{align} + \text{Chamfer Distance: } d_{CD}(X,Y) &= \frac{1}{|X|}\sum_{i \in X} || x_i - n(x_i, Y) || + \frac{1}{|Y|}\sum_{i \in Y} || y_i - n(y_i, X) ||\\ + \text{Hausdorff distance: } d_H(X,Y) &= \max \left\{ \max_{i \in X} || x_i - n(x_i, Y) ||, \max_{i \in Y} || y_i - n(y_i, X) || \right\}\\ + \text{Precision: } P(X,Y|d) &= \frac{100}{|X|} \sum_{i \in X} || x_i - n(x_i, Y) || < d \\ + \text{Recall: } R(X,Y|d) &= \frac{100}{|Y|} \sum_{i \in Y} || y_i - n(y_i, X) || < d \\ + \text{F-Score: } F(X,Y|d) &= \frac{2 P(X,Y|d) R(X,Y|d)}{P(X,Y|d) + R(X,Y|d)} \\ + \end{align} Args: pcd2 (t.geometry.PointCloud): Other point cloud to compare with. diff --git a/cpp/pybind/t/geometry/trianglemesh.cpp b/cpp/pybind/t/geometry/trianglemesh.cpp index b6a27b0a102..3f56bb52d8b 100644 --- a/cpp/pybind/t/geometry/trianglemesh.cpp +++ b/cpp/pybind/t/geometry/trianglemesh.cpp @@ -261,13 +261,14 @@ shared storage on the CPU (GPU resident images for textures is not yet supported Dictionary of names to triangle meshes. Example: + Converting the FlightHelmetModel to a dictionary of triangle meshes:: - flight_helmet = o3d.data.FlightHelmetModel() - model = o3d.io.read_triangle_model(flight_helmet.path) - mesh_dict = o3d.t.geometry.TriangleMesh.from_triangle_mesh_model(model) - o3d.visualization.draw(list({"name": name, "geometry": tmesh} for - (name, tmesh) in mesh_dict.items())) - )"); + flight_helmet = o3d.data.FlightHelmetModel() + model = o3d.io.read_triangle_model(flight_helmet.path) + mesh_dict = o3d.t.geometry.TriangleMesh.from_triangle_mesh_model(model) + o3d.visualization.draw(list({"name": name, "geometry": tmesh} for + (name, tmesh) in mesh_dict.items())) +)"); // conversion triangle_mesh.def("to_legacy", &TriangleMesh::ToLegacy, "Convert to a legacy Open3D TriangleMesh."); @@ -773,8 +774,7 @@ This function always uses the CPU device. Input meshes must be manifold for this method to work. The algorithm is based on: -Zhou et al, "Iso-charts: Stretch-driven Mesh Parameterization using Spectral - Analysis", Eurographics Symposium on Geometry Processing (2004) +Zhou et al, "Iso-charts: Stretch-driven Mesh Parameterization using Spectral Analysis", Eurographics Symposium on Geometry Processing (2004) Sander et al. "Signal-Specialized Parametrization" Europgraphics 2002 This function always uses the CPU device. @@ -1112,23 +1112,37 @@ Example:: )"); - triangle_mesh.def( - "compute_metrics", &TriangleMesh::ComputeMetrics, "mesh2"_a, - "metrics"_a, "params"_a, - R"(Compute various metrics between two triangle meshes. This uses ray casting for distance computations between a sampled point cloud and a triangle mesh. Currently, Chamfer distance, Hausdorff distance and F-Score [\\[Knapitsch2017\\]](../tutorial/reference.html#Knapitsch2017) are supported. The Chamfer distance is the sum of the mean distance to the nearest neighbor from the sampled surface points of the first mesh to the second mesh and vice versa. The F-Score at the fixed threshold radius is the harmonic mean of the Precision and Recall. Recall is the percentage of surface points from the first mesh that have the second mesh within the threshold radius, while Precision is the percentage of sampled points from the second mesh that have the first mesh surface within the threhold radius. - - .. math:: - \text{Chamfer Distance: } d_{CD}(X,Y) = \frac{1}{|X|}\sum_{i \in X} || x_i - n(x_i, Y) || + \frac{1}{|Y|}\sum_{i \in Y} || y_i - n(y_i, X) ||\\ - \text{Hausdorff distance: } d_H(X,Y) = \max \left{ \max_{i \in X} || x_i - n(x_i, Y) ||, \max_{i \in Y} || y_i - n(y_i, X) || \right}\\ - \text{Precision: } P(X,Y|d) = \frac{100}{|X|} \sum_{i \in X} || x_i - n(x_i, Y) || < d \\ - \text{Recall: } R(X,Y|d) = \frac{100}{|Y|} \sum_{i \in Y} || y_i - n(y_i, X) || < d \\ - \text{F-Score: } F(X,Y|d) = \frac{2 P(X,Y|d) R(X,Y|d)}{P(X,Y|d) + R(X,Y|d)} \\ + triangle_mesh.def("compute_metrics", &TriangleMesh::ComputeMetrics, + "mesh2"_a, "metrics"_a, "params"_a, + R"(Compute various metrics between two triangle meshes. + +This uses ray casting for distance computations between a sampled point cloud +and a triangle mesh. Currently, Chamfer distance, Hausdorff distance and +F-Score `[Knapitsch2017] <../tutorial/reference.html#Knapitsch2017>`_ are supported. +The Chamfer distance is the sum of the mean distance to the nearest neighbor from +the sampled surface points of the first mesh to the second mesh and vice versa. +The F-Score at the fixed threshold radius is the harmonic mean of the Precision +and Recall. Recall is the percentage of surface points from the first mesh that +have the second mesh within the threshold radius, while Precision is the +percentage of sampled points from the second mesh that have the first mesh +surface within the threhold radius. + +.. math:: + :nowrap: + + \begin{align} + \text{Chamfer Distance: } d_{CD}(X,Y) &= \frac{1}{|X|}\sum_{i \in X} || x_i - n(x_i, Y) || + \frac{1}{|Y|}\sum_{i \in Y} || y_i - n(y_i, X) ||\\ + \text{Hausdorff distance: } d_H(X,Y) &= \max \left\{ \max_{i \in X} || x_i - n(x_i, Y) ||, \max_{i \in Y} || y_i - n(y_i, X) || \right\}\\ + \text{Precision: } P(X,Y|d) &= \frac{100}{|X|} \sum_{i \in X} || x_i - n(x_i, Y) || < d \\ + \text{Recall: } R(X,Y|d) &= \frac{100}{|Y|} \sum_{i \in Y} || y_i - n(y_i, X) || < d \\ + \text{F-Score: } F(X,Y|d) &= \frac{2 P(X,Y|d) R(X,Y|d)}{P(X,Y|d) + R(X,Y|d)} \\ + \end{align} As a side effect, the triangle areas are saved in the "areas" attribute. Args: mesh2 (t.geometry.TriangleMesh): Other triangle mesh to compare with. - metrics (Sequence[t.geometry.Metric]): List of Metric s to compute. Multiple metrics can be computed at once for efficiency. + metrics (Sequence[t.geometry.Metric]): List of Metrics to compute. Multiple metrics can be computed at once for efficiency. params (t.geometry.MetricParameters): This holds parameters required by different metrics. Returns: diff --git a/docs/_static/contribute/example_python_docstring.webp b/docs/_static/contribute/example_python_docstring.webp new file mode 100644 index 00000000000..746d50e84c7 Binary files /dev/null and b/docs/_static/contribute/example_python_docstring.webp differ diff --git a/docs/conf.py b/docs/conf.py index 867fcae1423..eac752217c7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -67,6 +67,8 @@ def get_git_short_hash(): "sphinx.ext.autosummary", "sphinx.ext.napoleon", "sphinx.ext.todo", + "sphinx_tabs.tabs", + "sphinx_copybutton", "nbsphinx", "m2r2", ] diff --git a/docs/contribute/contribution_recipes.rst b/docs/contribute/contribution_recipes.rst index eee2b7ca3c8..454aff99e25 100644 --- a/docs/contribute/contribution_recipes.rst +++ b/docs/contribute/contribution_recipes.rst @@ -223,30 +223,21 @@ Case 2: When documenting Python bindings ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * First, complete the Python binding code following the guides from `Pybind11 Docs `_. Make sure to write the high-level docstrings for the classes and functions. Also use ``"param_name"_a`` to denote function parameters. Use standard RST based docstring syntax (`Google style `__) as explained `here `_ and `there `_. -* Use the ``docstring::ClassMethodDocInject()`` or ``docstring::FunctionDocInject()`` to insert parameter docs. -* Example binding and docstrings for the ``Calculator`` class: - -.. code:: cpp - - py::class_ calculator( - m, "Calculator", - "Calculator class performs numerical computations."); - calculator.def("add", &Calculator::Add, - "Performs ``a`` plus ``b``, i.e. :math:`c=a+b` Unlike " - ":math:`open3d.Calculator.sub`, " - ":math:`open3d.Calculator.add` is " - "commutative.", - "a"_a, "b"_a); - calculator.def("sub", &Calculator::Add, "Subtracts ``b`` from ``a``," - " i.e. :math:`c=a-b`", - "a"_a, - "b"_a); - docstring::ClassMethodDocInject(m, "Calculator", "add", - {{"a", "LHS operand for summation."}, - {"b", "RHS operand for summation."}}); - docstring::ClassMethodDocInject(m, "Calculator", "sub", - {{"a", "LHS operand for subtraction."}, - {"b", "RHS operand for subtraction."}}); +* Writing docstrings that render as expected can be tricky. The following example docstring points out some of the pitfalls to watch out for: + +.. tabs:: + + .. tab:: Docstring example + + .. literalinclude:: ./example_python_docstring.rst + :language: rest + + .. tab:: Rendered result + + .. image:: ../../_static/contribute/example_python_docstring.webp + :width: 800 + :alt: Rendered docstring + Case 3: When documenting pure Python code (no bindings) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/contribute/example_python_docstring.rst b/docs/contribute/example_python_docstring.rst new file mode 100644 index 00000000000..778477a9142 --- /dev/null +++ b/docs/contribute/example_python_docstring.rst @@ -0,0 +1,87 @@ +Single line short description: Example docstring for Open3D. + +Use the ``Args``, ``Returns``, ``Example``, ``Note`` sections to describe classes +or functions as needed. +Giving a code example showing how to use a class or function in the ``Example`` +section is highly recommended. + +To explain what a method does you can use code and math. +This is a literal block started with ``::`` followed by a blank line to show code:: + + pip install open3d + +This is an inline equation :math:`e^{i\pi} + 1 = 0` and this is a display equation +(don't forget the blank line before the ``..math::`` directive): + +.. math:: + e^{i\pi} + 1 = 0. + + +The default alignment for multiple equations is right-align, which is often not +desired. +To align multiple equations manually use the ``align`` environment with ``:nowrap:`` +**(don't forget to add a blank line after :nowrap: to make this work!)** + +.. math:: + :nowrap: + + \begin{align} + x &= r \sin(\theta) \cos(\phi) \\ + y &= r \sin(\theta) \sin(\phi) \\ + z &= r \cos(\theta). + \end{align} + + +Note: + You can use inline markup to format your documentation. + + - *italics* for emphasis + - **boldface** for strong emphasis + - ``code`` for inline code + + A list must be separated with blank lines to work. + + - This is a list + - Second item + + - A nested list must be .. + - separated with blank lines too + - **Use only two spaces to indent or it will be treated as a block quote.** + + - Third item + + This is a link `The space before the pointy left bracket is important! `_ + This does not work, `you forgot the space`_ + + +Args: + param1 (o3d.core.Tensor): Specify the shape with round brackets, (N,3), and + the dtype, Float32, as necessary for Tensor parameters + + param2 (str): Another parameter + +Returns: + Returned values can be tuples, lists or dictionaries sometimes. + Describing every item can be done with lists + + - o3d.core.Tensor: vertices with shape (N,3) and type Float32. + - int: the number of vertices. This is N. + + or definition lists, which is recommended if the returned value is a dictionary + + vertices (o3d.core.Tensor) [term must be a single line] + Description, first paragraph. + A tensor with the vertices of shape (N,3) and type Float32. + + Description, second paragraph. + The tensor has the same shape as param1. + + count (int) + Description. + The number of vertices. This is N. + + +Example: + This is a code example showing how Open3D is imported:: + + import open3d as o3d \ No newline at end of file diff --git a/docs/make_docs.py b/docs/make_docs.py index bb8ebfbacb0..7f691afd81c 100644 --- a/docs/make_docs.py +++ b/docs/make_docs.py @@ -88,7 +88,7 @@ def _get_documented_module_names(): with open("documented_modules.txt", "r") as f: for line in f: print(line, end="") - m = re.match("^(open3d\..*)\s*$", line) + m = re.match(r"^(open3d\..*)\s*$", line) if m: module_names.append(m.group(1)) print("Documented modules:") diff --git a/docs/requirements.txt b/docs/requirements.txt index 144653773f8..d52283a5db3 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -5,6 +5,8 @@ m2r2==0.3.3.post2 matplotlib==3.7.3 nbsphinx==0.9.3 sphinx==7.1.2 +sphinx-tabs==3.4.7 +sphinx-copybutton==0.5.2 nbconvert==6.5.4 lxml==5.2.1 lxml_html_clean==0.4.0 diff --git a/python/open3d/visualization/tensorboard_plugin/summary.py b/python/open3d/visualization/tensorboard_plugin/summary.py index 4a02e8e4cdc..8e35948b5be 100644 --- a/python/open3d/visualization/tensorboard_plugin/summary.py +++ b/python/open3d/visualization/tensorboard_plugin/summary.py @@ -575,6 +575,7 @@ def add_3d(name, data (dict): A dictionary of tensors representing 3D data. Tensorflow, PyTorch, Numpy and Open3D tensors are supported. The following keys are supported: + - ``vertex_positions``: shape `(B, N, 3)` where B is the number of point clouds and must be same for each key. N is the number of 3D points. Will be cast to ``float32``.