cellnav 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. cellnav/__init__.py +18 -0
  2. cellnav/core/containers/__init__.py +7 -0
  3. cellnav/core/containers/geometry_base.py +514 -0
  4. cellnav/core/containers/path_3d.py +33 -0
  5. cellnav/core/framework/__init__.py +20 -0
  6. cellnav/core/framework/point_cloud.py +719 -0
  7. cellnav/core/framework/surface_graph.py +943 -0
  8. cellnav/core/framework/surface_mesh.py +407 -0
  9. cellnav/core/framework/surface_wireframe.py +213 -0
  10. cellnav/core/framework/unique_surface_wireframe.py +85 -0
  11. cellnav/core/helpers/__init__.py +7 -0
  12. cellnav/core/helpers/alpha_shape_helper.py +244 -0
  13. cellnav/core/helpers/geo_shape_helper.py +251 -0
  14. cellnav/core/util/__init__.py +18 -0
  15. cellnav/core/util/distance_from_line.py +56 -0
  16. cellnav/core/util/distance_from_point.py +31 -0
  17. cellnav/core/util/geometry_transformer.py +300 -0
  18. cellnav/core/util/make_3d_path.py +126 -0
  19. cellnav/generators/__init__.py +29 -0
  20. cellnav/generators/circle.py +36 -0
  21. cellnav/generators/cube.py +71 -0
  22. cellnav/generators/cylinder.py +107 -0
  23. cellnav/generators/ellipsoid.py +47 -0
  24. cellnav/generators/gaussian_blob.py +44 -0
  25. cellnav/generators/gaussian_corona.py +51 -0
  26. cellnav/generators/gaussian_noise.py +41 -0
  27. cellnav/generators/hollow_cylinder.py +52 -0
  28. cellnav/generators/sphere.py +42 -0
  29. cellnav/visualization/__init__.py +20 -0
  30. cellnav/visualization/draw_3d_dataset.py +55 -0
  31. cellnav/visualization/draw_3d_path.py +46 -0
  32. cellnav/visualization/shortest_path_interactive.py +87 -0
  33. cellnav/visualization/shortest_path_mpl.py +100 -0
  34. cellnav/visualization/time_complexity_overview.py +142 -0
  35. cellnav-0.0.1.dist-info/LICENSE +201 -0
  36. cellnav-0.0.1.dist-info/METADATA +124 -0
  37. cellnav-0.0.1.dist-info/RECORD +38 -0
  38. cellnav-0.0.1.dist-info/WHEEL +4 -0
