conquer3d 0.3.2__tar.gz → 0.3.4__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 (105) hide show
  1. conquer3d-0.3.4/MANIFEST.in +1 -0
  2. {conquer3d-0.3.2/conquer3d.egg-info → conquer3d-0.3.4}/PKG-INFO +23 -1
  3. {conquer3d-0.3.2 → conquer3d-0.3.4}/README.md +23 -1
  4. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/_C.so +0 -0
  5. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/__init__.py +3 -1
  6. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +299 -0
  7. conquer3d-0.3.4/conquer3d/csrc/binds/primitive/ray.cpp +37 -0
  8. conquer3d-0.3.4/conquer3d/csrc/binds/primitive/triangle.cpp +68 -0
  9. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/data_structure/mesh_bvh.cu +1 -1
  10. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/data_structure/triangle_mesh.cu +299 -1
  11. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/data_structure/triangle_mesh.h +71 -0
  12. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/maths/f3x1.h +14 -0
  13. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/ops/mc.cu +4 -11
  14. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/ops/mc.h +1 -0
  15. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/primitive/edge.h +7 -0
  16. conquer3d-0.3.2/conquer3d/csrc/primitive/triangles.h → conquer3d-0.3.4/conquer3d/csrc/primitive/triangle.h +156 -6
  17. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/pybind.cpp +4 -0
  18. conquer3d-0.3.4/conquer3d/data/assets/common.py +132 -0
  19. conquer3d-0.3.4/conquer3d/data/assets/iphigenia.py +43 -0
  20. conquer3d-0.3.4/conquer3d/io/__init__.py +0 -0
  21. conquer3d-0.3.4/conquer3d/io/obj.py +46 -0
  22. conquer3d-0.3.4/conquer3d/io/off.py +84 -0
  23. conquer3d-0.3.4/conquer3d/ops/delaunay_triangulation.py +71 -0
  24. conquer3d-0.3.4/conquer3d/ops/marching_tetrahedra.py +125 -0
  25. conquer3d-0.3.4/conquer3d/primitive/__init__.py +3 -0
  26. {conquer3d-0.3.2 → conquer3d-0.3.4/conquer3d.egg-info}/PKG-INFO +23 -1
  27. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d.egg-info/SOURCES.txt +11 -1
  28. {conquer3d-0.3.2 → conquer3d-0.3.4}/pyproject.toml +1 -1
  29. {conquer3d-0.3.2 → conquer3d-0.3.4}/setup.py +4 -0
  30. conquer3d-0.3.2/MANIFEST.in +0 -1
  31. {conquer3d-0.3.2 → conquer3d-0.3.4}/LICENSE +0 -0
  32. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/_C.pyi +0 -0
  33. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/conversion/__init__.py +0 -0
  34. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/conversion/grid.py +0 -0
  35. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/conversion/mesh.py +0 -0
  36. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/creation/__init__.py +0 -0
  37. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/creation/triangle_creation.py +0 -0
  38. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
  39. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/binds/data_structure/bvh.cpp +0 -0
  40. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/binds/data_structure/grid.cpp +0 -0
  41. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
  42. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
  43. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +0 -0
  44. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
  45. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/binds/ops/chamfer.cpp +0 -0
  46. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/binds/ops/mc.cpp +0 -0
  47. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
  48. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
  49. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/check.h +0 -0
  50. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/constants.h +0 -0
  51. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/creation/triangle_creation.h +0 -0
  52. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/data_structure/bvh.cu +0 -0
  53. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/data_structure/bvh.h +0 -0
  54. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
  55. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
  56. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/data_structure/kdtree.h +0 -0
  57. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/data_structure/mesh_bvh.h +0 -0
  58. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
  59. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/data_structure/zcurve.h +0 -0
  60. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/maths/f2x2.h +0 -0
  61. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/maths/f3x3.h +0 -0
  62. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/maths/f3x4.h +0 -0
  63. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/maths/f4x1.h +0 -0
  64. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/maths/f4x4.h +0 -0
  65. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/maths/maths.h +0 -0
  66. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/maths/ops.h +0 -0
  67. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/ops/chamfer.cu +0 -0
  68. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/ops/chamfer.h +0 -0
  69. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/ops/mc_data.h +0 -0
  70. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/primitive/aabb.h +0 -0
  71. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/primitive/gs.cu +0 -0
  72. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/primitive/gs.h +0 -0
  73. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
  74. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
  75. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/primitive/pgs.cu +0 -0
  76. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/primitive/pgs.h +0 -0
  77. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
  78. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
  79. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/csrc/primitive/ray.h +0 -0
  80. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/data/__init__.py +0 -0
  81. {conquer3d-0.3.2/conquer3d/data/collate → conquer3d-0.3.4/conquer3d/data/assets}/__init__.py +0 -0
  82. {conquer3d-0.3.2/conquer3d/data/dataset → conquer3d-0.3.4/conquer3d/data/collate}/__init__.py +0 -0
  83. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/data/collate/mesh.py +0 -0
  84. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/data/collate/sparse_tensor.py +0 -0
  85. {conquer3d-0.3.2/conquer3d/data/transform → conquer3d-0.3.4/conquer3d/data/dataset}/__init__.py +0 -0
  86. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/data/dataset/base_mesh.py +0 -0
  87. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/data/dataset/digit3d.py +0 -0
  88. {conquer3d-0.3.2/conquer3d/primitive → conquer3d-0.3.4/conquer3d/data/transform}/__init__.py +0 -0
  89. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/data/transform/base.py +0 -0
  90. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/data/transform/ops.py +0 -0
  91. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/data/transform/vertex.py +0 -0
  92. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/data_structure/__init__.py +0 -0
  93. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/data_structure/bmesh.py +0 -0
  94. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/data_structure/grid.py +0 -0
  95. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/ops/__init__.py +0 -0
  96. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/ops/diff_marching_cubes.py +0 -0
  97. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/ops/distance.py +0 -0
  98. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/ops/marching_cubes.py +0 -0
  99. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/primitive/gs.py +0 -0
  100. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d/primitive/pgs.py +0 -0
  101. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d.egg-info/dependency_links.txt +0 -0
  102. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d.egg-info/not-zip-safe +0 -0
  103. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d.egg-info/requires.txt +0 -0
  104. {conquer3d-0.3.2 → conquer3d-0.3.4}/conquer3d.egg-info/top_level.txt +0 -0
  105. {conquer3d-0.3.2 → conquer3d-0.3.4}/setup.cfg +0 -0
