conquer3d 0.3.2__tar.gz → 0.3.3__tar.gz

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 (97) hide show
  1. {conquer3d-0.3.2/conquer3d.egg-info → conquer3d-0.3.3}/PKG-INFO +19 -1
  2. {conquer3d-0.3.2 → conquer3d-0.3.3}/README.md +18 -0
  3. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/_C.so +0 -0
  4. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/__init__.py +3 -1
  5. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +299 -0
  6. conquer3d-0.3.3/conquer3d/csrc/binds/primitive/ray.cpp +37 -0
  7. conquer3d-0.3.3/conquer3d/csrc/binds/primitive/triangle.cpp +68 -0
  8. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/data_structure/mesh_bvh.cu +1 -1
  9. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/data_structure/triangle_mesh.cu +299 -1
  10. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/data_structure/triangle_mesh.h +71 -0
  11. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/maths/f3x1.h +14 -0
  12. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/primitive/edge.h +7 -0
  13. conquer3d-0.3.2/conquer3d/csrc/primitive/triangles.h → conquer3d-0.3.3/conquer3d/csrc/primitive/triangle.h +156 -6
  14. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/pybind.cpp +4 -0
  15. conquer3d-0.3.3/conquer3d/primitive/__init__.py +3 -0
  16. {conquer3d-0.3.2 → conquer3d-0.3.3/conquer3d.egg-info}/PKG-INFO +19 -1
  17. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d.egg-info/SOURCES.txt +3 -1
  18. {conquer3d-0.3.2 → conquer3d-0.3.3}/pyproject.toml +1 -1
  19. conquer3d-0.3.2/conquer3d/primitive/__init__.py +0 -0
  20. {conquer3d-0.3.2 → conquer3d-0.3.3}/LICENSE +0 -0
  21. {conquer3d-0.3.2 → conquer3d-0.3.3}/MANIFEST.in +0 -0
  22. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/_C.pyi +0 -0
  23. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/conversion/__init__.py +0 -0
  24. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/conversion/grid.py +0 -0
  25. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/conversion/mesh.py +0 -0
  26. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/creation/__init__.py +0 -0
  27. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/creation/triangle_creation.py +0 -0
  28. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
  29. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/binds/data_structure/bvh.cpp +0 -0
  30. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/binds/data_structure/grid.cpp +0 -0
  31. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
  32. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
  33. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +0 -0
  34. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
  35. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/binds/ops/chamfer.cpp +0 -0
  36. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/binds/ops/mc.cpp +0 -0
  37. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
  38. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
  39. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/check.h +0 -0
  40. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/constants.h +0 -0
  41. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/creation/triangle_creation.h +0 -0
  42. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/data_structure/bvh.cu +0 -0
  43. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/data_structure/bvh.h +0 -0
  44. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
  45. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
  46. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/data_structure/kdtree.h +0 -0
  47. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/data_structure/mesh_bvh.h +0 -0
  48. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
  49. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/data_structure/zcurve.h +0 -0
  50. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/maths/f2x2.h +0 -0
  51. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/maths/f3x3.h +0 -0
  52. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/maths/f3x4.h +0 -0
  53. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/maths/f4x1.h +0 -0
  54. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/maths/f4x4.h +0 -0
  55. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/maths/maths.h +0 -0
  56. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/maths/ops.h +0 -0
  57. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/ops/chamfer.cu +0 -0
  58. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/ops/chamfer.h +0 -0
  59. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/ops/mc.cu +0 -0
  60. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/ops/mc.h +0 -0
  61. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/ops/mc_data.h +0 -0
  62. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/primitive/aabb.h +0 -0
  63. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/primitive/gs.cu +0 -0
  64. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/primitive/gs.h +0 -0
  65. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
  66. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
  67. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/primitive/pgs.cu +0 -0
  68. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/primitive/pgs.h +0 -0
  69. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
  70. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
  71. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/csrc/primitive/ray.h +0 -0
  72. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data/__init__.py +0 -0
  73. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data/collate/__init__.py +0 -0
  74. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data/collate/mesh.py +0 -0
  75. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data/collate/sparse_tensor.py +0 -0
  76. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data/dataset/__init__.py +0 -0
  77. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data/dataset/base_mesh.py +0 -0
  78. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data/dataset/digit3d.py +0 -0
  79. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data/transform/__init__.py +0 -0
  80. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data/transform/base.py +0 -0
  81. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data/transform/ops.py +0 -0
  82. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data/transform/vertex.py +0 -0
  83. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data_structure/__init__.py +0 -0
  84. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data_structure/bmesh.py +0 -0
  85. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/data_structure/grid.py +0 -0
  86. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/ops/__init__.py +0 -0
  87. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/ops/diff_marching_cubes.py +0 -0
  88. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/ops/distance.py +0 -0
  89. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/ops/marching_cubes.py +0 -0
  90. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/primitive/gs.py +0 -0
  91. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d/primitive/pgs.py +0 -0
  92. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d.egg-info/dependency_links.txt +0 -0
  93. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d.egg-info/not-zip-safe +0 -0
  94. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d.egg-info/requires.txt +0 -0
  95. {conquer3d-0.3.2 → conquer3d-0.3.3}/conquer3d.egg-info/top_level.txt +0 -0
  96. {conquer3d-0.3.2 → conquer3d-0.3.3}/setup.cfg +0 -0
  97. {conquer3d-0.3.2 → conquer3d-0.3.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conquer3d
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Geometric Cuda Tool Box
5
5
  Author-email: Do Hoang Khoi <khoido8899@gmail.com>
6
6
  License-Expression: MIT
@@ -131,6 +131,24 @@ twine upload dist/* --verbose
131
131
  }
132
132
  ```
133
133
 
134
+ ```bibtex
135
+ @InProceedings{10.1007/978-3-662-05105-4_2,
136
+ author="Meyer, Mark
137
+ and Desbrun, Mathieu
138
+ and Schr{\"o}der, Peter
139
+ and Barr, Alan H.",
140
+ editor="Hege, Hans-Christian
141
+ and Polthier, Konrad",
142
+ title="Discrete Differential-Geometry Operators for Triangulated 2-Manifolds",
143
+ booktitle="Visualization and Mathematics III",
144
+ year="2003",
145
+ publisher="Springer Berlin Heidelberg",
146
+ address="Berlin, Heidelberg",
147
+ pages="35--57",
148
+ isbn="978-3-662-05105-4"
149
+ }
150
+ ```
151
+
134
152
  ## Blog Post
135
153
  - [Thinking Parallel, Part I: Collision Detection on the GPU](https://developer.nvidia.com/blog/thinking-parallel-part-i-collision-detection-gpu/)
136
154
  - [Thinking Parallel, Part II: Tree Traversal on the GPU](https://developer.nvidia.com/blog/thinking-parallel-part-ii-tree-traversal-gpu/)
@@ -102,6 +102,24 @@ twine upload dist/* --verbose
102
102
  }
103
103
  ```
104
104
 
105
+ ```bibtex
106
+ @InProceedings{10.1007/978-3-662-05105-4_2,
107
+ author="Meyer, Mark
108
+ and Desbrun, Mathieu
109
+ and Schr{\"o}der, Peter
110
+ and Barr, Alan H.",
111
+ editor="Hege, Hans-Christian
112
+ and Polthier, Konrad",
113
+ title="Discrete Differential-Geometry Operators for Triangulated 2-Manifolds",
114
+ booktitle="Visualization and Mathematics III",
115
+ year="2003",
116
+ publisher="Springer Berlin Heidelberg",
117
+ address="Berlin, Heidelberg",
118
+ pages="35--57",
119
+ isbn="978-3-662-05105-4"
120
+ }
121
+ ```
122
+
105
123
  ## Blog Post
106
124
  - [Thinking Parallel, Part I: Collision Detection on the GPU](https://developer.nvidia.com/blog/thinking-parallel-part-i-collision-detection-gpu/)
107
125
  - [Thinking Parallel, Part II: Tree Traversal on the GPU](https://developer.nvidia.com/blog/thinking-parallel-part-ii-tree-traversal-gpu/)
@@ -7,4 +7,6 @@ from . import primitive
7
7
  from . import ops
8
8
  from . import conversion
9
9
 
10
- __all__ = ['_C', 'creation', 'data_structure', 'primitive', 'ops', 'conversion']
10
+ from .primitive import Triangle, Ray
11
+
12
+ __all__ = ['_C', 'creation', 'data_structure', 'primitive', 'ops', 'conversion', 'Triangle', 'Ray']
@@ -77,6 +77,238 @@ torch::Tensor TriangleMesh::get_vertex_normals()
77
77
  return this->vertex_normals;
78
78
  }
79
79
 
80
+ void TriangleMesh::compute_vertex_degrees()
81
+ {
82
+ if (this->vertex_degrees.defined()) return;
83
+
84
+ if (!this->edges.defined()) {
85
+ this->compute_edges_to_triangle_map();
86
+ }
87
+
88
+ uint32_t num_vertices = this->vertices.size(0);
89
+ this->vertex_degrees = torch::zeros({static_cast<int64_t>(num_vertices)}, torch::dtype(torch::kInt32).device(this->vertices.device()));
90
+
91
+ uint32_t num_edges = this->edges.size(0);
92
+ triangle_mesh::compute_vertex_degree(
93
+ num_edges,
94
+ reinterpret_cast<const int *>(this->edges.data_ptr<int>()),
95
+ reinterpret_cast<int *>(this->vertex_degrees.data_ptr<int>()));
96
+ }
97
+
98
+ torch::Tensor TriangleMesh::get_vertex_degrees()
99
+ {
100
+ if (!this->vertex_degrees.defined())
101
+ {
102
+ this->compute_vertex_degrees();
103
+ }
104
+ return this->vertex_degrees;
105
+ }
106
+
107
+ void TriangleMesh::compute_vertex_lb_uniform()
108
+ {
109
+ if (this->vertex_lb_uniform.defined()) return;
110
+
111
+ if (!this->edges.defined()) {
112
+ this->compute_edges_to_triangle_map();
113
+ }
114
+ if (!this->vertex_degrees.defined()) {
115
+ this->compute_vertex_degrees();
116
+ }
117
+
118
+ uint32_t num_vertices = this->vertices.size(0);
119
+ this->vertex_lb_uniform = torch::zeros({static_cast<int64_t>(num_vertices), 3}, torch::dtype(torch::kFloat32).device(this->vertices.device()));
120
+
121
+ uint32_t num_edges = this->edges.size(0);
122
+ triangle_mesh::compute_uniform_laplacian(
123
+ num_vertices,
124
+ num_edges,
125
+ reinterpret_cast<const int *>(this->edges.data_ptr<int>()),
126
+ reinterpret_cast<const int *>(this->vertex_degrees.data_ptr<int>()),
127
+ reinterpret_cast<const float3 *>(this->vertices.data_ptr<float>()),
128
+ reinterpret_cast<float3 *>(this->vertex_lb_uniform.data_ptr<float>()));
129
+ }
130
+
131
+ torch::Tensor TriangleMesh::get_vertex_lb_uniform()
132
+ {
133
+ if (!this->vertex_lb_uniform.defined())
134
+ {
135
+ this->compute_vertex_lb_uniform();
136
+ }
137
+ return this->vertex_lb_uniform;
138
+ }
139
+
140
+ torch::Tensor TriangleMesh::compute_laplacian(int mode) {
141
+ if (mode == 0) {
142
+ return this->get_vertex_lb_uniform();
143
+ } else if (mode == 1) {
144
+ return this->get_vertex_lb_cotangent();
145
+ } else {
146
+ throw std::runtime_error("Unsupported laplacian mode. Use 0 for Uniform, 1 for Cotangent.");
147
+ }
148
+ }
149
+
150
+ torch::Tensor TriangleMesh::get_mean_curvature(bool signed_curvature) {
151
+ // mode 1 is cotangent laplacian
152
+ torch::Tensor lb_cotangent = this->compute_laplacian(1);
153
+
154
+ if (!signed_curvature) {
155
+ // Absolute Mean Curvature: || 2 * H * n || / 2.0 = |H|
156
+ return torch::norm(lb_cotangent, 2, 1) / 2.0f;
157
+ } else {
158
+ // Signed Mean Curvature: dot(2 * H * n, n) / 2.0 = H
159
+ torch::Tensor v_normals = this->get_vertex_normals();
160
+ return torch::sum(lb_cotangent * v_normals, 1) / 2.0f;
161
+ }
162
+ }
163
+
164
+ torch::Tensor TriangleMesh::get_principal_curvatures(bool signed_curvature) {
165
+ torch::Tensor H = this->get_mean_curvature(signed_curvature);
166
+ torch::Tensor K = this->get_gaussian_curvature();
167
+
168
+ // In discrete settings, floating point inaccuracies can rarely cause H^2 < K.
169
+ // We use torch::relu to clamp negative values to 0 before sqrt to prevent NaNs.
170
+ torch::Tensor delta = torch::relu(H * H - K);
171
+ torch::Tensor sqrt_delta = torch::sqrt(delta);
172
+
173
+ torch::Tensor k1 = H + sqrt_delta;
174
+ torch::Tensor k2 = H - sqrt_delta;
175
+
176
+ return torch::stack({k1, k2}, 1);
177
+ }
178
+
179
+ void TriangleMesh::compute_voronoi_areas()
180
+ {
181
+ if (this->voronoi_areas.defined()) return;
182
+
183
+ uint32_t num_vertices = this->vertices.size(0);
184
+ this->voronoi_areas = torch::zeros({static_cast<int64_t>(num_vertices)}, torch::dtype(torch::kFloat32).device(this->vertices.device()));
185
+ uint32_t num_triangles = this->triangles.size(0);
186
+
187
+ triangle_mesh::compute_voronoi_areas(
188
+ num_triangles,
189
+ reinterpret_cast<const int3 *>(this->triangles.data_ptr<int>()),
190
+ reinterpret_cast<const float3 *>(this->vertices.data_ptr<float>()),
191
+ reinterpret_cast<float *>(this->voronoi_areas.data_ptr<float>()));
192
+ }
193
+
194
+ void TriangleMesh::compute_gaussian_curvature()
195
+ {
196
+ if (this->gaussian_curvature.defined()) return;
197
+
198
+ if (!this->voronoi_areas.defined()) {
199
+ this->compute_voronoi_areas();
200
+ }
201
+
202
+ uint32_t num_vertices = this->vertices.size(0);
203
+ this->gaussian_curvature = torch::zeros({static_cast<int64_t>(num_vertices)}, torch::dtype(torch::kFloat32).device(this->vertices.device()));
204
+
205
+ uint32_t num_triangles = this->triangles.size(0);
206
+ auto vertex_angle_sum = torch::zeros({static_cast<int64_t>(num_vertices)}, torch::dtype(torch::kFloat32).device(this->vertices.device()));
207
+
208
+ triangle_mesh::compute_gaussian_curvature(
209
+ num_vertices,
210
+ num_triangles,
211
+ reinterpret_cast<const int3 *>(this->triangles.data_ptr<int>()),
212
+ reinterpret_cast<const float3 *>(this->vertices.data_ptr<float>()),
213
+ reinterpret_cast<const float *>(this->voronoi_areas.data_ptr<float>()),
214
+ reinterpret_cast<float *>(vertex_angle_sum.data_ptr<float>()),
215
+ reinterpret_cast<float *>(this->gaussian_curvature.data_ptr<float>()));
216
+ }
217
+
218
+ torch::Tensor TriangleMesh::get_gaussian_curvature()
219
+ {
220
+ if (!this->gaussian_curvature.defined())
221
+ {
222
+ this->compute_gaussian_curvature();
223
+ }
224
+ return this->gaussian_curvature;
225
+ }
226
+ void TriangleMesh::compute_vertex_lb_cotangent()
227
+ {
228
+ if (this->vertex_lb_cotangent.defined()) return;
229
+
230
+ if (!this->voronoi_areas.defined()) {
231
+ this->compute_voronoi_areas();
232
+ }
233
+
234
+ uint32_t num_vertices = this->vertices.size(0);
235
+ this->vertex_lb_cotangent = torch::zeros({static_cast<int64_t>(num_vertices), 3}, torch::dtype(torch::kFloat32).device(this->vertices.device()));
236
+
237
+ uint32_t num_triangles = this->triangles.size(0);
238
+ triangle_mesh::compute_cotangent_laplacian(
239
+ num_vertices,
240
+ num_triangles,
241
+ reinterpret_cast<const int3 *>(this->triangles.data_ptr<int>()),
242
+ reinterpret_cast<const float3 *>(this->vertices.data_ptr<float>()),
243
+ reinterpret_cast<float *>(this->voronoi_areas.data_ptr<float>()),
244
+ reinterpret_cast<float3 *>(this->vertex_lb_cotangent.data_ptr<float>()));
245
+ }
246
+
247
+ torch::Tensor TriangleMesh::get_vertex_lb_cotangent()
248
+ {
249
+ if (!this->vertex_lb_cotangent.defined())
250
+ {
251
+ this->compute_vertex_lb_cotangent();
252
+ }
253
+ return this->vertex_lb_cotangent;
254
+ }
255
+
256
+ torch::Tensor TriangleMesh::get_voronoi_areas()
257
+ {
258
+ if (!this->voronoi_areas.defined())
259
+ {
260
+ this->compute_voronoi_areas();
261
+ }
262
+ return this->voronoi_areas;
263
+ }
264
+
265
+ torch::Tensor TriangleMesh::get_isolated_vertices() {
266
+ torch::Tensor degrees = this->get_vertex_degrees();
267
+ return torch::nonzero(degrees == 0).squeeze(1);
268
+ }
269
+
270
+ int32_t TriangleMesh::get_num_isolated_vertices() {
271
+ return this->get_isolated_vertices().size(0);
272
+ }
273
+
274
+ void TriangleMesh::remove_isolated_vertices() {
275
+ torch::Tensor degrees = this->get_vertex_degrees();
276
+ torch::Tensor keep_mask = degrees > 0;
277
+
278
+ int64_t num_kept = keep_mask.sum().item<int64_t>();
279
+ if (num_kept == this->vertices.size(0)) {
280
+ return;
281
+ }
282
+
283
+ // Create old to new mapping. Unused vertices map to -1.
284
+ torch::Tensor cumsum = torch::cumsum(keep_mask.to(torch::kInt32), 0);
285
+ torch::Tensor old_to_new = (cumsum - 1).masked_fill_(~keep_mask, -1);
286
+
287
+ // Filter vertices
288
+ this->vertices = this->vertices.index({keep_mask});
289
+
290
+ // Update triangles
291
+ torch::Tensor flat_tris = this->triangles.to(torch::kInt64).view({-1});
292
+ this->triangles = old_to_new.index_select(0, flat_tris).view({-1, 3}).to(torch::kInt32);
293
+
294
+ // Invalidate all caches
295
+ this->triangle_areas = torch::Tensor();
296
+ this->triangle_normals = torch::Tensor();
297
+ this->surface_area = torch::Tensor();
298
+ this->bvh.reset();
299
+ this->edges = torch::Tensor();
300
+ this->edge_to_triangle_offsets = torch::Tensor();
301
+ this->edge_to_triangle_counts = torch::Tensor();
302
+ this->edge_to_triangle_indices = torch::Tensor();
303
+ this->vertex_to_triangle_offsets = torch::Tensor();
304
+ this->vertex_to_triangle_counts = torch::Tensor();
305
+ this->vertex_to_triangle_indices = torch::Tensor();
306
+ this->vertex_degrees = torch::Tensor();
307
+ this->vertex_lb_uniform = torch::Tensor();
308
+ this->vertex_lb_cotangent = torch::Tensor();
309
+ this->voronoi_areas = torch::Tensor();
310
+ }
311
+
80
312
  void TriangleMesh::compute_triangle_areas()
81
313
  {
82
314
  this->triangle_areas = torch::empty({static_cast<int64_t>(this->num_triangles)}, torch::dtype(torch::kFloat32).device(this->vertices.device()));
@@ -342,6 +574,11 @@ void TriangleMesh::remove_triangles_by_mask(const torch::Tensor &keep_mask)
342
574
  this->vertex_to_triangle_offsets = torch::Tensor();
343
575
  this->vertex_to_triangle_counts = torch::Tensor();
344
576
  this->vertex_to_triangle_indices = torch::Tensor();
577
+
578
+ this->vertex_degrees = torch::Tensor();
579
+ this->vertex_lb_uniform = torch::Tensor();
580
+ this->vertex_lb_cotangent = torch::Tensor();
581
+ this->voronoi_areas = torch::Tensor();
345
582
  }
346
583
 
347
584
  int32_t TriangleMesh::get_euler_characteristic()
@@ -460,6 +697,18 @@ void bind_ds_triangle_mesh(py::module_ &m)
460
697
  Returns:
461
698
  torch.Tensor - Shape (N, 3) float32 tensor of vertex colors.
462
699
  )doc")
700
+ .def_property_readonly("vertex_degrees", &TriangleMesh::get_vertex_degrees, R"doc(
701
+ Degree of each vertex (number of incident edges).
702
+
703
+ Returns:
704
+ torch.Tensor - Shape (N,) int32 tensor of vertex degrees.
705
+ )doc")
706
+ .def_property_readonly("vertex_lb_uniform", &TriangleMesh::get_vertex_lb_uniform, R"doc(
707
+ Uniform Laplace-Beltrami operator evaluated at each vertex.
708
+
709
+ Returns:
710
+ torch.Tensor - Shape (N, 3) float32 tensor of uniform laplacian vectors.
711
+ )doc")
463
712
  .def_property_readonly("triangles", &TriangleMesh::get_triangles, R"doc(
464
713
  Mesh triangles.
465
714
 
@@ -629,6 +878,56 @@ void bind_ds_triangle_mesh(py::module_ &m)
629
878
  Returns:
630
879
  torch.Tensor: Tensor of vertex indices that are non-manifold.
631
880
  )doc")
881
+ .def("get_isolated_vertices", &TriangleMesh::get_isolated_vertices, R"doc(
882
+ Gets the indices of all isolated vertices (degree 0).
883
+
884
+ Returns:
885
+ torch.Tensor - 1D tensor of isolated vertex indices.
886
+ )doc")
887
+ .def_property_readonly("num_isolated_vertices", &TriangleMesh::get_num_isolated_vertices, R"doc(
888
+ Gets the total number of isolated vertices.
889
+
890
+ Returns:
891
+ int - The total number of isolated vertices.
892
+ )doc")
893
+ .def("remove_isolated_vertices", &TriangleMesh::remove_isolated_vertices, R"doc(
894
+ Removes all isolated vertices from the mesh and reindexes the triangles.
895
+ )doc")
896
+ .def("get_voronoi_areas", &TriangleMesh::get_voronoi_areas, R"doc(
897
+ Get the per-vertex voronoi areas.
898
+ )doc")
899
+ .def("get_gaussian_curvature", &TriangleMesh::get_gaussian_curvature, R"doc(
900
+ Compute and return the discrete Gaussian curvature (angle deficit) at each vertex.
901
+ )doc")
902
+ .def("get_mean_curvature", &TriangleMesh::get_mean_curvature, py::arg("signed_curvature") = false, R"doc(
903
+ Compute and return the discrete Mean curvature at each vertex using the Laplace-Beltrami operator.
904
+ If signed_curvature is true, returns H. If false, returns |H|.
905
+ )doc")
906
+ .def("get_principal_curvatures", &TriangleMesh::get_principal_curvatures, py::arg("signed_curvature") = true, R"doc(
907
+ Compute and return the two principal curvatures (k1, k2) at each vertex as an [N, 2] tensor.
908
+ k1 is the maximum principal curvature, k2 is the minimum principal curvature.
909
+ )doc")
910
+ .def("compute_laplacian", &TriangleMesh::compute_laplacian, py::arg("mode") = 0, R"doc(
911
+ Computes the Laplace-Beltrami operator.
912
+
913
+ Args:
914
+ mode (int): The laplacian mode. 0 for Uniform, 1 for Cotangent.
915
+
916
+ Returns:
917
+ torch.Tensor - Shape (N, 3) float32 tensor of laplacian vectors.
918
+ )doc")
919
+ .def_property_readonly("vertex_lb_cotangent", &TriangleMesh::get_vertex_lb_cotangent, R"doc(
920
+ Cotangent Laplace-Beltrami operator evaluated at each vertex.
921
+
922
+ Returns:
923
+ torch.Tensor - Shape (N, 3) float32 tensor.
924
+ )doc")
925
+ .def_property_readonly("voronoi_areas", &TriangleMesh::get_voronoi_areas, R"doc(
926
+ Vertex voronoi areas (1/3 of the sum of incident triangle areas).
927
+
928
+ Returns:
929
+ torch.Tensor - Shape (N,) float32 tensor.
930
+ )doc")
632
931
  .def("remove_triangles_by_mask", &TriangleMesh::remove_triangles_by_mask, py::arg("keep_mask"), R"doc(
633
932
  Removes triangles from the mesh based on a boolean mask.
634
933
 
@@ -0,0 +1,37 @@
1
+ #include <torch/extension.h>
2
+ #include "../../primitive/ray.h"
3
+ #include "../../check.h"
4
+ #include <pybind11/pybind11.h>
5
+
6
+ namespace py = pybind11;
7
+
8
+ inline float3 tensor_to_float3_ray(const torch::Tensor& t) {
9
+ TORCH_CHECK(t.dim() == 1 && t.size(0) == 3, "Tensor must be 1D with 3 elements");
10
+ auto t_contig = t.contiguous().cpu().to(torch::kFloat32);
11
+ float* ptr = t_contig.data_ptr<float>();
12
+ return make_float3(ptr[0], ptr[1], ptr[2]);
13
+ }
14
+
15
+ inline torch::Tensor float3_to_tensor_ray(const float3& f) {
16
+ return torch::tensor({f.x, f.y, f.z}, torch::dtype(torch::kFloat32));
17
+ }
18
+
19
+ void bind_primitive_ray(py::module_& m) {
20
+ py::class_<Ray>(m, "Ray")
21
+ .def(py::init([](const torch::Tensor& origin, const torch::Tensor& dir) {
22
+ return Ray(tensor_to_float3_ray(origin), tensor_to_float3_ray(dir));
23
+ }), py::arg("origin"), py::arg("direction"))
24
+ .def_property_readonly("origin", [](const Ray& self) { return float3_to_tensor_ray(self.origin); })
25
+ .def_property_readonly("direction", [](const Ray& self) { return float3_to_tensor_ray(self.direction); })
26
+ .def_property_readonly("inv_direction", [](const Ray& self) { return float3_to_tensor_ray(self.inv_direction); })
27
+ .def_property_readonly("t_min", [](const Ray& self) { return self.t_min; })
28
+ .def_property_readonly("t_max", [](const Ray& self) { return self.t_max; })
29
+ .def("at", [](const Ray& self, float t) {
30
+ return float3_to_tensor_ray(self.at(t));
31
+ }, py::arg("t"))
32
+ .def("is_intersect_aabb", [](const Ray& self, const torch::Tensor& aabb_min, const torch::Tensor& aabb_max) {
33
+ float t_hit;
34
+ bool hit = self.is_intersect_aabb(tensor_to_float3_ray(aabb_min), tensor_to_float3_ray(aabb_max), t_hit);
35
+ return py::make_tuple(hit, t_hit);
36
+ }, py::arg("aabb_min"), py::arg("aabb_max"));
37
+ }
@@ -0,0 +1,68 @@
1
+ #include <torch/extension.h>
2
+ #include "../../primitive/triangle.h"
3
+ #include "../../check.h"
4
+ #include <pybind11/pybind11.h>
5
+
6
+ namespace py = pybind11;
7
+
8
+ inline float3 tensor_to_float3(const torch::Tensor& t) {
9
+ TORCH_CHECK(t.dim() == 1 && t.size(0) == 3, "Tensor must be 1D with 3 elements");
10
+ auto t_contig = t.contiguous().cpu().to(torch::kFloat32);
11
+ float* ptr = t_contig.data_ptr<float>();
12
+ return make_float3(ptr[0], ptr[1], ptr[2]);
13
+ }
14
+
15
+ inline torch::Tensor float3_to_tensor(const float3& f) {
16
+ return torch::tensor({f.x, f.y, f.z}, torch::dtype(torch::kFloat32));
17
+ }
18
+
19
+ void bind_primitive_triangle(py::module_& m) {
20
+ py::class_<Triangle>(m, "Triangle")
21
+ .def(py::init([](const torch::Tensor& v0, const torch::Tensor& v1, const torch::Tensor& v2) {
22
+ return Triangle(tensor_to_float3(v0), tensor_to_float3(v1), tensor_to_float3(v2));
23
+ }), py::arg("v0"), py::arg("v1"), py::arg("v2"))
24
+ .def("is_intersect_ray", [](const Triangle& self, const Ray& ray) {
25
+ float t_hit, u, v;
26
+ bool hit = self.is_intersect_ray(ray, t_hit, u, v);
27
+ return py::make_tuple(hit, t_hit, u, v);
28
+ }, py::arg("ray"))
29
+ .def("compute_closest_point", [](const Triangle& self, const torch::Tensor& p) {
30
+ return float3_to_tensor(self.compute_closest_point(tensor_to_float3(p)));
31
+ }, py::arg("p"))
32
+ .def("compute_normal", [](const Triangle& self) {
33
+ return float3_to_tensor(self.compute_normal());
34
+ })
35
+ .def("compute_area", &Triangle::compute_area)
36
+ .def("sample_point", [](const Triangle& self, float r1, float r2) {
37
+ return float3_to_tensor(self.sample_point(r1, r2));
38
+ }, py::arg("r1"), py::arg("r2"))
39
+ .def("compute_aabb", [](const Triangle& self) {
40
+ float3 aabb_min, aabb_max;
41
+ self.compute_aabb(aabb_min, aabb_max);
42
+ return py::make_tuple(float3_to_tensor(aabb_min), float3_to_tensor(aabb_max));
43
+ })
44
+ .def("test_intersection", [](const Triangle& self, const Triangle& other) {
45
+ return self.test_intersection(other);
46
+ }, py::arg("other"))
47
+ .def("is_obtuse", [](const Triangle& self) {
48
+ return self.is_obtuse();
49
+ })
50
+ .def("compute_centroid", [](const Triangle& self) {
51
+ return float3_to_tensor(self.compute_centroid());
52
+ })
53
+ .def("compute_circumcenter", [](const Triangle& self, bool strict_inside) {
54
+ return float3_to_tensor(self.compute_circumcenter(strict_inside));
55
+ }, py::arg("strict_inside") = false)
56
+ .def("test_point_on_tria_plane", [](const Triangle& self, const torch::Tensor& p, float eps) {
57
+ return self.test_point_on_tria_plane(tensor_to_float3(p), eps);
58
+ }, py::arg("p"), py::arg("eps") = 1e-5f)
59
+ .def("test_point_inside_on_tria_plane", [](const Triangle& self, const torch::Tensor& p) {
60
+ return self.test_point_inside_on_tria_plane(tensor_to_float3(p));
61
+ }, py::arg("p"))
62
+ .def("test_point_inside", [](const Triangle& self, const torch::Tensor& p, float eps) {
63
+ return self.test_point_inside(tensor_to_float3(p), eps);
64
+ }, py::arg("p"), py::arg("eps") = 1e-5f)
65
+ .def_property_readonly("v0", [](const Triangle& self) { return float3_to_tensor(self.v0); })
66
+ .def_property_readonly("v1", [](const Triangle& self) { return float3_to_tensor(self.v1); })
67
+ .def_property_readonly("v2", [](const Triangle& self) { return float3_to_tensor(self.v2); });
68
+ }
@@ -1,5 +1,5 @@
1
1
  #include "mesh_bvh.h"
2
- #include "../primitive/triangles.h"
2
+ #include "../primitive/triangle.h"
3
3
  #include <thrust/device_vector.h>
4
4
 
5
5
  namespace mesh_bvh