ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
lineset.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "t/geometry/LineSet.h"
9 
10 #include <string>
11 
12 #include "pybind/docstring.h"
15 
16 namespace cloudViewer {
17 namespace t {
18 namespace geometry {
19 
20 void pybind_lineset(py::module& m) {
21  py::class_<LineSet, PyGeometry<LineSet>, std::shared_ptr<LineSet>, Geometry,
23  line_set(m, "LineSet", R"(
24  A LineSet contains points and lines joining them and optionally attributes on
25  the points and lines. The ``LineSet`` class stores the attribute data in
26  key-value maps, where the key is the attribute name and value is a Tensor
27  containing the attribute data. There are two maps: one each for ``point``
28  and ``line``.
29 
30  The attributes of the line set have different levels::
31 
32  import cloudViewer as cv3d
33 
34  dtype_f = cv3d.core.float32
35  dtype_i = cv3d.core.int32
36 
37  # Create an empty line set
38  # Use lineset.point to access the point attributes
39  # Use lineset.line to access the line attributes
40  lineset = cv3d.t.geometry.LineSet()
41 
42  # Default attribute: point.positions, line.indices
43  # These attributes is created by default and are required by all line
44  # sets. The shape must be (N, 3) and (N, 2) respectively. The device of
45  # "positions" determines the device of the line set.
46  lineset.point.positions = cv3d.core.Tensor([[0, 0, 0],
47  [0, 0, 1],
48  [0, 1, 0],
49  [0, 1, 1]], dtype_f, device)
50  lineset.line.indices = cv3d.core.Tensor([[0, 1],
51  [1, 2],
52  [2, 3],
53  [3, 0]], dtype_i, device)
54 
55  # Common attributes: line.colors
56  # Common attributes are used in built-in line set operations. The
57  # spellings must be correct. For example, if "color" is used instead of
58  # "color", some internal operations that expects "colors" will not work.
59  # "colors" must have shape (N, 3) and must be on the same device as the
60  # line set.
61  lineset.line.colors = cv3d.core.Tensor([[0.0, 0.0, 0.0],
62  [0.1, 0.1, 0.1],
63  [0.2, 0.2, 0.2],
64  [0.3, 0.3, 0.3]], dtype_f, device)
65 
66  # User-defined attributes
67  # You can also attach custom attributes. The value tensor must be on the
68  # same device as the line set. The are no restrictions on the shape or
69  # dtype, e.g.,
70  lineset.point.labels = cv3d.core.Tensor(...)
71  lineset.line.features = cv3d.core.Tensor(...)
72  )");
73 
74  // Constructors.
75  line_set.def(py::init<const core::Device&>(),
76  "device"_a = core::Device("CPU:0"),
77  "Construct an empty LineSet on the provided device.")
78  .def(py::init<const core::Tensor&, const core::Tensor&>(),
79  "point_positions"_a, "line_indices"_a, R"(
80 Construct a LineSet from point_positions and line_indices.
81 
82 The input tensors will be directly used as the underlying storage of the line
83 set (no memory copy). The resulting ``LineSet`` will have the same ``dtype``
84 and ``device`` as the tensor. The device for ``point_positions`` must be consistent with
85 ``line_indices``.)");
87  m, "LineSet", "__init__",
88  {{"point_positions", "A tensor with element shape (3,)"},
89  {"line_indices",
90  "A tensor with element shape (2,) and Int dtype."}});
91 
92  py::detail::bind_copy_functions<LineSet>(line_set);
93  // Pickling support.
94  line_set.def(py::pickle(
95  [](const LineSet& line_set) {
96  // __getstate__
97  return py::make_tuple(line_set.GetDevice(),
98  line_set.GetPointAttr(),
99  line_set.GetLineAttr());
100  },
101  [](py::tuple t) {
102  // __setstate__
103  if (t.size() != 3) {
105  "Cannot unpickle LineSet! Expecting a tuple of "
106  "size 3.");
107  }
108 
109  const core::Device device = t[0].cast<core::Device>();
110  LineSet line_set(device);
111  if (!device.IsAvailable()) {
113  "Device ({}) is not available. LineSet will be "
114  "created on CPU.",
115  device.ToString());
116  line_set.To(core::Device("CPU:0"));
117  }
118 
119  const TensorMap point_attr = t[1].cast<TensorMap>();
120  const TensorMap line_attr = t[2].cast<TensorMap>();
121  for (auto& kv : point_attr) {
122  line_set.SetPointAttr(kv.first, kv.second);
123  }
124  for (auto& kv : line_attr) {
125  line_set.SetLineAttr(kv.first, kv.second);
126  }
127 
128  return line_set;
129  }));
130 
131  // Line set's attributes: point_positions, line_indices, line_colors, etc.
132  // def_property_readonly is sufficient, since the returned TensorMap can
133  // be editable in Python. We don't want the TensorMap to be replaced
134  // by another TensorMap in Python.
135  line_set.def_property_readonly(
136  "point", py::overload_cast<>(&LineSet::GetPointAttr, py::const_),
137  "Dictionary containing point attributes. The primary key "
138  "``positions`` contains point positions.");
139  line_set.def_property_readonly(
140  "line", py::overload_cast<>(&LineSet::GetLineAttr, py::const_),
141  "Dictionary containing line attributes. The primary key "
142  "``indices`` contains indices of points defining the lines.");
143 
144  line_set.def("__repr__", &LineSet::ToString);
145 
146  // Device transfers.
147  line_set.def("to", &LineSet::To,
148  "Transfer the line set to a specified device.", "device"_a,
149  "copy"_a = false);
150  line_set.def("clone", &LineSet::Clone,
151  "Returns copy of the line set on the same device.");
152  line_set.def(
153  "cpu",
154  [](const LineSet& line_set) {
155  return line_set.To(core::Device("CPU:0"));
156  },
157  "Transfer the line set to CPU. If the line set "
158  "is already on CPU, no copy will be performed.");
159  line_set.def(
160  "cuda",
161  [](const LineSet& line_set, int device_id) {
162  return line_set.To(core::Device("CUDA", device_id));
163  },
164  "Transfer the line set to a CUDA device. If the line set "
165  "is already on the specified CUDA device, no copy will be "
166  "performed.",
167  "device_id"_a = 0);
168 
169  // Line Set specific functions.
170  line_set.def("get_min_bound", &LineSet::GetMinBound,
171  "Returns the min bound for point coordinates.");
172  line_set.def("get_max_bound", &LineSet::GetMaxBound,
173  "Returns the max bound for point coordinates.");
174  line_set.def("get_center", &LineSet::GetCenter,
175  "Returns the center for point coordinates.");
176  line_set.def("transform", &LineSet::Transform, "transformation"_a, R"(
177 Transforms the points and lines. Custom attributes (e.g. point normals) are not
178 transformed. Extracts R, t from the transformation as:
179 
180 .. math::
181  T_{(4,4)} = \begin{bmatrix} R_{(3,3)} & t_{(3,1)} \\
182  O_{(1,3)} & s_{(1,1)} \end{bmatrix}
183 
184 It assumes :math:`s = 1` (no scaling) and :math:`O = [0,0,0]` and applies the
185 transformation as :math:`P = R(P) + t`)");
187  m, "LineSet", "transform",
188  {{"transformation",
189  "Transformation [Tensor of shape (4,4)]. Should be on the same "
190  "device as the LineSet"}});
191  line_set.def("translate", &LineSet::Translate, "translation"_a,
192  "relative"_a = true,
193  "Translates points and lines of the LineSet.");
195  m, "LineSet", "translate",
196  {{"translation",
197  "Translation tensor of dimension (3,). Should be on the same "
198  "device as the LineSet"},
199  {"relative",
200  "If true (default) translates relative to center of LineSet."}});
201  line_set.def("scale", &LineSet::Scale, "scale"_a, "center"_a,
202  "Scale points and lines. Custom attributes are not scaled.");
204  m, "LineSet", "scale",
205  {{"scale", "Scale magnitude."},
206  {"center",
207  "Center [Tensor of shape (3,)] about which the LineSet is to be "
208  "scaled. Should be on the same device as the LineSet."}});
209  line_set.def("rotate", &LineSet::Rotate, "R"_a, "center"_a,
210  "Rotate points and lines. Custom attributes (e.g. point "
211  "normals) are not rotated.");
213  m, "LineSet", "rotate",
214  {{"R", "Rotation [Tensor of shape (3,3)]."},
215  {"center",
216  "Center [Tensor of shape (3,)] about which the LineSet is to be "
217  "rotated. Should be on the same device as the LineSet."}});
218  line_set.def_static("from_legacy", &LineSet::FromLegacy, "lineset_legacy"_a,
219  "float_dtype"_a = core::Float32,
220  "int_dtype"_a = core::Int64,
221  "device"_a = core::Device("CPU:0"),
222  "Create a LineSet from a legacy Open3D LineSet.");
224  m, "LineSet", "from_legacy",
225  {
226  {"lineset_legacy", "Legacy Open3D LineSet."},
227  {"float_dtype",
228  "Float32 or Float64, used to store floating point values, "
229  "e.g. points, normals, colors."},
230  {"int_dtype",
231  "Int32 or Int64, used to store index values, e.g. line "
232  "indices."},
233  {"device",
234  "The device where the resulting LineSet resides."},
235  });
236  line_set.def("to_legacy", &LineSet::ToLegacy,
237  "Convert to a legacy Open3D LineSet.");
238 
239  line_set.def("get_axis_aligned_bounding_box",
241  "Create an axis-aligned bounding box from point attribute "
242  "'positions'.");
243  line_set.def("get_oriented_bounding_box", &LineSet::GetOrientedBoundingBox,
244  "Create an oriented bounding box from point attribute "
245  "'positions'.");
246  line_set.def("extrude_rotation", &LineSet::ExtrudeRotation, "angle"_a,
247  "axis"_a, "resolution"_a = 16, "translation"_a = 0.0,
248  "capping"_a = true,
249  R"(Sweeps the line set rotationally about an axis.
250 
251 Args:
252  angle (float): The rotation angle in degree.
253  axis (cloudViewer.core.Tensor): The rotation axis.
254  resolution (int): The resolution defines the number of intermediate sweeps
255  about the rotation axis.
256  translation (float): The translation along the rotation axis.
257 
258 Returns:
259  A triangle mesh with the result of the sweep operation.
260 
261 
262 Example:
263  This code generates a spring from a single line::
264 
265  import cloudViewer as cv3d
266 
267  line = cv3d.t.geometry.LineSet([[0.7,0,0],[1,0,0]], [[0,1]])
268  spring = line.extrude_rotation(3*360, [0,1,0], resolution=3*16, translation=2)
269  cv3d.visualization.draw([{'name': 'spring', 'geometry': spring}])
270 
271 )");
272 
273  line_set.def("extrude_linear", &LineSet::ExtrudeLinear, "vector"_a,
274  "scale"_a = 1.0, "capping"_a = true,
275  R"(Sweeps the line set along a direction vector.
276 
277 Args:
278  vector (cloudViewer.core.Tensor): The direction vector.
279  scale (float): Scalar factor which essentially scales the direction vector.
280 
281 Returns:
282  A triangle mesh with the result of the sweep operation.
283 
284 
285 Example:
286  This code generates an L-shaped mesh::
287 
288  import cloudViewer as cv3d
289 
290  lines = cv3d.t.geometry.LineSet([[1.0,0.0,0.0],[0,0,0],[0,0,1]], [[0,1],[1,2]])
291  mesh = lines.extrude_linear([0,1,0])
292  cv3d.visualization.draw([{'name': 'L', 'geometry': mesh}])
293 
294 )");
295  line_set.def("paint_uniform_color", &LineSet::PaintUniformColor, "color"_a,
296  "Assigns unifom color to all the lines of the LineSet. "
297  "Floating color values are clipped between 00 and 1.0. Input "
298  "`color` should be a (3,) shape tensor.");
299  line_set.def_static(
300  "create_camera_visualization", &LineSet::CreateCameraVisualization,
301  "view_width_px"_a, "view_height_px"_a, "intrinsic"_a, "extrinsic"_a,
302  "scale"_a = 1.f,
303  py::arg_v("color", core::Tensor({}, core::Float32),
304  "cloudViewer.core.Tensor([], "
305  "dtype=cloudViewer.core.Dtype.Float32)"),
306  R"(Factory function to create a LineSet from intrinsic and extrinsic
307 matrices. Camera reference frame is shown with XYZ axes in RGB.
308 
309 Args:
310  view_width_px (int): The width of the view, in pixels.
311  view_height_px (int): The height of the view, in pixels.
312  intrinsic (cloudViewer.core.Tensor): The intrinsic matrix {3,3} shape.
313  extrinsic (cloudViewer.core.Tensor): The extrinsic matrix {4,4} shape.
314  scale (float): camera scale
315  color (cloudViewer.core.Tensor): color with float32 and shape {3}. Default is blue.
316 
317 Example:
318 
319  Draw a purple camera frame with XYZ axes in RGB::
320 
321  import cloudViewer.core as o3c
322  from cloudViewer.t.geometry import LineSet
323  from cloudViewer.visualization import draw
324  K = o3c.Tensor([[512, 0, 512], [0, 512, 512], [0, 0, 1]], dtype=o3c.float32)
325  T = o3c.Tensor.eye(4, dtype=o3c.float32)
326  ls = LineSet.create_camera_visualization(1024, 1024, K, T, 1, [0.8, 0.2, 0.8])
327  draw([ls])
328 )");
329 }
330 
331 } // namespace geometry
332 } // namespace t
333 } // namespace cloudViewer
std::string ToString() const
Returns string representation of device, e.g. "CPU:0", "CUDA:0".
Definition: Device.cpp:89
bool IsAvailable() const
Returns true if the device is available.
Definition: Device.cpp:108
Mix-in class for geometry types that can be visualized.
The base geometry class.
Definition: Geometry.h:23
A LineSet contains points and lines joining them and optionally attributes on the points and lines.
Definition: LineSet.h:85
core::Tensor GetCenter() const
Returns the center for point coordinates.
Definition: LineSet.h:296
core::Tensor GetMaxBound() const
Returns the max bound for point coordinates.
Definition: LineSet.h:293
std::string ToString() const
Text description.
Definition: LineSet.cpp:56
LineSet & Translate(const core::Tensor &translation, bool relative=true)
Translates the points and lines of the LineSet.
Definition: LineSet.cpp:103
LineSet & PaintUniformColor(const core::Tensor &color)
Assigns uniform color to all lines of the LineSet.
Definition: LineSet.cpp:215
OrientedBoundingBox GetOrientedBoundingBox() const
Create an oriented bounding box from point attribute "positions".
Definition: LineSet.cpp:211
const TensorMap & GetLineAttr() const
Getter for line_attr_ TensorMap. Used in Pybind.
Definition: LineSet.h:142
LineSet Clone() const
Returns copy of the line set on the same device.
Definition: LineSet.h:118
LineSet To(const core::Device &device, bool copy=false) const
Definition: LineSet.cpp:42
TriangleMesh ExtrudeLinear(const core::Tensor &vector, double scale=1.0, bool capping=true) const
Definition: LineSet.cpp:204
TriangleMesh ExtrudeRotation(double angle, const core::Tensor &axis, int resolution=16, double translation=0.0, bool capping=true) const
Definition: LineSet.cpp:194
static LineSet CreateCameraVisualization(int view_width_px, int view_height_px, const core::Tensor &intrinsic, const core::Tensor &extrinsic, double scale, const core::Tensor &color={})
Definition: LineSet.cpp:231
cloudViewer::geometry::LineSet ToLegacy() const
Convert to a legacy CloudViewer LineSet.
Definition: LineSet.cpp:170
LineSet & Transform(const core::Tensor &transformation)
Transforms the points and lines of the LineSet.
Definition: LineSet.cpp:97
static geometry::LineSet FromLegacy(const cloudViewer::geometry::LineSet &lineset_legacy, core::Dtype float_dtype=core::Float32, core::Dtype int_dtype=core::Int64, const core::Device &device=core::Device("CPU:0"))
Definition: LineSet.cpp:133
const TensorMap & GetPointAttr() const
Getter for point_attr_ TensorMap. Used in Pybind.
Definition: LineSet.h:124
AxisAlignedBoundingBox GetAxisAlignedBoundingBox() const
Create an axis-aligned bounding box from point attribute "positions".
Definition: LineSet.cpp:190
LineSet & Rotate(const core::Tensor &R, const core::Tensor &center)
Rotates the points and lines of the line set. Custom attributes (e.g.: point or line normals) are not...
Definition: LineSet.cpp:126
core::Tensor GetMinBound() const
Returns the max bound for point coordinates.
Definition: LineSet.h:290
LineSet & Scale(double scale, const core::Tensor &center)
Scales the points and lines of the LineSet.
Definition: LineSet.cpp:116
#define LogWarning(...)
Definition: Logging.h:72
#define LogError(...)
Definition: Logging.h:60
const Dtype Int64
Definition: Dtype.cpp:47
const Dtype Float32
Definition: Dtype.cpp:42
void ClassMethodDocInject(py::module &pybind_module, const std::string &class_name, const std::string &function_name, const std::unordered_map< std::string, std::string > &map_parameter_body_docs)
Definition: docstring.cpp:27
void pybind_lineset(py::module &m)
Definition: lineset.cpp:20
Generic file read and write utility for python interface.