@@ -0,0 +1 @@
1
+ recursive-include conquer3d/csrc *.h *.cuh *.cpp *.cu
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conquer3d
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: Geometric Cuda Tool Box
5
5
  Author-email: Do Hoang Khoi <khoido8899@gmail.com>
6
6
  License-Expression: MIT
@@ -77,6 +77,9 @@ twine upload dist/* --verbose
77
77
 
78
78
  # Reference
79
79
 
80
+ ## Book
81
+
82
+
80
83
  ## Research Paper
81
84
  ```bibtex
82
85
  @inproceedings{2383795.2383801,
@@ -131,6 +134,24 @@ twine upload dist/* --verbose
131
134
  }
132
135
  ```
133
136
 
137
+ ```bibtex
138
+ @InProceedings{10.1007/978-3-662-05105-4_2,
139
+ author="Meyer, Mark
140
+ and Desbrun, Mathieu
141
+ and Schr{\"o}der, Peter
142
+ and Barr, Alan H.",
143
+ editor="Hege, Hans-Christian
144
+ and Polthier, Konrad",
145
+ title="Discrete Differential-Geometry Operators for Triangulated 2-Manifolds",
146
+ booktitle="Visualization and Mathematics III",
147
+ year="2003",
148
+ publisher="Springer Berlin Heidelberg",
149
+ address="Berlin, Heidelberg",
150
+ pages="35--57",
151
+ isbn="978-3-662-05105-4"
152
+ }
153
+ ```
154
+
134
155
  ## Blog Post
135
156
  - [Thinking Parallel, Part I: Collision Detection on the GPU](https://developer.nvidia.com/blog/thinking-parallel-part-i-collision-detection-gpu/)
136
157
  - [Thinking Parallel, Part II: Tree Traversal on the GPU](https://developer.nvidia.com/blog/thinking-parallel-part-ii-tree-traversal-gpu/)
@@ -143,3 +164,4 @@ twine upload dist/* --verbose
143
164
  - [Pytorch3D](https://github.com/facebookresearch/pytorch3d)
144
165
  - [Open3D](https://www.open3d.org/)
145
166
  - [trimesh](https://github.com/mikedh/trimesh)
167
+ - [TetWeave](https://github.com/AlexandreBinninger/TetWeave/tree/main)
@@ -48,6 +48,9 @@ twine upload dist/* --verbose
48
48
 
49
49
  # Reference
50
50
 
51
+ ## Book
52
+
53
+
51
54
  ## Research Paper
52
55
  ```bibtex
53
56
  @inproceedings{2383795.2383801,
@@ -102,6 +105,24 @@ twine upload dist/* --verbose
102
105
  }
103
106
  ```
104
107
 
108
+ ```bibtex
109
+ @InProceedings{10.1007/978-3-662-05105-4_2,
110
+ author="Meyer, Mark
111
+ and Desbrun, Mathieu
112
+ and Schr{\"o}der, Peter
113
+ and Barr, Alan H.",
114
+ editor="Hege, Hans-Christian
115
+ and Polthier, Konrad",
116
+ title="Discrete Differential-Geometry Operators for Triangulated 2-Manifolds",
117
+ booktitle="Visualization and Mathematics III",
118
+ year="2003",
119
+ publisher="Springer Berlin Heidelberg",
120
+ address="Berlin, Heidelberg",
121
+ pages="35--57",
122
+ isbn="978-3-662-05105-4"
123
+ }
124
+ ```
125
+
105
126
  ## Blog Post
106
127
  - [Thinking Parallel, Part I: Collision Detection on the GPU](https://developer.nvidia.com/blog/thinking-parallel-part-i-collision-detection-gpu/)
107
128
  - [Thinking Parallel, Part II: Tree Traversal on the GPU](https://developer.nvidia.com/blog/thinking-parallel-part-ii-tree-traversal-gpu/)
@@ -113,4 +134,5 @@ twine upload dist/* --verbose
113
134
  - [Kaolin](https://github.com/NVIDIAGameWorks/kaolin)
114
135
  - [Pytorch3D](https://github.com/facebookresearch/pytorch3d)
115
136
  - [Open3D](https://www.open3d.org/)
116
- - [trimesh](https://github.com/mikedh/trimesh)
137
+ - [trimesh](https://github.com/mikedh/trimesh)
138
+ - [TetWeave](https://github.com/AlexandreBinninger/TetWeave/tree/main)
@@ -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