ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
o3dvisualizer.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 
9 
11 #include <ecvHObject.h>
12 
13 #include "t/geometry/Geometry.h"
16 // clang-format off
20 // clang-format on
21 
22 namespace cloudViewer {
23 namespace visualization {
24 
25 using namespace visualizer;
26 
27 void pybind_o3dvisualizer(py::module& m) {
28  py::class_<O3DVisualizerSelections::SelectedIndex> selected_index(
29  m, "SelectedIndex",
30  "Information about a point or vertex that was selected");
31  selected_index
32  .def("__repr__",
34  std::stringstream s;
35  s << "{ index: " << idx.index << ", order: " << idx.order
36  << ", point: (" << idx.point.x() << ", " << idx.point.y()
37  << ", " << idx.point.z() << ") }";
38  return s.str();
39  })
40  .def_readonly("index",
42  "The index of this point in the point/vertex "
43  "array")
44  .def_readonly("order",
46  "A monotonically increasing value that can be "
47  "used to determine in what order the points "
48  "were selected")
49  .def_readonly("point",
51  "The (x, y, z) value of this point");
52 
53  py::class_<O3DVisualizer, UnownedPointer<O3DVisualizer>, gui::Window>
54  o3dvis(m, "O3DVisualizer", "Visualization object used by draw()");
55 
56  py::native_enum<O3DVisualizer::Shader>(o3dvis, "Shader", "enum.Enum",
57  "Scene-level rendering options.")
58  .value("STANDARD", O3DVisualizer::Shader::STANDARD,
59  "Pixel colors from standard lighting model")
60  .value("UNLIT", O3DVisualizer::Shader::UNLIT,
61  "Normals will be ignored (useful for point clouds)")
62  .value("NORMALS", O3DVisualizer::Shader::NORMALS,
63  "Pixel colors correspond to surface normal")
64  .value("DEPTH", O3DVisualizer::Shader::DEPTH,
65  "Pixel colors correspond to depth buffer value")
66  .export_values()
67  .finalize();
68 
69  py::native_enum<O3DVisualizer::TickResult>(
70  o3dvis, "TickResult", "enum.Enum",
71  "Return value from animation tick callback.")
72  .value("NO_CHANGE", O3DVisualizer::TickResult::NO_CHANGE,
73  "Signals that no change happened and no redraw is required")
74  .value("REDRAW", O3DVisualizer::TickResult::REDRAW,
75  "Signals that a redraw is required")
76  .export_values()
77  .finalize();
78 
79  py::class_<O3DVisualizer::DrawObject> drawobj(
80  o3dvis, "DrawObject",
81  "Information about an object that is drawn. Do not modify this, it "
82  "can lead to unexpected results.");
83  drawobj.def_readonly("name", &O3DVisualizer::DrawObject::name,
84  "The name of the object")
85  .def_property_readonly(
86  "geometry",
87  [](const O3DVisualizer::DrawObject& o) {
88  if (o.geometry) {
89  return py::cast(o.geometry);
90  } else {
91  return py::cast(o.tgeometry);
92  }
93  },
94  "The geometry. Modifying this will not "
95  "result in any visible change. Use "
96  "remove_geometry() and then add_geometry()"
97  "to change the geometry")
98  .def_readonly("group", &O3DVisualizer::DrawObject::group,
99  "The group that the object belongs to")
100  .def_readonly("time", &O3DVisualizer::DrawObject::time,
101  "The object's timestamp")
102  .def_readonly("is_visible", &O3DVisualizer::DrawObject::is_visible,
103  "True if the object is checked in the list. "
104  "If the object's group is unchecked or an "
105  "animation is playing, the object's "
106  "visiblity may not correspond with this "
107  "value");
108 
109  o3dvis.def(py::init<const std::string, int, int>(),
110  "title"_a = "CloudViewer", "width"_a = 1024, "height"_a = 768,
111  "Creates a O3DVisualizer object")
112  // selected functions inherited from Window
113  .def_property("os_frame", &O3DVisualizer::GetOSFrame,
115  "Window rect in OS coords, not device pixels")
116  .def_property("title", &O3DVisualizer::GetTitle,
118  "Returns the title of the window")
119  .def("size_to_fit", &O3DVisualizer::SizeToFit,
120  "Sets the width and height of window to its preferred size")
121  .def_property("size", &O3DVisualizer::GetSize,
123  "The size of the window in device pixels, including "
124  "menubar (except on macOS)")
125  .def_property_readonly(
126  "content_rect", &O3DVisualizer::GetContentRect,
127  "Returns the frame in device pixels, relative "
128  " to the window, which is available for widgets "
129  "(read-only)")
130  .def_property_readonly(
131  "scaling", &O3DVisualizer::GetScaling,
132  "Returns the scaling factor between OS pixels "
133  "and device pixels (read-only)")
134  .def_property_readonly("is_visible", &O3DVisualizer::IsVisible,
135  "True if window is visible (read-only)")
136  .def_property_readonly(
138  "Window's unique ID when WebRTCWindowSystem is use."
139  "Returns 'window_undefined' otherwise.")
140  .def("post_redraw", &O3DVisualizer::PostRedraw,
141  "Tells the window to redraw")
142  .def("show", &O3DVisualizer::Show, "Shows or hides the window",
143  "vis"_a)
144  .def("close", &O3DVisualizer::Close,
145  "Closes the window and destroys it, unless an on_close "
146  "callback cancels the close.")
147  .def(
148  "show_dialog",
150  w.ShowDialog(TakeOwnership<gui::Dialog>(dlg));
151  },
152  "Displays the dialog", "dlg"_a)
153  .def("close_dialog", &O3DVisualizer::CloseDialog,
154  "Closes the current dialog")
155  .def("show_message_box", &O3DVisualizer::ShowMessageBox,
156  "Displays a simple dialog with a title and message and okay "
157  "button",
158  "title"_a, "message"_a)
159  .def("set_on_close", &O3DVisualizer::SetOnClose,
160  "Sets a callback that will be called when the window is "
161  "closed. The callback is given no arguments and should return "
162  "True to continue closing the window or False to cancel the "
163  "close",
164  "callback"_a)
165  .def("show_menu", &O3DVisualizer::ShowMenu,
166  "Shows or hides the menu in the window, except on macOS since "
167  "the menubar is not in the window and all applications must "
168  "have a menubar.",
169  "show"_a)
170  // from O3DVisualizer
171  .def("add_action", &O3DVisualizer::AddAction,
172  "Adds a button to the custom actions section of the UI and a "
173  "corresponding menu item in the \"Actions\" menu. The "
174  "callback will be given one parameter, the O3DVisualizer "
175  "instance, and does not return any value.",
176  "name"_a, "callback"_a)
177  .def("add_geometry",
178  py::overload_cast<const std::string&,
179  std::shared_ptr<ccHObject>,
181  const std::string&, double, bool>(
183  "name"_a, "geometry"_a, "material"_a = nullptr, "group"_a = "",
184  "time"_a = 0.0, "is_visible"_a = true,
185  "Adds a geometry. 'name' must be unique.")
186  .def("add_geometry",
187  py::overload_cast<const std::string&,
188  std::shared_ptr<t::geometry::Geometry>,
190  const std::string&, double, bool>(
192  "name"_a, "geometry"_a, "material"_a = nullptr, "group"_a = "",
193  "time"_a = 0.0, "is_visible"_a = true,
194  "Adds a Tensor-based geometry. 'name' must be unique.")
195  .def("add_geometry",
196  py::overload_cast<
197  const std::string&,
198  std::shared_ptr<rendering::TriangleMeshModel>,
199  const rendering::MaterialRecord*, const std::string&,
200  double, bool>(&O3DVisualizer::AddGeometry),
201  "name"_a, "model"_a, "material"_a = nullptr, "group"_a = "",
202  "time"_a = 0.0, "is_visible"_a = true,
203  "Adds a TriangleMeshModel. 'name' must be unique. 'material' "
204  "is ignored.")
205  .def(
206  "add_geometry",
207  [](py::object dv, const py::dict& d) {
208  rendering::MaterialRecord* material = nullptr;
209  std::string group = "";
210  double time = 0;
211  bool is_visible = true;
212 
213  std::string name = py::cast<std::string>(d["name"]);
214  if (d.contains("material")) {
215  material = py::cast<rendering::MaterialRecord*>(
216  d["material"]);
217  }
218  if (d.contains("group")) {
219  group = py::cast<std::string>(d["group"]);
220  }
221  if (d.contains("time")) {
222  time = py::cast<double>(d["time"]);
223  }
224  if (d.contains("is_visible")) {
225  is_visible = py::cast<bool>(d["is_visible"]);
226  }
227  py::object g = d["geometry"];
228  // Instead of trying to figure out how to cast 'g' as
229  // the appropriate shared_ptr, if we can call the
230  // function using pybind and let it figure out how to do
231  // everything.
232  dv.attr("add_geometry")(name, g, material, group, time,
233  is_visible);
234  },
235  "Adds a geometry from a dictionary. The dictionary has the "
236  "following elements:\n"
237  "name: unique name of the object (required)\n"
238  "geometry: the geometry or t.geometry object (required)\n"
239  "material: a visualization.rendering.Material object "
240  "(optional)\n"
241  "group: a string declaring the group it is a member of "
242  "(optional)\n"
243  "time: a time value\n",
244  "d"_a)
245  .def("remove_geometry", &O3DVisualizer::RemoveGeometry,
246  "Removes the geometry with the name.", "name"_a)
247  .def("update_geometry", &O3DVisualizer::UpdateGeometry,
248  "Updates the attributes of the named geometry specified by "
249  "update_flags with tpoint_cloud. Note: Currently this "
250  "function only works with T Geometry Point Clouds.",
251  "name"_a, "tpoint_cloud"_a, "update_flags"_a)
252  .def("show_geometry", &O3DVisualizer::ShowGeometry,
253  "Checks or unchecks the named geometry in the list. Note that "
254  "even if show_geometry(name, True) is called, the object may "
255  "not actually be visible if its group is unchecked, or if an "
256  "animation is in progress.",
257  "name"_a, "show"_a)
258  .def("get_geometry", &O3DVisualizer::GetGeometry,
259  "Returns the DrawObject corresponding to the name. This "
260  "should be treated as read-only. Modify visibility with "
261  "show_geometry(), and other values by removing the object and "
262  "re-adding it with the new values",
263  "name"_a)
264  .def("get_geometry_material", &O3DVisualizer::GetGeometryMaterial,
265  "Returns the MaterialRecord corresponding to the name. The "
266  "returned material is a copy, therefore modifying it directly "
267  "will not change the visualization.",
268  "name"_a)
269  .def("modify_geometry_material",
271  "Updates the named geometry to use the new provided material.",
272  "name"_a, "material"_a)
273  .def("add_3d_label", &O3DVisualizer::Add3DLabel,
274  "Displays text anchored at the 3D coordinate specified",
275  "pos"_a, "text"_a)
276  .def("clear_3d_labels", &O3DVisualizer::Clear3DLabels,
277  "Clears all 3D text")
278  .def("setup_camera",
279  py::overload_cast<float, const Eigen::Vector3f&,
280  const Eigen::Vector3f&,
281  const Eigen::Vector3f&>(
283  "Sets the camera view so that the camera is located at 'eye', "
284  "pointing towards 'center', and oriented so that the up "
285  "vector is 'up'",
286  "field_of_view"_a, "center"_a, "eye"_a, "up"_a)
287  .def("setup_camera",
288  py::overload_cast<const camera::PinholeCameraIntrinsic&,
289  const Eigen::Matrix4d&>(
291  "Sets the camera view", "intrinsic"_a, "extrinsic_matrix"_a)
292  .def("setup_camera",
293  py::overload_cast<const Eigen::Matrix3d&,
294  const Eigen::Matrix4d&, int, int>(
296  "Sets the camera view", "intrinsic_matrix"_a,
297  "extrinsic_matrix"_a, "intrinsic_width_px"_a,
298  "intrinsic_height_px"_a)
299  .def("reset_camera_to_default",
301  "Sets camera to default position")
302  .def("get_selection_sets", &O3DVisualizer::GetSelectionSets,
303  "Returns the selection sets, as [{'obj_name', "
304  "[SelectedIndex]}]")
305  .def("set_on_animation_frame", &O3DVisualizer::SetOnAnimationFrame,
306  "Sets a callback that will be called every frame of the "
307  "animation. The callback will be called as callback(o3dvis, "
308  "current_time).",
309  "callback"_a)
310  .def("set_on_animation_tick", &O3DVisualizer::SetOnAnimationTick,
311  "Sets a callback that will be called every frame of the "
312  "animation. The callback will be called as callback(o3dvis, "
313  "time_since_last_tick, "
314  "total_elapsed_since_animation_started). Note that this is a "
315  "low-level callback. If you need to change the current "
316  "timestamp being shown you will need to update the "
317  "o3dvis.current_time property in the callback. The callback "
318  "must return either O3DVisualizer.TickResult.IGNORE if no "
319  "redraw is required or O3DVisualizer.TickResult.REDRAW if a "
320  "redraw is required.",
321  "callback"_a)
322  .def("export_current_image", &O3DVisualizer::ExportCurrentImage,
323  "Exports a PNG or JPEG image of what is currently displayed "
324  "to the given path.",
325  "path"_a)
326  .def("start_rpc_interface", &O3DVisualizer::StartRPCInterface,
327  "address"_a, "timeout"_a,
328  "Starts the RPC interface.\n"
329  "address: str with the address to listen on.\n"
330  "timeout: int timeout in milliseconds for sending the reply.")
331  .def("stop_rpc_interface", &O3DVisualizer::StopRPCInterface,
332  "Stops the RPC interface.")
333  .def("set_background", &O3DVisualizer::SetBackground,
334  "Sets the background color and, optionally, the background "
335  "image. Passing None for the background image will clear any "
336  "image already there.",
337  "bg_color"_a, "bg_image"_a)
338  .def("set_ibl", &O3DVisualizer::SetIBL,
339  "Sets the IBL and its matching skybox. If ibl_name_ibl.ktx is "
340  "found in the default resource directory then it is used. "
341  "Otherwise, ibl_name is assumed to be a path to the ibl KTX "
342  "file.",
343  "ibl_name"_a)
344  .def("set_ibl_intensity", &O3DVisualizer::SetIBLIntensity,
345  "Sets the intensity of the current IBL", "intensity"_a)
346  .def("enable_raw_mode", &O3DVisualizer::EnableBasicMode,
347  "Enables/disables raw mode for simplified lighting "
348  "environment.",
349  "enable"_a)
350  .def("show_skybox", &O3DVisualizer::ShowSkybox,
351  "Show/Hide the skybox", "show"_a)
352  .def("set_panel_open", &O3DVisualizer::SetPanelOpen,
353  "Expand/Collapse verts(panels) within the settings panel",
354  "name"_a, "open"_a)
355  .def_property(
356  "show_settings",
357  [](const O3DVisualizer& dv) {
358  return dv.GetUIState().show_settings;
359  },
361  "Gets/sets if settings panel is visible")
362  .def_property(
363  "mouse_mode",
364  [](const O3DVisualizer& dv) {
365  return dv.GetUIState().mouse_mode;
366  },
368  "Gets/sets the control mode being used for the mouse")
369  .def_property(
370  "scene_shader",
371  [](const O3DVisualizer& dv) {
372  return dv.GetUIState().scene_shader;
373  },
375  "Gets/sets the shading model for the scene")
376  .def_property(
377  "show_axes",
378  [](const O3DVisualizer& dv) {
379  return dv.GetUIState().show_axes;
380  },
381  &O3DVisualizer::ShowAxes, "Gets/sets if axes are visible")
382  .def_property(
383  "show_ground",
384  [](const O3DVisualizer& dv) {
385  return dv.GetUIState().show_ground;
386  },
388  "Gets/sets if ground plane is visible")
389  .def_property(
390  "ground_plane",
391  [](const O3DVisualizer& dv) {
392  return dv.GetUIState().ground_plane;
393  },
395  "Sets the plane for ground plane, XZ, XY, or YZ")
396  .def_property(
397  "point_size",
398  [](const O3DVisualizer& dv) {
399  return dv.GetUIState().point_size;
400  },
402  "Gets/sets size of points (in units of pixels)")
403  .def_property(
404  "line_width",
405  [](const O3DVisualizer& dv) {
406  return dv.GetUIState().line_width;
407  },
409  "Gets/sets width of lines (in units of pixels)")
410  .def_property_readonly(
411  "scene", &O3DVisualizer::GetScene,
412  "Returns the rendering.CloudViewerScene object "
413  "for low-level manipulation")
414  .def_property(
415  "current_time",
416  // MSVC doesn't like this for some reason
417  //&O3DVisualizer::GetCurrentTime,
418  [](const O3DVisualizer& dv) -> double {
419  return dv.GetCurrentTime();
420  },
422  "Gets/sets the current time. If setting, only the "
423  "objects belonging to the current time-step will "
424  "be displayed")
425  .def_property("animation_time_step",
428  "Gets/sets the time step for animations. Default is "
429  "1.0 sec")
430  .def_property("animation_frame_delay",
433  "Gets/sets the length of time a frame is visible.")
434  .def_property("animation_duration",
437  "Gets/sets the duration (in seconds) of the "
438  "animation. This is automatically computed to be the "
439  "difference between the minimum and maximum time "
440  "values, but this is useful if no time values have "
441  "been specified (that is, all objects are at the "
442  "default t=0)")
443  .def_property("is_animating", &O3DVisualizer::GetIsAnimating,
445  "Gets/sets the status of the animation. Changing "
446  "value will start or stop the animating.");
447 }
448 
449 } // namespace visualization
450 } // namespace cloudViewer
std::string name
Contains the pinhole camera intrinsic parameters.
void SetSize(const Size &size)
Sets the size of the window in pixels. Includes menubar on Linux.
Definition: Window.cpp:508
void SetTitle(const char *title)
Definition: Window.cpp:466
void ShowDialog(std::shared_ptr< Dialog > dlg)
Definition: Window.cpp:619
void CloseDialog()
Closes the dialog.
Definition: Window.cpp:648
void ShowMessageBox(const char *title, const char *message)
Definition: Window.cpp:661
float GetScaling() const
Returns the scaling factor from OS pixels to device pixels.
Definition: Window.cpp:538
void SetOnClose(std::function< bool()> callback)
Definition: Window.cpp:611
void StartRPCInterface(const std::string &address, int timeout)
Starts the RPC interface. See io/rpc/ZMQReceiver for the parameters.
void SetMouseMode(gui::SceneWidget::Controls mode)
void SetOnAnimationTick(std::function< TickResult(O3DVisualizer &, double, double)> cb)
void SetGroundPlane(rendering::Scene::GroundPlane plane)
void SetBackground(const Eigen::Vector4f &bg_color, std::shared_ptr< geometry::Image > bg_image=nullptr)
DrawObject GetGeometry(const std::string &name) const
Returns Visualizer's internal DrawObject for the named geometry.
void SetOnAnimationFrame(std::function< void(O3DVisualizer &, double)> cb)
rendering::MaterialRecord GetGeometryMaterial(const std::string &name) const
void SetPanelOpen(const std::string &name, bool open)
void ShowGeometry(const std::string &name, bool show)
Show/hide the named geometry.
void SetupCamera(float fov, const Eigen::Vector3f &center, const Eigen::Vector3f &eye, const Eigen::Vector3f &up)
rendering::CloudViewerScene * GetScene() const
void RemoveGeometry(const std::string &name)
Removes the named geometry from the Visualizer.
void UpdateGeometry(const std::string &name, std::shared_ptr< t::geometry::Geometry > tgeom, uint32_t update_flags)
std::vector< O3DVisualizerSelections::SelectionSet > GetSelectionSets() const
void Add3DLabel(const Eigen::Vector3f &pos, const char *text)
void AddAction(const std::string &name, std::function< void(O3DVisualizer &)> callback)
void ModifyGeometryMaterial(const std::string &name, const rendering::MaterialRecord *material)
void AddGeometry(const std::string &name, std::shared_ptr< ccHObject > geom, const rendering::MaterialRecord *material=nullptr, const std::string &group="", double time=0.0, bool is_visible=true)
void pybind_o3dvisualizer(py::module &m)
Generic file read and write utility for python interface.