cellnav/__init__.py ADDED
@@ -0,0 +1,18 @@
1
+ ## forward imports for main user-facing module
2
+ from cellnav.core.framework import (
3
+ SurfaceMesh as SurfaceMesh,
4
+ SurfaceGraph as SurfaceGraph,
5
+ SurfaceWireframe as SurfaceWireframe,
6
+ PointCloud as PointCloud,
7
+ UniqueSurfaceWireframe as UniqueSurfaceWireframe,
8
+ )
9
+ from cellnav.core.util import GeometryTransformer as GeometryTransformer
10
+
11
+ __all__ = [
12
+ "SurfaceMesh",
13
+ "SurfaceGraph",
14
+ "SurfaceWireframe",
15
+ "PointCloud",
16
+ "UniqueSurfaceWireframe",
17
+ "GeometryTransformer",
18
+ ]
@@ -0,0 +1,7 @@
1
+ ## forward imports for containers module
2
+ from cellnav.core.containers.geometry_base import (
3
+ GeometryBase as GeometryBase,
4
+ )
5
+
6
+
7
+ __all__ = ["GeometryBase"]
@@ -0,0 +1,514 @@
1
+ ## dependencies
2
+ import numpy as np
3
+ import open3d as o3d
4
+ import matplotlib.colors
5
+ from copy import deepcopy
6
+ from abc import ABC, abstractmethod
7
+ from typing import Dict, List, Tuple, Union, Optional
8
+
9
+ __all_ = ["GeometryBase"]
10
+
11
+
12
+ ## main class implementation - Cell membrane extraction tool
13
+ class GeometryBase(ABC):
14
+ _geometry: Union[
15
+ o3d.geometry.PointCloud, o3d.geometry.TriangleMesh, o3d.geometry.LineSet
16
+ ]
17
+ _config: Dict[str, bool]
18
+
19
+ def __init__(
20
+ self,
21
+ geometry: Union[
22
+ o3d.geometry.PointCloud, o3d.geometry.TriangleMesh, o3d.geometry.LineSet
23
+ ],
24
+ **kwargs,
25
+ ) -> None:
26
+ """
27
+ Main base class for all geometrical objects in the Min3D framework.
28
+ This class is not meant to be instantiated directly,
29
+ but rather to be subclassed by specific geometry types (e.g. PointCloud, SurfaceMesh, SurfaceWireframe).
30
+ The implementation of this class provides common functionality for all geometrical objects,
31
+ such as basic transformations (translate, scale, center), visualization functions,
32
+ and utility functions to get properties of the geometry (e.g. center of mass, bounding box).
33
+
34
+ Args:
35
+ geometry (Union[ o3d.geometry.PointCloud, o3d.geometry.TriangleMesh, o3d.geometry.LineSet ]): The underlying Open3D geometry object that this class will wrap around. This can be a PointCloud, TriangleMesh, or LineSet depending on the specific subclass.
36
+ **kwargs: Additional keyword arguments that can be used to configure the behavior of the class. Currently, this is a placeholder for future configuration options (e.g. verbose, debug mode) that can be set when instantiating the class.
37
+ """
38
+ self._geometry = geometry
39
+
40
+ # config options for decorators - currently a placeholder
41
+ self.config = {"verbose": True, "DEBUG": False}
42
+
43
+ # update from kwargs if provided
44
+ for key in self.config.keys():
45
+ if key in kwargs:
46
+ self.config[key] = kwargs[key]
47
+
48
+ # override if DEBUG
49
+ if self.config["DEBUG"]:
50
+ self.config["verbose"] = True
51
+
52
+ # run post init
53
+ self.__post_init__()
54
+
55
+ def __post_init__(self) -> None:
56
+ """
57
+ Post initialization function that can be used to perform any additional setup after the object has been initialized.
58
+ This is a placeholder for now, but can be used in the future to set up additional properties or perform checks on the geometry.
59
+ """
60
+ pass
61
+
62
+ # %% Classmethods
63
+ @classmethod
64
+ @abstractmethod
65
+ def from_ply(cls, file_path: Optional[str] = None, **kwargs) -> "GeometryBase":
66
+ """
67
+ Load geometry from a PLY file.
68
+ If no file path is provided and on a Windows machine, a file dialog will be opened to select a PLY file.
69
+
70
+ Args:
71
+ file_path (Optional[str], optional): The path to the PLY file. Defaults to None.
72
+
73
+ Returns:
74
+ GeometryBase: The loaded geometry object.
75
+ """
76
+ pass
77
+
78
+ @classmethod
79
+ @abstractmethod
80
+ def from_o3d(
81
+ cls,
82
+ geometry: Union[
83
+ o3d.geometry.PointCloud, o3d.geometry.TriangleMesh, o3d.geometry.LineSet
84
+ ],
85
+ **kwargs,
86
+ ) -> "GeometryBase":
87
+ """
88
+ Create a GeometryBase object from an existing Open3D geometry object.
89
+ This is a common classmethod that can be used by all subclasses to create an instance from an Open3D geometry.
90
+
91
+ Args:
92
+ geometry (Union[ o3d.geometry.PointCloud, o3d.geometry.TriangleMesh, o3d.geometry.LineSet ]): The existing Open3D geometry object.
93
+
94
+ Returns:
95
+ GeometryBase: The created geometry object.
96
+ """
97
+ pass
98
+
99
+ # %% Utility functions
100
+ def get_center_of_mass(self) -> np.ndarray:
101
+ """Calculate the center of mass of the geometry.
102
+
103
+ Returns:
104
+ np.ndarray: The center of mass. 3D vector.
105
+ """
106
+ return self._geometry.get_center()
107
+
108
+ def get_max_bound(self) -> np.ndarray:
109
+ """Get the maximum bounding box coordinates.
110
+
111
+ Returns:
112
+ np.ndarray: The maximum bounding box coordinates. 3D vector.
113
+ """
114
+ return self._geometry.get_max_bound()
115
+
116
+ def get_min_bound(self) -> np.ndarray:
117
+ """Get the minimum bounding box coordinates.
118
+
119
+ Returns:
120
+ np.ndarray: The minimum bounding box coordinates. 3D vector.
121
+ """
122
+ return self._geometry.get_min_bound()
123
+
124
+ def get_axis_aligned_bounding_box(self) -> o3d.geometry.AxisAlignedBoundingBox:
125
+ """Get the axis-aligned bounding box of the geometry.
126
+
127
+ Returns:
128
+ o3d.geometry.AxisAlignedBoundingBox: The axis-aligned bounding box.
129
+ """
130
+ return self._geometry.get_axis_aligned_bounding_box()
131
+
132
+ def get_oriented_bounding_box(self) -> o3d.geometry.OrientedBoundingBox:
133
+ """Get the oriented bounding box of the geometry.
134
+
135
+ Returns:
136
+ o3d.geometry.OrientedBoundingBox: The oriented bounding box.
137
+ """
138
+ return self._geometry.get_oriented_bounding_box()
139
+
140
+ def get_center_of_bounding_box(self) -> np.ndarray:
141
+ """Get the center of the axis-aligned bounding box.
142
+
143
+ Returns:
144
+ np.ndarray: The center of the axis-aligned bounding box. 3D vector.
145
+ """
146
+ return self._geometry.get_axis_aligned_bounding_box().get_center()
147
+
148
+ # %% Data Manipulation
149
+ def translate(
150
+ self, translation_vector: np.ndarray, inplace: bool = True
151
+ ) -> "GeometryBase":
152
+ """Translate (Move) the geometry by a given translation vector.
153
+
154
+ Args:
155
+ translation_vector (np.ndarray): The translation vector.
156
+ inplace (bool, optional): Whether to modify the geometry in place. Defaults to True.
157
+
158
+ Returns:
159
+ GeometryBase: Either the original geometry (if inplace=True) or a new translated geometry.
160
+ """
161
+ if inplace:
162
+ self._geometry.translate(translation_vector)
163
+ return self
164
+ else:
165
+ new_geometry = deepcopy(self._geometry).translate(translation_vector)
166
+ return GeometryBase.from_o3d(new_geometry)
167
+
168
+ def scale(
169
+ self,
170
+ scale_factor: float,
171
+ center: Union[np.ndarray, bool] = True,
172
+ inplace: bool = True,
173
+ ) -> "GeometryBase":
174
+ """Scale the geometry by a given scale factor, optionally around a specified center point.
175
+
176
+ Args:
177
+ scale_factor (float): The scale factor.
178
+ center (Union[np.ndarray, bool], optional): The center point around which to scale. If True, the center of mass is used. Defaults to True.
179
+ inplace (bool, optional): Whether to modify the geometry in place. Defaults to True.
180
+
181
+ Returns:
182
+ GeometryBase: Either the original geometry (if inplace=True) or a new scaled geometry.
183
+ """
184
+ if center is None:
185
+ center = self.get_center_of_mass()
186
+ if inplace:
187
+ self._geometry.scale(scale_factor, center)
188
+ return self
189
+ else:
190
+ new_geometry = deepcopy(self._geometry).scale(scale_factor, center)
191
+ return GeometryBase.from_o3d(new_geometry)
192
+
193
+ def center_on_origin(self, inplace: bool = True) -> "GeometryBase":
194
+ """Center the geometry on the origin by translating it such that its center of mass is at the origin.
195
+
196
+ Args:
197
+ inplace (bool, optional): Whether to modify the geometry in place. Defaults to True.
198
+
199
+ Returns:
200
+ GeometryBase: Either the original geometry (if inplace=True) or a new centered geometry.
201
+ """
202
+ center_of_mass = self.get_center_of_mass()
203
+ translation_vector = -center_of_mass
204
+
205
+ if inplace:
206
+ self._geometry.translate(translation_vector)
207
+ return self
208
+ else:
209
+ new_geometry = deepcopy(self._geometry).translate(translation_vector)
210
+ return GeometryBase.from_o3d(new_geometry)
211
+
212
+ # %% 3D Visualization functions
213
+ def visualize(self) -> None:
214
+ """
215
+ Visualize the geometry object saved to self._geometry using Open3D's visualization tools.
216
+ This will open a window where the geometry can be viewed.
217
+ No interactions are possible or planned in this basic visualization mode.
218
+ """
219
+ o3d.visualization.draw_geometries([self._geometry]) # type: ignore
220
+
221
+ def visualize_only(self, *args) -> None:
222
+ """
223
+ Visualizes only the provided geometries, without including the geometry of the current object.
224
+ This can be useful for visualizing related geometries (e.g. a path on top of a mesh) without showing the base geometry.
225
+ """
226
+ o3d.visualization.draw_geometries([*args]) # type: ignore
227
+
228
+ def visualize_with(self, *args) -> None:
229
+ """
230
+ Visualizes the current geometry together with additional geometries provided as arguments.
231
+ This can be useful for visualizing the base geometry together with related geometries (e.g. a path on top of a mesh).
232
+ """
233
+ args = [arg.geometry if isinstance(arg, GeometryBase) else arg for arg in args]
234
+ o3d.visualization.draw_geometries([self._geometry, *args]) # type: ignore
235
+
236
+ def visual_editor(
237
+ self, inplace: bool = True, full_screen: bool = False
238
+ ) -> "GeometryBase":
239
+ """
240
+ Spin up the Open3D visualizer in editing mode, which allows the user to interactively select points on the geometry.
241
+ After the user finishes editing and closes the visualizer, the edited geometry can be returned as either a new object or by modifying the original object in place.
242
+
243
+ Args:
244
+ inplace (bool, optional): Whether to modify the original geometry in place or return a new geometry object. Defaults to True.
245
+ full_screen (bool, optional): Whether to open the visualizer in full screen mode. Defaults to False.
246
+
247
+ Returns:
248
+ "GeometryBase": The edited geometry object, either the original or a new instance.
249
+ """
250
+
251
+ # the visualizer blocks the execution until the user finishes editing and closes the visualizer,
252
+ # so we can simply return the edited geometry after vis.run() is done
253
+ vis = o3d.visualization.VisualizerWithEditing() # type: ignore
254
+ vis.create_window()
255
+
256
+ # set full screen if requested
257
+ # we put this behind an if statement because else it will raise a warning
258
+ if full_screen:
259
+ vis.set_full_screen(full_screen)
260
+
261
+ # add the geometry to the visualizer and run it
262
+ vis.add_geometry(self._geometry)
263
+ vis.run()
264
+
265
+ # close the visualizer after editing is done
266
+ vis.destroy_window()
267
+
268
+ # chosen points are stored in vis.get_cropped_geometry()
269
+ edited_geometry = vis.get_cropped_geometry()
270
+
271
+ if inplace:
272
+ self._geometry = edited_geometry
273
+ return self
274
+ else:
275
+ return GeometryBase.from_o3d(edited_geometry)
276
+
277
+ # %% Visualization helper functions
278
+ def paint_uniform_color(
279
+ self, color: Union[Tuple[float, float, float], List[float], np.ndarray]
280
+ ) -> None:
281
+ """Paint the geometry with a uniform color.
282
+
283
+ Args:
284
+ color (Union[Tuple[float, float, float], List[float], np.ndarray]): The RGB color to paint the geometry with, where each component is in the range [0, 1].
285
+
286
+ Returns:
287
+ None: This function modifies the geometry in place and does not return anything.
288
+ """
289
+ self._geometry.paint_uniform_color(color)
290
+
291
+ def paint(
292
+ self,
293
+ color_array: np.ndarray,
294
+ cmap: Union[str, matplotlib.colors.Colormap] = "viridis",
295
+ ) -> None:
296
+ """Paint the geometry with a color map.
297
+
298
+ Args:
299
+ color_array (np.ndarray): An array of scalar values to be mapped to colors.
300
+ cmap (Union[str, matplotlib.colors.Colormap], optional): The colormap to use. Defaults to "viridis".
301
+
302
+ Returns:
303
+ None: This function modifies the geometry in place and does not return anything.
304
+ """
305
+
306
+ # select colormap
307
+ if isinstance(cmap, str):
308
+ cmap = matplotlib.colormaps[cmap]
309
+
310
+ # apply colormap to the color array
311
+ norm = matplotlib.colors.Normalize(
312
+ vmin=np.min(color_array), vmax=np.max(color_array)
313
+ )
314
+ color_array = cmap(norm(color_array))[
315
+ :, :3
316
+ ] # apply colormap and take only RGB values
317
+
318
+ # paint the point cloud with the new colors
319
+ self._geometry.colors = o3d.utility.Vector3dVector(color_array)
320
+
321
+ def print_visualizer_key_bindings(self) -> None:
322
+ """
323
+ Print the key bindings for the Open3D visualizer.
324
+ This can be useful for users who are not familiar with the Open3D visualizer and want to know how to interact with it.
325
+ """
326
+
327
+ print(
328
+ """
329
+ -- Mouse view control --
330
+ Left button + drag : Rotate.
331
+ Ctrl + left button + drag : Translate.
332
+ Wheel button + drag : Translate.
333
+ Shift + left button + drag : Roll.
334
+ Wheel : Zoom in/out.
335
+
336
+ -- Keyboard view control --
337
+ [/] : Increase/decrease field of view.
338
+ R : Reset view point.
339
+ Ctrl/Cmd + C : Copy current view status into the clipboard.
340
+ Ctrl/Cmd + V : Paste view status from clipboard.
341
+
342
+ -- General control --
343
+ Q, Esc : Exit window.
344
+ H : Print help message.
345
+ P, PrtScn : Take a screen capture.
346
+ D : Take a depth capture.
347
+ O : Take a capture of current rendering settings.
348
+ Alt + Enter : Toggle between full screen and windowed mode.
349
+
350
+ -- Render mode control --
351
+ L : Turn on/off lighting.
352
+ +/- : Increase/decrease point size.
353
+ Ctrl + +/- : Increase/decrease width of geometry::LineSet.
354
+ N : Turn on/off point cloud normal rendering.
355
+ S : Toggle between mesh flat shading and smooth shading.
356
+ W : Turn on/off mesh wireframe.
357
+ B : Turn on/off back face rendering.
358
+ I : Turn on/off image zoom in interpolation.
359
+ T : Toggle among image render:
360
+ no stretch / keep ratio / freely stretch.
361
+
362
+ -- Color control --
363
+ 0..4,9 : Set point cloud color option.
364
+ 0 - Default behavior, render point color.
365
+ 1 - Render point color.
366
+ 2 - x coordinate as color.
367
+ 3 - y coordinate as color.
368
+ 4 - z coordinate as color.
369
+ 9 - normal as color.
370
+ Ctrl + 0..4,9: Set mesh color option.
371
+ 0 - Default behavior, render uniform gray color.
372
+ 1 - Render point color.
373
+ 2 - x coordinate as color.
374
+ 3 - y coordinate as color.
375
+ 4 - z coordinate as color.
376
+ 9 - normal as color.
377
+ Shift + 0..4 : Color map options.
378
+ 0 - Gray scale color.
379
+ 1 - JET color map.
380
+ 2 - SUMMER color map.
381
+ 3 - WINTER color map.
382
+ 4 - HOT color map.
383
+
384
+ -- Editing control --
385
+ Y : Lock Y-Axis when picking points.
386
+ X : Lock X-Axis when picking points.
387
+ Z : Lock Z-Axis when picking points.
388
+ F : Go back to Free-View mode when picking points.
389
+ K : Lock view and start picking points. Drag to select points, then release to finish picking.
390
+ C : confirm selection of points when picking points. Press K again to start a new selection.
391
+ S : Save selected points to a .ply file when picking points.
392
+ """
393
+ )
394
+
395
+ # %% IO
396
+ @abstractmethod
397
+ def save(self, file_path: Optional[str] = None) -> None:
398
+ """Save the object stored in self._geometry to a PLY file.
399
+ If no file path is provided and on a Windows machine, a file dialog will be opened to select a save location.
400
+
401
+ Args:
402
+ file_path (Optional[str], optional): The path to the file where the object will be saved. Defaults to None.
403
+ """
404
+ pass
405
+
406
+ @abstractmethod
407
+ def load(self, file_path: Optional[str] = None) -> None:
408
+ """Load a geometry object from a PLY file and place it in self._geometry.
409
+ If no file path is provided and on a Windows machine, a file dialog will be opened to select a PLY file.
410
+
411
+ Args:
412
+ file_path (Optional[str], optional): The path to the file from which the object will be loaded. Defaults to None.
413
+ """
414
+ pass
415
+
416
+ # %% Dunder methods
417
+ @abstractmethod
418
+ def __repr__(self) -> str:
419
+ pass
420
+
421
+ @abstractmethod
422
+ def __len__(self) -> int:
423
+ pass
424
+
425
+ # %% Properties
426
+ @property
427
+ @abstractmethod
428
+ def colors(self) -> Union[np.ndarray, o3d.utility.Vector3dVector]:
429
+ """
430
+ Color property of the geometry.
431
+ This can be either a numpy array or an Open3D Vector3dVector,
432
+ depending on the specific geometry type and how colors are stored in that geometry.
433
+ The shape of the color array will depend on the geometry type (e.g. for a point cloud, it will be (N, 3) where N is the number of points, and for a mesh it will be (M, 3) where M is the number of vertices).
434
+
435
+ Returns:
436
+ Union[np.ndarray, o3d.utility.Vector3dVector]: The color array of the geometry.
437
+ """
438
+ pass
439
+
440
+ @colors.setter
441
+ @abstractmethod
442
+ def colors(
443
+ self, color_array: Union[np.ndarray, o3d.utility.Vector3dVector]
444
+ ) -> None:
445
+ """
446
+ Set the color property of the geometry.
447
+
448
+ Args:
449
+ color_array (Union[np.ndarray, o3d.utility.Vector3dVector]): The color array to set.
450
+ """
451
+ pass
452
+
453
+ @property
454
+ def config(self) -> Dict[str, bool]:
455
+ """
456
+ Configuration dictionary for the geometry object.
457
+ This can be used to store various configuration options that affect the behavior of the object, such as verbosity, debug mode, or other flags that can be checked in different methods to modify their behavior.
458
+
459
+ Returns:
460
+ Dict[str, bool]: The configuration dictionary.
461
+ """
462
+ return self._config
463
+
464
+ @config.setter
465
+ def config(self, config_dict: Dict[str, bool]) -> None:
466
+ """
467
+ Set the configuration dictionary for the geometry object.
468
+
469
+ Args:
470
+ config_dict (Dict[str, bool]): The configuration dictionary to set.
471
+ """
472
+ if not isinstance(config_dict, dict):
473
+ raise ValueError("Config must be a dictionary.")
474
+
475
+ self._config = config_dict
476
+
477
+ @property
478
+ def geometry(
479
+ self,
480
+ ) -> Union[
481
+ o3d.geometry.PointCloud, o3d.geometry.TriangleMesh, o3d.geometry.LineSet
482
+ ]:
483
+ """
484
+ The underlying Open3D geometry object that this class wraps around.
485
+ This can be a PointCloud, TriangleMesh, or LineSet depending on the specific subclass.
486
+ This property allows access to the raw Open3D geometry for cases where direct manipulation or access to Open3D functions is needed.
487
+
488
+ Returns:
489
+ Union[o3d.geometry.PointCloud, o3d.geometry.TriangleMesh, o3d.geometry.LineSet]: The underlying Open3D geometry object.
490
+ """
491
+ return self._geometry
492
+
493
+ @geometry.setter
494
+ def geometry(
495
+ self,
496
+ geometry: Union[
497
+ o3d.geometry.PointCloud, o3d.geometry.TriangleMesh, o3d.geometry.LineSet
498
+ ],
499
+ ) -> None:
500
+ """
501
+ Set the underlying Open3D geometry object.
502
+
503
+ Args:
504
+ geometry (Union[o3d.geometry.PointCloud, o3d.geometry.TriangleMesh, o3d.geometry.LineSet]): The Open3D geometry object to set.
505
+ """
506
+ if not isinstance(
507
+ geometry,
508
+ (o3d.geometry.PointCloud, o3d.geometry.TriangleMesh, o3d.geometry.LineSet),
509
+ ):
510
+ raise ValueError(
511
+ "Geometry must be an instance of open3d.geometry.PointCloud, open3d.geometry.TriangleMesh, or open3d.geometry.LineSet."
512
+ )
513
+
514
+ self._geometry = geometry
@@ -0,0 +1,33 @@
1
+ import open3d as o3d
2
+ from typing import List
3
+ from dataclasses import dataclass
4
+
5
+ __all__ = ["Path3D"]
6
+
7
+
8
+ @dataclass
9
+ class Path3D:
10
+ """
11
+ A class to represent a 3D path in the Min3D framework.
12
+ This class contains the geometry of the path as well as additional attributes for visualization purposes.
13
+
14
+ A path is defined as a sequence of 3D points that represent a trajectory or curve in 3D space.
15
+ In our case, we represent the path as a LineSet geometry in Open3D, which consists of a set of points and a set of edges that connect those points.
16
+ Vertices on the path are visualized as 3D meshes of different geometry, with the start and end points highlighted in different colors for better visualization.
17
+ """
18
+
19
+ # base geometry attributes
20
+ edges: o3d.geometry.LineSet
21
+
22
+ # additional attributes for visualization
23
+ start_point: o3d.geometry.TriangleMesh
24
+ end_point: o3d.geometry.TriangleMesh
25
+ intermediate_points: List[o3d.geometry.TriangleMesh]
26
+
27
+ def to_list(self) -> List[o3d.geometry.Geometry]:
28
+ """Forward the geometry attributes of the Path3D object as a list of Open3D geometries for visualization purposes.
29
+
30
+ Returns:
31
+ List[o3d.geometry.Geometry]: A list of Open3D geometries that represent the path, including the edges and the start, end, and intermediate points.
32
+ """
33
+ return [self.edges, self.start_point, self.end_point] + self.intermediate_points
@@ -0,0 +1,20 @@
1
+ ## forward imports for framework module
2
+ from cellnav.core.framework.surface_mesh import SurfaceMesh as SurfaceMesh
3
+ from cellnav.core.framework.surface_graph import SurfaceGraph as SurfaceGraph
4
+ from cellnav.core.framework.surface_wireframe import (
5
+ SurfaceWireframe as SurfaceWireframe,
6
+ )
7
+ from cellnav.core.framework.point_cloud import (
8
+ PointCloud as PointCloud,
9
+ )
10
+ from cellnav.core.framework.unique_surface_wireframe import (
11
+ UniqueSurfaceWireframe as UniqueSurfaceWireframe,
12
+ )
13
+
14
+ __all__ = [
15
+ "SurfaceMesh",
16
+ "SurfaceGraph",
17
+ "SurfaceWireframe",
18
+ "PointCloud",
19
+ "UniqueSurfaceWireframe",
20
+ ]