Skip to content

Commit

Permalink
add three python examples
Browse files Browse the repository at this point in the history
  • Loading branch information
LiangliangNan committed Dec 26, 2024
1 parent 35ae631 commit b0ca682
Show file tree
Hide file tree
Showing 9 changed files with 369 additions and 47 deletions.
49 changes: 46 additions & 3 deletions python/bindings/easy3d/core/point_cloud.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include <easy3d/core/point_cloud.h>
#include <easy3d/core/property.h>
#include <easy3d/core/vec.h>

#include <memory>
#include <typeinfo>
Expand Down Expand Up @@ -103,6 +102,48 @@ void bind_easy3d_core_point_cloud(pybind11::module_& m)
}
}, "Add multiple points to the PointCloud from a NumPy array with shape (n, 3)");

cl.def("add_point", (struct easy3d::PointCloud::Vertex (easy3d::PointCloud::*)(const easy3d::vec3 &)) &easy3d::PointCloud::add_vertex, "add a new vertex with position \n\nC++: easy3d::PointCloud::add_vertex(const easy3d::vec3 &) --> struct easy3d::PointCloud::Vertex", pybind11::arg("p"));

cl.def("add_point",
[](easy3d::PointCloud &pc, pybind11::object point) {
if (pybind11::isinstance<easy3d::vec3>(point)) {
// If the input is an easy3d::vec3
pc.add_vertex(point.cast<easy3d::vec3>());
} else if (pybind11::isinstance<pybind11::iterable>(point)) {
// Convert iterable (like list) to NumPy array
auto iterable = point.cast<pybind11::iterable>();
std::vector<float> values;
for (auto item : iterable) {
values.push_back(item.cast<float>());
}

if (values.size() != 3) {
throw std::runtime_error("Iterable must contain exactly 3 elements.");
}

easy3d::vec3 v(values[0], values[1], values[2]);
pc.add_vertex(v);
} else if (pybind11::isinstance<pybind11::array_t<float>>(point)) {
// If the input is a NumPy array
pybind11::array_t<float> array = point.cast<pybind11::array_t<float>>();

// Check array shape
if (array.ndim() != 1 || array.shape(0) != 3) {
throw std::runtime_error("NumPy array must be a 1D array with 3 elements.");
}

auto buf = array.unchecked<1>(); // Access data buffer
easy3d::vec3 v(buf(0), buf(1), buf(2));
pc.add_vertex(v);
} else {
throw std::runtime_error(
"Input must be of type easy3d::vec3, a NumPy array with 3 elements, or a list/iterable with 3 elements."
);
}
},
"Adds a single point to the point cloud. Input can be easy3d::vec3, a NumPy array, or a list with 3 elements.",
pybind11::arg("point"));

// Retrieve all points as a NumPy array
cl.def("to_numpy", [](const easy3d::PointCloud &pc) {
// Create a NumPy array with shape (n_points, 3)
Expand All @@ -124,7 +165,6 @@ void bind_easy3d_core_point_cloud(pybind11::module_& m)
cl.def("__iadd__", (class easy3d::PointCloud & (easy3d::PointCloud::*)(const class easy3d::PointCloud &)) &easy3d::PointCloud::operator+=, "Merges another point cloud into the current one.\n Shifts the indices of vertices of the other point cloud by `number_of_vertices() + number_of_removed_vertices()`.\n Copies entries of all property maps which have the same name in both point clouds. That is, property maps which\n are only in `other` are ignored.\n Also copies elements which are marked as removed, and concatenates the freelists of both point clouds.\n\nC++: easy3d::PointCloud::operator+=(const class easy3d::PointCloud &) --> class easy3d::PointCloud &", pybind11::return_value_policy::automatic, pybind11::arg("other"));
cl.def("join", (class easy3d::PointCloud & (easy3d::PointCloud::*)(const class easy3d::PointCloud &)) &easy3d::PointCloud::join, "Merges another point cloud into the current one.\n Shifts the indices of vertices of the other point cloud by `number_of_vertices() + number_of_removed_vertices()`.\n Copies entries of all property maps which have the same name in both point cloud. That is, property maps which\n are only in `other` are ignored.\n Also copies elements which are marked as removed, and concatenates the freelists of both point cloud.\n\nC++: easy3d::PointCloud::join(const class easy3d::PointCloud &) --> class easy3d::PointCloud &", pybind11::return_value_policy::automatic, pybind11::arg("other"));
cl.def("assign", (class easy3d::PointCloud & (easy3d::PointCloud::*)(const class easy3d::PointCloud &)) &easy3d::PointCloud::assign, "assign to does not copy custom properties.\n\nC++: easy3d::PointCloud::assign(const class easy3d::PointCloud &) --> class easy3d::PointCloud &", pybind11::return_value_policy::automatic, pybind11::arg("rhs"));
cl.def("add_vertex", (struct easy3d::PointCloud::Vertex (easy3d::PointCloud::*)(const easy3d::vec3 &)) &easy3d::PointCloud::add_vertex, "add a new vertex with position \n\nC++: easy3d::PointCloud::add_vertex(const easy3d::vec3 &) --> struct easy3d::PointCloud::Vertex", pybind11::arg("p"));
cl.def("vertices_size", (unsigned int (easy3d::PointCloud::*)() const) &easy3d::PointCloud::vertices_size, "returns number of (deleted and valid) vertices in the cloud\n\nC++: easy3d::PointCloud::vertices_size() const --> unsigned int");
cl.def("n_vertices", (unsigned int (easy3d::PointCloud::*)() const) &easy3d::PointCloud::n_vertices, "returns number of vertices in the cloud\n\nC++: easy3d::PointCloud::n_vertices() const --> unsigned int");
cl.def("clear", (void (easy3d::PointCloud::*)()) &easy3d::PointCloud::clear, "clear cloud: remove all vertices\n\nC++: easy3d::PointCloud::clear() --> void");
Expand All @@ -150,7 +190,10 @@ void bind_easy3d_core_point_cloud(pybind11::module_& m)
cl.def("points", (class std::vector<easy3d::vec3 > & (easy3d::PointCloud::*)()) &easy3d::PointCloud::points, "vector of vertex positions\n\nC++: easy3d::PointCloud::points() --> class std::vector<easy3d::vec3 > &", pybind11::return_value_policy::automatic);
cl.def("points", (const class std::vector<easy3d::vec3 > & (easy3d::PointCloud::*)() const) &easy3d::PointCloud::points, "vector of vertex positions\n\nC++: easy3d::PointCloud::points() --> class std::vector<easy3d::vec3 > &", pybind11::return_value_policy::automatic);

{ // easy3d::PointCloud::BaseHandle file:easy3d/core/point_cloud.h line:52
cl.def("name", [](easy3d::PointCloud& self) { return self.name(); }, pybind11::return_value_policy::copy, "Get the name of the point cloud.");
cl.def("set_name", [](easy3d::PointCloud& self, const std::string& name) { self.set_name(name); }, "Set the name of the point cloud.");

{ // easy3d::PointCloud::BaseHandle file:easy3d/core/point_cloud.h line:52
auto & enclosing_class = cl;
pybind11::class_<easy3d::PointCloud::BaseHandle, std::shared_ptr<easy3d::PointCloud::BaseHandle>> cl(enclosing_class, "BaseHandle", "Base class for topology types (internally it is basically an index)\n \n\n Vertex");
cl.def( pybind11::init( [](){ return new easy3d::PointCloud::BaseHandle(); } ), "doc" );
Expand Down
17 changes: 16 additions & 1 deletion python/bindings/easy3d/core/vec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ void bind_easy3d_core_vec(pybind11::module_& m)
cl.def( pybind11::init<const float &>(), pybind11::arg("s") );

cl.def( pybind11::init( [](easy3d::vec2 const &o){ return new easy3d::vec2(o); } ) );
cl.def("length2", (float (easy3d::vec2::*)() const) &easy3d::vec2::length2, "C++: easy3d::vec2::length2() const --> float");

cl.def("to_numpy", [](const easy3d::vec2 &v) {
return pybind11::array_t<float>({2}, {sizeof(float)}, &v.x);
}, "Converts the vector to a NumPy array.");

cl.def("length2", (float (easy3d::vec2::*)() const) &easy3d::vec2::length2, "C++: easy3d::vec2::length2() const --> float");
cl.def("length", (float (easy3d::vec2::*)() const) &easy3d::vec2::length, "C++: easy3d::vec2::length() const --> float");
cl.def("norm", (float (easy3d::vec2::*)() const) &easy3d::vec2::norm, "C++: easy3d::vec2::norm() const --> float");
cl.def("distance2", (float (easy3d::vec2::*)(const easy3d::vec2 &) const) &easy3d::vec2::distance2, "C++: easy3d::vec2::distance2(const easy3d::vec2 &) const --> float", pybind11::arg("rhs"));
Expand Down Expand Up @@ -76,6 +81,11 @@ void bind_easy3d_core_vec(pybind11::module_& m)
cl.def( pybind11::init<const float &>(), pybind11::arg("s") );

cl.def( pybind11::init( [](easy3d::vec3 const &o){ return new easy3d::vec3(o); } ) );

cl.def("to_numpy", [](const easy3d::vec3 &v) {
return pybind11::array_t<float>({3}, {sizeof(float)}, &v.x);
}, "Converts the vector to a NumPy array.");

cl.def("__imul__", (easy3d::vec3 & (easy3d::vec3::*)(float)) &easy3d::vec3::operator*=<float>, "C++: easy3d::vec3::operator*=(float) --> easy3d::vec3 &", pybind11::return_value_policy::automatic, pybind11::arg("s"));
cl.def("__mul__", (easy3d::vec3 (easy3d::vec3::*)(float) const) &easy3d::vec3::operator*<float>, "C++: easy3d::vec3::operator*(float) const --> easy3d::vec3", pybind11::arg("s"));
cl.def("length2", (float (easy3d::vec3::*)() const) &easy3d::vec3::length2, "C++: easy3d::vec3::length2() const --> float");
Expand Down Expand Up @@ -131,6 +141,11 @@ void bind_easy3d_core_vec(pybind11::module_& m)
cl.def( pybind11::init<const float &>(), pybind11::arg("s") );

cl.def( pybind11::init( [](easy3d::vec4 const &o){ return new easy3d::vec4(o); } ) );

cl.def("to_numpy", [](const easy3d::vec4 &v) {
return pybind11::array_t<float>({4}, {sizeof(float)}, &v.x);
}, "Converts the vector to a NumPy array.");

cl.def("length2", (float (easy3d::vec4::*)() const) &easy3d::vec4::length2, "C++: easy3d::vec4::length2() const --> float");
cl.def("length", (float (easy3d::vec4::*)() const) &easy3d::vec4::length, "C++: easy3d::vec4::length() const --> float");
cl.def("norm", (float (easy3d::vec4::*)() const) &easy3d::vec4::norm, "C++: easy3d::vec4::norm() const --> float");
Expand Down
72 changes: 72 additions & 0 deletions python/examples/example_001-data_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# -------------------------------------------------------------------------------
# Example: Demonstrating vec2, vec3, and vec4
# -------------------------------------------------------------------------------
# This script demonstrates how to use the Easy3D vector classes:
# - `vec2`: Represents 2D points
# - `vec3`: Represents 3D points
# - `vec4`: Represents 4D points
# The script shows how to create these objects and convert them to NumPy arrays
# for easy manipulation in Python.
# -------------------------------------------------------------------------------

# -------------------------------------------------------------------------------
# Adding Easy3D Python Bindings to the System Path
# -------------------------------------------------------------------------------
# This is required if the bindings are not installed via `pip` but are located in
# a local build directory. To install the bindings, use the following command:
# "pip install YOUR_BUILD_DIRECTORY/lib/python/setup.py"
# -------------------------------------------------------------------------------
import sys
sys.path.append("../../cmake-build-debug/lib/python")

# -------------------------------------------------------------------------------
# Importing Necessary Libraries
# -------------------------------------------------------------------------------
import easy3d # Easy3D library for 3D visualization and processing
import numpy as np # NumPy library (used for handling data)

# -------------------------------------------------------------------------------
# Initializing Easy3D
# -------------------------------------------------------------------------------
# The `easy3d.initialize(True)` function initializes the Easy3D library.
# The `True` parameter enables detailed logging, which is useful for debugging.
easy3d.initialize(True)

# -------------------------------------------------------------------------------
# Creating and Converting a vec2 Object
# -------------------------------------------------------------------------------
# A `vec2` object represents a 2D point. We create a `vec2` object using two values.
# Optionally, the object can also be created using a list: `vec2([x, y])`.
v2 = easy3d.vec2(2.0, 2.0) # or v2 = easy3d.vec2([2.0, 2.0])

# The `to_numpy()` method converts the `vec2` object into a NumPy array.
np_v2 = v2.to_numpy() # Convert to NumPy array

# Output the NumPy array representation of the `vec2` object.
print("vec2 as NumPy array:", np_v2) # Output: [2. 2.]

# -------------------------------------------------------------------------------
# Creating and Converting a vec3 Object
# -------------------------------------------------------------------------------
# A `vec3` object represents a 3D point. We create a `vec3` object using three values.
# Optionally, the object can also be created using a list: `vec3([x, y, z])`.
v3 = easy3d.vec3(3.0, 3.0, 3.0) # or v3 = easy3d.vec3([3.0, 3.0, 3.0])

# The `to_numpy()` method converts the `vec3` object into a NumPy array.
np_v3 = v3.to_numpy() # Convert to NumPy array

# Output the NumPy array representation of the `vec3` object.
print("vec3 as NumPy array:", np_v3) # Output: [3. 3. 3.]

# -------------------------------------------------------------------------------
# Creating and Converting a vec4 Object
# -------------------------------------------------------------------------------
# A `vec4` object represents a 4D point. We create a `vec4` object using four values.
# Optionally, the object can also be created using a list: `vec4([x, y, z, w])`.
v4 = easy3d.vec4(4.0, 4.0, 4.0, 4.0) # or v4 = easy3d.vec4([4.0, 4.0, 4.0, 4.0])

# The `to_numpy()` method converts the `vec4` object into a NumPy array.
np_v4 = v4.to_numpy() # Convert to NumPy array

# Output the NumPy array representation of the `vec4` object.
print("vec4 as NumPy array:", np_v4) # Output: [4. 4. 4. 4.]
81 changes: 81 additions & 0 deletions python/examples/example_001-point_cloud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# -------------------------------------------------------------------------------
# Example: Working with the PointCloud class
# -------------------------------------------------------------------------------
# This script demonstrates how to use the Easy3D `PointCloud` class:
# - Initialize a `PointCloud` object with a NumPy array of points.
# - Add multiple points at once using a NumPy array.
# - Add individual points using various formats (vec3, lists, NumPy arrays).
# - Export the entire point cloud back to a NumPy array.
# -------------------------------------------------------------------------------

# -------------------------------------------------------------------------------
# Adding Easy3D Python Bindings to the System Path
# -------------------------------------------------------------------------------
# This is required if the bindings are not installed via `pip` but are located in
# a local build directory. To install the bindings, use the following command:
# "pip install YOUR_BUILD_DIRECTORY/lib/python/setup.py"
# -------------------------------------------------------------------------------
import sys
sys.path.append("../../cmake-build-debug/lib/python")

# -------------------------------------------------------------------------------
# Importing Necessary Libraries
# -------------------------------------------------------------------------------
import easy3d # Easy3D library for 3D visualization and processing
import numpy as np # NumPy library (used for handling data)

# -------------------------------------------------------------------------------
# Initializing Easy3D
# -------------------------------------------------------------------------------
# The `easy3d.initialize(True)` function initializes the Easy3D library.
# The `True` parameter enables detailed logging, which is useful for debugging.
easy3d.initialize(True)

# -------------------------------------------------------------------------------
# Creating a PointCloud Object and Initializing with Points
# -------------------------------------------------------------------------------
# Create a NumPy array of initial points (each point is represented by [x, y, z]).
initial_points = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) # Initial points

# Initialize a `PointCloud` object with the array of points.
pc = easy3d.PointCloud(initial_points)

# Set a name for the point cloud. This helps with identification in complex scenes.
pc.set_name("Small Point Cloud")

# Output the name of the point cloud.
print(f"Point cloud name: {pc.name()}") # Output: Small Point Cloud

# -------------------------------------------------------------------------------
# Adding Multiple Points to the PointCloud
# -------------------------------------------------------------------------------
# Create a NumPy array of additional points to add to the point cloud.
more_points = np.array([[7.0, 8.0, 9.0], [10.0, 11.0, 12.0]]) # Additional points

# Add the new points to the point cloud using `add_points`.
pc.add_points(more_points)

# -------------------------------------------------------------------------------
# Adding Individual Points Using Various Formats
# -------------------------------------------------------------------------------
# Add individual points using the `vec3` format.
v3 = easy3d.vec3(3.0, 3.0, 3.0) # Creating a vec3 object
pc.add_point(v3) # Add point using vec3 format
pc.add_point(easy3d.vec3(1, 8, 8)) # Another point using vec3

# Add individual points using Python lists.
pc.add_point([20, 20, 20]) # Add point using list format
pc.add_point([5, 5, 5]) # Another point using list format

# Add individual points using a NumPy array.
point_np = np.array([4.0, 5.0, 6.0], dtype=np.float32) # Point in NumPy array format
pc.add_point(point_np) # Add point using NumPy array format

# -------------------------------------------------------------------------------
# Exporting the PointCloud Data to a NumPy Array
# -------------------------------------------------------------------------------
# Convert the point cloud data back to a NumPy array using the `to_numpy()` method.
points_np = pc.to_numpy()

# Output the point cloud data in NumPy array format.
print("Point cloud data:\n", points_np)
Loading

0 comments on commit b0ca682

Please sign in to comment.