conquer3d 0.3.1__tar.gz → 0.3.2__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 (94) hide show
  1. {conquer3d-0.3.1/conquer3d.egg-info → conquer3d-0.3.2}/PKG-INFO +3 -1
  2. {conquer3d-0.3.1 → conquer3d-0.3.2}/README.md +2 -0
  3. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/_C.so +0 -0
  4. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/__init__.py +2 -1
  5. conquer3d-0.3.2/conquer3d/conversion/__init__.py +13 -0
  6. {conquer3d-0.3.1/conquer3d/data_structure → conquer3d-0.3.2/conquer3d/conversion}/grid.py +49 -88
  7. conquer3d-0.3.2/conquer3d/conversion/mesh.py +120 -0
  8. conquer3d-0.3.2/conquer3d/data/collate/mesh.py +40 -0
  9. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/dataset/digit3d.py +4 -8
  10. conquer3d-0.3.2/conquer3d/data/transform/base.py +39 -0
  11. conquer3d-0.3.2/conquer3d/data/transform/ops.py +62 -0
  12. conquer3d-0.3.2/conquer3d/data/transform/vertex.py +55 -0
  13. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data_structure/__init__.py +0 -6
  14. conquer3d-0.3.2/conquer3d/data_structure/bmesh.py +23 -0
  15. conquer3d-0.3.2/conquer3d/data_structure/grid.py +65 -0
  16. {conquer3d-0.3.1 → conquer3d-0.3.2/conquer3d.egg-info}/PKG-INFO +3 -1
  17. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d.egg-info/SOURCES.txt +8 -0
  18. {conquer3d-0.3.1 → conquer3d-0.3.2}/pyproject.toml +1 -1
  19. {conquer3d-0.3.1 → conquer3d-0.3.2}/LICENSE +0 -0
  20. {conquer3d-0.3.1 → conquer3d-0.3.2}/MANIFEST.in +0 -0
  21. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/_C.pyi +0 -0
  22. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/creation/__init__.py +0 -0
  23. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/creation/triangle_creation.py +0 -0
  24. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
  25. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/bvh.cpp +0 -0
  26. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/grid.cpp +0 -0
  27. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
  28. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
  29. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +0 -0
  30. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
  31. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +0 -0
  32. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/ops/chamfer.cpp +0 -0
  33. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/ops/mc.cpp +0 -0
  34. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
  35. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
  36. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/check.h +0 -0
  37. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/constants.h +0 -0
  38. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/creation/triangle_creation.h +0 -0
  39. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/bvh.cu +0 -0
  40. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/bvh.h +0 -0
  41. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
  42. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
  43. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/kdtree.h +0 -0
  44. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/mesh_bvh.cu +0 -0
  45. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/mesh_bvh.h +0 -0
  46. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
  47. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/triangle_mesh.cu +0 -0
  48. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/triangle_mesh.h +0 -0
  49. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/zcurve.h +0 -0
  50. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/f2x2.h +0 -0
  51. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/f3x1.h +0 -0
  52. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/f3x3.h +0 -0
  53. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/f3x4.h +0 -0
  54. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/f4x1.h +0 -0
  55. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/f4x4.h +0 -0
  56. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/maths.h +0 -0
  57. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/ops.h +0 -0
  58. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/ops/chamfer.cu +0 -0
  59. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/ops/chamfer.h +0 -0
  60. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/ops/mc.cu +0 -0
  61. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/ops/mc.h +0 -0
  62. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/ops/mc_data.h +0 -0
  63. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/aabb.h +0 -0
  64. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/edge.h +0 -0
  65. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/gs.cu +0 -0
  66. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/gs.h +0 -0
  67. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
  68. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
  69. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/pgs.cu +0 -0
  70. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/pgs.h +0 -0
  71. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
  72. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
  73. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/ray.h +0 -0
  74. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/triangles.h +0 -0
  75. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/pybind.cpp +0 -0
  76. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/__init__.py +0 -0
  77. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/collate/__init__.py +0 -0
  78. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/collate/sparse_tensor.py +0 -0
  79. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/dataset/__init__.py +0 -0
  80. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/dataset/base_mesh.py +0 -0
  81. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/transform/__init__.py +0 -0
  82. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/ops/__init__.py +0 -0
  83. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/ops/diff_marching_cubes.py +0 -0
  84. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/ops/distance.py +0 -0
  85. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/ops/marching_cubes.py +0 -0
  86. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/primitive/__init__.py +0 -0
  87. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/primitive/gs.py +0 -0
  88. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/primitive/pgs.py +0 -0
  89. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d.egg-info/dependency_links.txt +0 -0
  90. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d.egg-info/not-zip-safe +0 -0
  91. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d.egg-info/requires.txt +0 -0
  92. {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d.egg-info/top_level.txt +0 -0
  93. {conquer3d-0.3.1 → conquer3d-0.3.2}/setup.cfg +0 -0
  94. {conquer3d-0.3.1 → conquer3d-0.3.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conquer3d
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: Geometric Cuda Tool Box
5
5
  Author-email: Do Hoang Khoi <khoido8899@gmail.com>
6
6
  License-Expression: MIT
@@ -63,6 +63,8 @@ pip install pybind11-stubgen
63
63
  pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
64
64
 
65
65
  pip install plotly open3d jupyter trimesh point-cloud-utils meshlib
66
+
67
+ pip install rectified-flow-pytorch
66
68
  ```
67
69
 
68
70
  # Development
@@ -34,6 +34,8 @@ pip install pybind11-stubgen
34
34
  pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
35
35
 
36
36
  pip install plotly open3d jupyter trimesh point-cloud-utils meshlib
37
+
38
+ pip install rectified-flow-pytorch
37
39
  ```
38
40
 
39
41
  # Development
@@ -5,5 +5,6 @@ from . import creation
5
5
  from . import data_structure
6
6
  from . import primitive
7
7
  from . import ops
8
+ from . import conversion
8
9
 
9
- __all__ = ['_C', 'creation', 'data_structure', 'primitive', 'ops']
10
+ __all__ = ['_C', 'creation', 'data_structure', 'primitive', 'ops', 'conversion']
@@ -0,0 +1,13 @@
1
+ from .grid import (
2
+ voxel2sparse,
3
+ sparse2voxel,
4
+ sparse_coo2dense_occ,
5
+ dense_occ2sparse_coo
6
+ )
7
+
8
+ __all__ = [
9
+ "voxel2sparse",
10
+ "sparse2voxel",
11
+ "sparse_coo2dense_occ",
12
+ "dense_occ2sparse_coo"
13
+ ]
@@ -1,68 +1,5 @@
1
1
  import torch
2
2
  from typing import Tuple, List, Union
3
- from .. import _C
4
-
5
- def create_voxel_grid(
6
- grid_min: Union[List[float], Tuple[float, float, float]],
7
- grid_max: Union[List[float], Tuple[float, float, float]],
8
- res: Union[List[int], Tuple[int, int, int]],
9
- device: str = "cuda"
10
- ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
11
- """
12
- Creates a structured 3D voxel grid efficiently.
13
-
14
- Args:
15
- grid_min (List[float] | Tuple[float, float, float]): The minimum (x, y, z) bounding box coordinates.
16
- grid_max (List[float] | Tuple[float, float, float]): The maximum (x, y, z) bounding box coordinates.
17
- res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
18
- device (str, optional): Target device for the tensors (e.g., "cuda" or "cpu"). Defaults to "cuda".
19
-
20
- Returns:
21
- Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
22
- - grid_vertices (torch.Tensor): A float32 tensor of shape (N, 3) containing all grid coordinates.
23
- - voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices
24
- for each voxel, mapped identically to the Marching Cubes convention.
25
- - idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) 3D coordinate indices for each vertex.
26
- """
27
- return _C.create_voxel_grid(list(grid_min), list(grid_max), list(res), device)
28
-
29
- def compute_grid_normal(
30
- sdf: torch.Tensor,
31
- grid_vertices: torch.Tensor,
32
- idx_grids: torch.Tensor,
33
- res: Union[List[int], Tuple[int, int, int]]
34
- ) -> torch.Tensor:
35
- """
36
- Computes smooth normals (gradients) for a voxel grid using central differences.
37
-
38
- Args:
39
- sdf (torch.Tensor): A float32 tensor of shape (N,) containing the SDF values.
40
- grid_vertices (torch.Tensor): A float32 tensor of shape (N, 3) containing the grid coordinates.
41
- idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) indices, returned by create_voxel_grid.
42
- res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
43
-
44
- Returns:
45
- torch.Tensor: A float32 tensor of shape (N, 3) containing the normalized gradient vectors.
46
- """
47
- return _C.compute_grid_normal(sdf.contiguous(), grid_vertices.contiguous(), idx_grids.contiguous(), list(res))
48
-
49
- def compute_active_voxels(
50
- voxels: torch.Tensor,
51
- sdf: torch.Tensor,
52
- iso: float = 0.0
53
- ) -> torch.Tensor:
54
- """
55
- Computes the indices of active voxels that intersect the isosurface.
56
-
57
- Args:
58
- voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices for each voxel.
59
- sdf (torch.Tensor): A float32 tensor of shape (N,) containing the SDF values.
60
- iso (float): The isosurface value. Defaults to 0.0.
61
-
62
- Returns:
63
- torch.Tensor: An int64 tensor containing the indices of the active voxels.
64
- """
65
- return _C.compute_active_voxels(voxels.contiguous(), sdf.contiguous(), iso)
66
3
 
67
4
  def voxel2sparse(
68
5
  active_indices: torch.Tensor,
@@ -104,28 +41,6 @@ def voxel2sparse(
104
41
  return sparse_coords
105
42
 
106
43
  def sparse2voxel(
107
- sparse_coords: torch.Tensor,
108
- res: Union[List[int], Tuple[int, int, int]]
109
- ) -> torch.Tensor:
110
- """
111
- Converts a [N, 4] sparse coordinates tensor back to 1D active voxel indices.
112
-
113
- Args:
114
- sparse_coords (torch.Tensor): An int32 tensor of shape (N, 4) in the format [batch_idx, x, y, z].
115
- res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
116
-
117
- Returns:
118
- torch.Tensor: An int64 tensor containing the indices of the active voxels.
119
- """
120
- x = sparse_coords[:, 1].to(torch.int64)
121
- y = sparse_coords[:, 2].to(torch.int64)
122
- z = sparse_coords[:, 3].to(torch.int64)
123
-
124
- rx, ry, rz = res
125
- active_indices = x * (ry - 1) * (rz - 1) + y * (rz - 1) + z
126
- return active_indices
127
-
128
- def sparse2mesh_topology(
129
44
  sparse_coords: torch.Tensor,
130
45
  sparse_sdfs: torch.Tensor,
131
46
  grid_min: Union[List[float], Tuple[float, float, float]],
@@ -182,9 +97,9 @@ def sparse2mesh_topology(
182
97
 
183
98
  # 6. Convert integer coordinates back to global float positions
184
99
  rx, ry, rz = res
185
- x_coords = torch.linspace(grid_min[0], grid_max[0], rx, device=device, dtype=torch.float32)
186
- y_coords = torch.linspace(grid_min[1], grid_max[1], ry, device=device, dtype=torch.float32)
187
- z_coords = torch.linspace(grid_min[2], grid_max[2], rz, device=device, dtype=torch.float32)
100
+ x_coords = torch.linspace(grid_min[0], grid_max[0], rx + 1, device=device, dtype=torch.float32)
101
+ y_coords = torch.linspace(grid_min[1], grid_max[1], ry + 1, device=device, dtype=torch.float32)
102
+ z_coords = torch.linspace(grid_min[2], grid_max[2], rz + 1, device=device, dtype=torch.float32)
188
103
 
189
104
  ux = x_coords[unique_corners_int[:, 0]]
190
105
  uy = y_coords[unique_corners_int[:, 1]]
@@ -193,3 +108,49 @@ def sparse2mesh_topology(
193
108
  unique_vertices = torch.stack([ux, uy, uz], dim=1)
194
109
 
195
110
  return unique_vertices, local_voxels, merged_sdfs
111
+
112
+ def sparse_coo2dense_occ(
113
+ sparse_coords: torch.Tensor,
114
+ batch_size: int,
115
+ res: Union[List[int], Tuple[int, int, int]]
116
+ ) -> torch.Tensor:
117
+ """
118
+ Converts a [N, 4] sparse coordinates tensor into a [B, 1, rx, ry, rz] dense occupancy grid.
119
+
120
+ Args:
121
+ sparse_coords (torch.Tensor): An int32 tensor of shape (N, 4) in the format [batch_idx, x, y, z].
122
+ batch_size (int): The batch size B.
123
+ res (List[int] | Tuple[int, int, int]): The grid resolution (rx, ry, rz).
124
+
125
+ Returns:
126
+ torch.Tensor: A float32 tensor of shape [B, 1, rx, ry, rz] containing 1.0 at active voxels and 0.0 otherwise.
127
+ """
128
+ rx, ry, rz = res
129
+ dense_occ = torch.zeros((batch_size, 1, rx, ry, rz), dtype=torch.float32, device=sparse_coords.device)
130
+
131
+ b = sparse_coords[:, 0].to(torch.int64)
132
+ x = sparse_coords[:, 1].to(torch.int64)
133
+ y = sparse_coords[:, 2].to(torch.int64)
134
+ z = sparse_coords[:, 3].to(torch.int64)
135
+
136
+ dense_occ[b, 0, x, y, z] = 1.0
137
+ return dense_occ
138
+
139
+ def dense_occ2sparse_coo(
140
+ dense_occ: torch.Tensor,
141
+ threshold: float = 0.0
142
+ ) -> torch.Tensor:
143
+ """
144
+ Converts a [B, C, rx, ry, rz] dense occupancy grid back to a [N, 4] sparse coordinates tensor.
145
+
146
+ Args:
147
+ dense_occ (torch.Tensor): A tensor of shape [B, C, rx, ry, rz].
148
+
149
+ Returns:
150
+ torch.Tensor: An int32 tensor of shape (N, 4) in the format [batch_idx, x, y, z].
151
+ """
152
+ active_mask = dense_occ > threshold
153
+ indices = torch.argwhere(active_mask)
154
+ # indices has shape [N, 5] for [b, c, x, y, z]. We want [batch_idx, x, y, z]
155
+ sparse_coords = indices[:, [0, 2, 3, 4]].to(torch.int32)
156
+ return sparse_coords
@@ -0,0 +1,120 @@
1
+ import torch
2
+ from conquer3d.data_structure.grid import create_voxel_grid, compute_active_voxels
3
+ from conquer3d.conversion.grid import voxel2sparse
4
+ from conquer3d.data_structure import TriangleMesh
5
+ from conquer3d.data_structure.bmesh import BTriangleMesh
6
+ from typing import Union, List, Tuple
7
+
8
+ def mesh2voxel(vertices: torch.Tensor, res: Union[List[int], Tuple[int, int, int]], grid_bound: float = None):
9
+ """
10
+ Computes bounding box of mesh and returns a structured voxel grid.
11
+ """
12
+ if grid_bound is None:
13
+ grid_min = vertices.min(dim=0)[0].tolist()
14
+ grid_max = vertices.max(dim=0)[0].tolist()
15
+ else:
16
+ grid_min = [-grid_bound, -grid_bound, -grid_bound]
17
+ grid_max = [grid_bound, grid_bound, grid_bound]
18
+
19
+ device_str = str(vertices.device)
20
+ if device_str.startswith('cuda'):
21
+ # Fix format if there's an index like cuda:0
22
+ device_str = "cuda"
23
+
24
+ grid_vertices, voxels, idx_grids = create_voxel_grid(grid_min, grid_max, res, device=device_str)
25
+
26
+ # Ensure they are on the exact same device as vertices (especially if multiple GPUs)
27
+ grid_vertices = grid_vertices.to(vertices.device)
28
+ voxels = voxels.to(vertices.device)
29
+ idx_grids = idx_grids.to(vertices.device)
30
+
31
+ return grid_vertices, voxels, idx_grids, grid_min, grid_max
32
+
33
+ def mesh2voxel_sdf(vertices: torch.Tensor, faces: torch.Tensor, grid_vertices: torch.Tensor):
34
+ """
35
+ Computes SDF for grid vertices against the mesh using C++ TriangleMesh fast winding numbers.
36
+ """
37
+ mesh = TriangleMesh(vertices, faces.to(torch.int32))
38
+
39
+ # query_points returns: q_ids, tri_ids, prj_pts, sdf
40
+ _, _, _, sdf = mesh.query_points(
41
+ grid_vertices,
42
+ return_sdf=True,
43
+ return_prj_pts=False,
44
+ sign_mode=0,
45
+ distance_mode=0
46
+ )
47
+ return sdf
48
+
49
+ def mesh2sparse(bmesh: BTriangleMesh, res: Union[List[int], Tuple[int, int, int]], grid_bound: float = 1.2, iso: float = 0.0):
50
+ """
51
+ Batched processing of BTriangleMesh into sparse tensors.
52
+ """
53
+ all_coords = []
54
+ all_sdfs = []
55
+
56
+ for b in range(bmesh.batch_size):
57
+ # Extract individual mesh
58
+ v_mask = bmesh.vertbids == b
59
+ f_mask = bmesh.facebids == b
60
+
61
+ v = bmesh.vertices[v_mask]
62
+ f = bmesh.faces[f_mask]
63
+
64
+ if len(v) == 0:
65
+ continue
66
+
67
+ # Voxelize and Compute SDF
68
+ grid_vertices, voxels, idx_grids, _, _ = mesh2voxel(v, res, grid_bound)
69
+ sdf = mesh2voxel_sdf(v, f, grid_vertices)
70
+
71
+ # Sparse conversion
72
+ active_voxels = compute_active_voxels(voxels, sdf, iso)
73
+ sparse_coords, sparse_sdfs = voxel2sparse(active_voxels, voxels, idx_grids, sdf=sdf, batch_idx=b)
74
+
75
+ all_coords.append(sparse_coords)
76
+ all_sdfs.append(sparse_sdfs)
77
+
78
+ if len(all_coords) > 0:
79
+ return torch.cat(all_coords, dim=0), torch.cat(all_sdfs, dim=0)
80
+ else:
81
+ # Fallback for empty batch
82
+ device = bmesh.vertices.device
83
+ return torch.empty((0, 4), dtype=torch.int32, device=device), torch.empty((0, 8), dtype=torch.float32, device=device)
84
+
85
+ def mesh2sparse_with_dense(bmesh: BTriangleMesh, res: Union[List[int], Tuple[int, int, int]], grid_bound: float = 1.2, iso: float = 0.0):
86
+ """
87
+ Batched processing of BTriangleMesh into sparse tensors, also returning full dense coords.
88
+ """
89
+ all_coords = []
90
+ all_sdfs = []
91
+ all_dense_coords = []
92
+ all_dense_sdfs = []
93
+
94
+ for b in range(bmesh.batch_size):
95
+ v_mask = bmesh.vertbids == b
96
+ f_mask = bmesh.facebids == b
97
+
98
+ v = bmesh.vertices[v_mask]
99
+ f = bmesh.faces[f_mask]
100
+
101
+ if len(v) == 0:
102
+ continue
103
+
104
+ grid_vertices, voxels, idx_grids, _, _ = mesh2voxel(v, res, grid_bound)
105
+ sdf = mesh2voxel_sdf(v, f, grid_vertices)
106
+
107
+ active_voxels = compute_active_voxels(voxels, sdf, iso)
108
+ sparse_coords, sparse_sdfs = voxel2sparse(active_voxels, voxels, idx_grids, sdf=sdf, batch_idx=b)
109
+ dense_coords, dense_sdfs = voxel2sparse(torch.ones_like(voxels[:, 0], dtype=torch.bool), voxels, idx_grids, sdf=sdf, batch_idx=b)
110
+
111
+ all_coords.append(sparse_coords)
112
+ all_sdfs.append(sparse_sdfs)
113
+ all_dense_coords.append(dense_coords)
114
+ all_dense_sdfs.append(dense_sdfs)
115
+
116
+ if len(all_coords) > 0:
117
+ return torch.cat(all_coords, dim=0), torch.cat(all_sdfs, dim=0), torch.cat(all_dense_coords, dim=0), torch.cat(all_dense_sdfs, dim=0)
118
+ else:
119
+ device = bmesh.vertices.device
120
+ return torch.empty((0, 4), dtype=torch.int32, device=device), torch.empty((0, 8), dtype=torch.float32, device=device), torch.empty((0, 4), dtype=torch.int32, device=device), torch.empty((0, 8), dtype=torch.float32, device=device)
@@ -0,0 +1,40 @@
1
+ import torch
2
+ from conquer3d.data_structure.bmesh import BTriangleMesh
3
+
4
+ def bmesh_collate_fn(batch):
5
+ """
6
+ Collate function for a dataset returning (vertices, faces, label).
7
+ Returns a BTriangleMesh and a batched label tensor.
8
+ """
9
+ all_vertices = []
10
+ all_faces = []
11
+ all_vertbids = []
12
+ all_facebids = []
13
+ all_labels = []
14
+
15
+ for b, (v, f, l) in enumerate(batch):
16
+ all_vertices.append(v)
17
+
18
+ # We do not offset the face indices because we will process them per-mesh locally
19
+ all_faces.append(f)
20
+
21
+ all_vertbids.append(torch.full((v.shape[0],), b, dtype=torch.int32))
22
+ all_facebids.append(torch.full((f.shape[0],), b, dtype=torch.int32))
23
+ all_labels.append(l)
24
+
25
+ batched_vertices = torch.cat(all_vertices, dim=0)
26
+ batched_faces = torch.cat(all_faces, dim=0)
27
+ batched_vertbids = torch.cat(all_vertbids, dim=0)
28
+ batched_facebids = torch.cat(all_facebids, dim=0)
29
+
30
+ batched_labels = torch.tensor(all_labels, dtype=torch.long)
31
+
32
+ bmesh = BTriangleMesh(
33
+ vertices=batched_vertices,
34
+ faces=batched_faces,
35
+ vertbids=batched_vertbids,
36
+ facebids=batched_facebids,
37
+ batch_size=len(batch)
38
+ )
39
+
40
+ return bmesh, batched_labels
@@ -1,9 +1,7 @@
1
1
  import os
2
2
  import zipfile
3
3
  import torch
4
- import numpy as np
5
4
  import conquer3d as c3d
6
- import trimesh
7
5
  import meshlib.mrmeshpy as mr
8
6
  import meshlib.mrmeshnumpy as mrnp
9
7
 
@@ -50,7 +48,7 @@ class Digit3D(BaseMeshDataset):
50
48
  if self.cached and idx in self._cache:
51
49
  vertices_t, faces_t, label = self._cache[idx]
52
50
  if self.transform:
53
- vertices_t = self.transform(vertices_t.clone())
51
+ vertices_t, faces_t = self.transform(vertices_t.clone(), faces_t.clone())
54
52
  return vertices_t, faces_t, label
55
53
 
56
54
  f_path = self.all_files[idx]
@@ -72,7 +70,6 @@ class Digit3D(BaseMeshDataset):
72
70
  vertices.append([float(parts[1]), float(parts[2]), float(parts[3])])
73
71
  elif line.startswith("f "):
74
72
  parts = line.split()
75
- # Wavefront OBJ faces are 1-indexed, so we subtract 1 for 0-indexed tensors
76
73
  faces.append([int(parts[1])-1, int(parts[2])-1, int(parts[3])-1])
77
74
 
78
75
  vertices_t = torch.tensor(vertices, dtype=torch.float32)
@@ -82,15 +79,14 @@ class Digit3D(BaseMeshDataset):
82
79
  self._cache[idx] = (vertices_t, faces_t, label)
83
80
 
84
81
  if self.transform:
85
- vertices_t = self.transform(vertices_t.clone())
82
+ vertices_t, faces_t = self.transform(vertices_t.clone(), faces_t.clone())
86
83
 
87
84
  return vertices_t, faces_t, label
88
85
 
89
86
 
90
87
  class SparseDigit3D(Digit3D):
91
88
  """
92
- Digit3D Dataset that constructs a sparse SDF voxel grid from the mesh on-the-fly using CPU (Open3D).
93
- This allows arbitrary geometric augmentations on the mesh before voxelization without CUDA IPC issues.
89
+ Digit3D Dataset that constructs a sparse SDF voxel grid from the mesh on-the-fly.
94
90
  """
95
91
  def __init__(self, root: str = "~/.conquer3d/", train: bool = True, transform=None, download: bool = False,
96
92
  grid_res: int = 32, grid_bound: float = 1.2, cached: bool = False):
@@ -126,7 +122,7 @@ class SparseDigit3D(Digit3D):
126
122
  active_voxel_indices = c3d.data_structure.compute_active_voxels(voxels, sdf, iso=0.0)
127
123
 
128
124
  # 5. Extract purely Voxel-Centric representations using voxel2sparse
129
- sparse_coords, sparse_sdfs = c3d.data_structure.voxel2sparse(
125
+ sparse_coords, sparse_sdfs = c3d.conversion.grid.voxel2sparse(
130
126
  active_voxel_indices, voxels, idx_grids, sdf=sdf, batch_idx=0
131
127
  )
132
128
 
@@ -0,0 +1,39 @@
1
+ import random
2
+
3
+ class BaseTransform(object):
4
+ def __init__(self, p=1.0):
5
+ self.p = p
6
+
7
+ def __call__(self, **data):
8
+ if random.random() <= self.p:
9
+ return self.apply(**data)
10
+ return data
11
+
12
+ def apply(self, **data):
13
+ raise NotImplementedError("Transform must implement apply")
14
+
15
+ class Sequence(BaseTransform):
16
+ def __init__(self, transforms, shuffle=False, p=1.0):
17
+ super().__init__(p=p)
18
+ self.transforms = transforms
19
+ self.shuffle = shuffle
20
+
21
+ def apply(self, **data):
22
+ transforms_to_apply = self.transforms.copy()
23
+ if self.shuffle:
24
+ random.shuffle(transforms_to_apply)
25
+
26
+ for transform in transforms_to_apply:
27
+ data = transform(**data)
28
+
29
+ return data
30
+
31
+ class MeshSequence(Sequence):
32
+ def __call__(self, vertices, faces):
33
+ if random.random() <= self.p:
34
+ return self.apply(vertices, faces)
35
+ return vertices, faces
36
+
37
+ def apply(self, vertices, faces):
38
+ data = super().apply(vertices=vertices, faces=faces)
39
+ return data.get('vertices', vertices), data.get('faces', faces)
@@ -0,0 +1,62 @@
1
+ import torch
2
+ import math
3
+
4
+ def rotation(vertices, rotation_axis, rotation_degree):
5
+ """
6
+ Rotates vertices around a given axis by a specific degree.
7
+
8
+ Args:
9
+ vertices (torch.Tensor): Shape (N, 3) representing 3D coordinates.
10
+ rotation_axis (str): 'x', 'y', or 'z'.
11
+ rotation_degree (float): Degree to rotate by (in degrees).
12
+
13
+ Returns:
14
+ torch.Tensor: The rotated vertices of the same type.
15
+ """
16
+ theta = math.radians(rotation_degree)
17
+ cos_t = math.cos(theta)
18
+ sin_t = math.sin(theta)
19
+
20
+ device = vertices.device
21
+ dtype = vertices.dtype
22
+
23
+ # Define rotation matrices based on the axis directly as PyTorch tensors
24
+ if rotation_axis == 'x':
25
+ rot_mat = torch.tensor([
26
+ [1.0, 0.0, 0.0],
27
+ [0.0, cos_t, -sin_t],
28
+ [0.0, sin_t, cos_t]
29
+ ], dtype=dtype, device=device)
30
+ elif rotation_axis == 'y':
31
+ rot_mat = torch.tensor([
32
+ [cos_t, 0.0, sin_t],
33
+ [0.0, 1.0, 0.0],
34
+ [-sin_t, 0.0, cos_t]
35
+ ], dtype=dtype, device=device)
36
+ elif rotation_axis == 'z':
37
+ rot_mat = torch.tensor([
38
+ [cos_t, -sin_t, 0.0],
39
+ [sin_t, cos_t, 0.0],
40
+ [0.0, 0.0, 1.0]
41
+ ], dtype=dtype, device=device)
42
+ else:
43
+ raise ValueError("rotation_axis must be 'x', 'y', or 'z'")
44
+
45
+ # Compute dot product
46
+ return torch.matmul(vertices, rot_mat.T)
47
+
48
+ def scale(vertices, scale_factor):
49
+ """
50
+ Scales vertices by a given factor.
51
+
52
+ Args:
53
+ vertices (torch.Tensor): Shape (N, 3) representing 3D coordinates.
54
+ scale_factor (float, tuple, or list): The factor(s) to scale by.
55
+
56
+ Returns:
57
+ torch.Tensor: The scaled vertices.
58
+ """
59
+ if isinstance(scale_factor, (list, tuple)):
60
+ scale_factor = torch.tensor(scale_factor, dtype=vertices.dtype, device=vertices.device)
61
+
62
+ return vertices * scale_factor
@@ -0,0 +1,55 @@
1
+ import torch
2
+ import random
3
+ from .base import BaseTransform
4
+ from .ops import rotation, scale
5
+
6
+ class Rotation(BaseTransform):
7
+ def __init__(self, axis='z', degree_range=(-180, 180), p=1.0):
8
+ """
9
+ Args:
10
+ axis (str or list): The axis or list of axes to rotate around ('x', 'y', 'z').
11
+ degree_range (tuple): Range of degrees to randomly sample from (min, max).
12
+ p (float): Probability of applying the transform.
13
+ """
14
+ super().__init__(p=p)
15
+ self.axis = axis
16
+ self.degree_range = degree_range
17
+
18
+ def apply(self, **data):
19
+ if 'vertices' not in data:
20
+ raise KeyError("Rotation transform requires 'vertices' in data")
21
+
22
+ vertices = data['vertices']
23
+
24
+ # Support multiple axes in sequence
25
+ axes = self.axis if isinstance(self.axis, (list, tuple)) else [self.axis]
26
+
27
+ for ax in axes:
28
+ # Sample a random degree for each axis
29
+ degree = random.uniform(self.degree_range[0], self.degree_range[1])
30
+ vertices = rotation(vertices, ax, degree)
31
+
32
+ data['vertices'] = vertices
33
+ return data
34
+
35
+ class Scale(BaseTransform):
36
+ def __init__(self, scale_range=(0.8, 1.2), p=1.0):
37
+ """
38
+ Args:
39
+ scale_range (tuple): Range of scale factors to randomly sample from (min, max).
40
+ p (float): Probability of applying the transform.
41
+ """
42
+ super().__init__(p=p)
43
+ self.scale_range = scale_range
44
+
45
+ def apply(self, **data):
46
+ if 'vertices' not in data:
47
+ raise KeyError("Scale transform requires 'vertices' in data")
48
+
49
+ vertices = data['vertices']
50
+
51
+ # Sample a uniform random scale factor
52
+ scale_factor = random.uniform(self.scale_range[0], self.scale_range[1])
53
+
54
+ data['vertices'] = scale(vertices, scale_factor)
55
+ return data
@@ -10,9 +10,6 @@ from .grid import (
10
10
  create_voxel_grid,
11
11
  compute_grid_normal,
12
12
  compute_active_voxels,
13
- voxel2sparse,
14
- sparse2voxel,
15
- sparse2mesh_topology
16
13
  )
17
14
 
18
15
  spatial_data_structures = ['KDTree', 'BVH', 'GSBVH', 'PGSBVH', 'MeshBVH']
@@ -21,9 +18,6 @@ grid_data_structures = [
21
18
  'create_voxel_grid',
22
19
  'compute_grid_normal',
23
20
  'compute_active_voxels',
24
- 'voxel2sparse',
25
- 'sparse2voxel',
26
- 'sparse2mesh_topology'
27
21
  ]
28
22
 
29
23
  __all__ = spatial_data_structures + mesh_data_structures + grid_data_structures
@@ -0,0 +1,23 @@
1
+ import torch
2
+
3
+ class BTriangleMesh:
4
+ def __init__(self, vertices: torch.Tensor, faces: torch.Tensor, vertbids: torch.Tensor, facebids: torch.Tensor, batch_size: int):
5
+ self.vertices = vertices
6
+ self.faces = faces
7
+ self.vertbids = vertbids
8
+ self.facebids = facebids
9
+ self.batch_size = batch_size
10
+
11
+ def to(self, device):
12
+ self.vertices = self.vertices.to(device)
13
+ self.faces = self.faces.to(device)
14
+ self.vertbids = self.vertbids.to(device)
15
+ self.facebids = self.facebids.to(device)
16
+ return self
17
+
18
+ def cuda(self, non_blocking=False):
19
+ self.vertices = self.vertices.cuda(non_blocking=non_blocking)
20
+ self.faces = self.faces.cuda(non_blocking=non_blocking)
21
+ self.vertbids = self.vertbids.cuda(non_blocking=non_blocking)
22
+ self.facebids = self.facebids.cuda(non_blocking=non_blocking)
23
+ return self
@@ -0,0 +1,65 @@
1
+ import torch
2
+ from typing import Tuple, List, Union
3
+ from .. import _C
4
+
5
+ def create_voxel_grid(
6
+ grid_min: Union[List[float], Tuple[float, float, float]],
7
+ grid_max: Union[List[float], Tuple[float, float, float]],
8
+ res: Union[List[int], Tuple[int, int, int]],
9
+ device: str = "cuda"
10
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
11
+ """
12
+ Creates a structured 3D voxel grid efficiently.
13
+
14
+ Args:
15
+ grid_min (List[float] | Tuple[float, float, float]): The minimum (x, y, z) bounding box coordinates.
16
+ grid_max (List[float] | Tuple[float, float, float]): The maximum (x, y, z) bounding box coordinates.
17
+ res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
18
+ device (str, optional): Target device for the tensors (e.g., "cuda" or "cpu"). Defaults to "cuda".
19
+
20
+ Returns:
21
+ Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
22
+ - grid_vertices (torch.Tensor): A float32 tensor of shape (N, 3) containing all grid coordinates.
23
+ - voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices
24
+ for each voxel, mapped identically to the Marching Cubes convention.
25
+ - idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) 3D coordinate indices for each vertex.
26
+ """
27
+ return _C.create_voxel_grid(list(grid_min), list(grid_max), list(res), device)
28
+
29
+ def compute_grid_normal(
30
+ sdf: torch.Tensor,
31
+ grid_vertices: torch.Tensor,
32
+ idx_grids: torch.Tensor,
33
+ res: Union[List[int], Tuple[int, int, int]]
34
+ ) -> torch.Tensor:
35
+ """
36
+ Computes smooth normals (gradients) for a voxel grid using central differences.
37
+
38
+ Args:
39
+ sdf (torch.Tensor): A float32 tensor of shape (N,) containing the SDF values.
40
+ grid_vertices (torch.Tensor): A float32 tensor of shape (N, 3) containing the grid coordinates.
41
+ idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) indices, returned by create_voxel_grid.
42
+ res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
43
+
44
+ Returns:
45
+ torch.Tensor: A float32 tensor of shape (N, 3) containing the normalized gradient vectors.
46
+ """
47
+ return _C.compute_grid_normal(sdf.contiguous(), grid_vertices.contiguous(), idx_grids.contiguous(), list(res))
48
+
49
+ def compute_active_voxels(
50
+ voxels: torch.Tensor,
51
+ sdf: torch.Tensor,
52
+ iso: float = 0.0
53
+ ) -> torch.Tensor:
54
+ """
55
+ Computes the indices of active voxels that intersect the isosurface.
56
+
57
+ Args:
58
+ voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices for each voxel.
59
+ sdf (torch.Tensor): A float32 tensor of shape (N,) containing the SDF values.
60
+ iso (float): The isosurface value. Defaults to 0.0.
61
+
62
+ Returns:
63
+ torch.Tensor: An int64 tensor containing the indices of the active voxels.
64
+ """
65
+ return _C.compute_active_voxels(voxels.contiguous(), sdf.contiguous(), iso)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conquer3d
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: Geometric Cuda Tool Box
5
5
  Author-email: Do Hoang Khoi <khoido8899@gmail.com>
6
6
  License-Expression: MIT
@@ -63,6 +63,8 @@ pip install pybind11-stubgen
63
63
  pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
64
64
 
65
65
  pip install plotly open3d jupyter trimesh point-cloud-utils meshlib
66
+
67
+ pip install rectified-flow-pytorch
66
68
  ```
67
69
 
68
70
  # Development
@@ -12,6 +12,9 @@ conquer3d.egg-info/dependency_links.txt
12
12
  conquer3d.egg-info/not-zip-safe
13
13
  conquer3d.egg-info/requires.txt
14
14
  conquer3d.egg-info/top_level.txt
15
+ conquer3d/conversion/__init__.py
16
+ conquer3d/conversion/grid.py
17
+ conquer3d/conversion/mesh.py
15
18
  conquer3d/creation/__init__.py
16
19
  conquer3d/creation/triangle_creation.py
17
20
  conquer3d/csrc/check.h
@@ -68,12 +71,17 @@ conquer3d/csrc/primitive/ray.h
68
71
  conquer3d/csrc/primitive/triangles.h
69
72
  conquer3d/data/__init__.py
70
73
  conquer3d/data/collate/__init__.py
74
+ conquer3d/data/collate/mesh.py
71
75
  conquer3d/data/collate/sparse_tensor.py
72
76
  conquer3d/data/dataset/__init__.py
73
77
  conquer3d/data/dataset/base_mesh.py
74
78
  conquer3d/data/dataset/digit3d.py
75
79
  conquer3d/data/transform/__init__.py
80
+ conquer3d/data/transform/base.py
81
+ conquer3d/data/transform/ops.py
82
+ conquer3d/data/transform/vertex.py
76
83
  conquer3d/data_structure/__init__.py
84
+ conquer3d/data_structure/bmesh.py
77
85
  conquer3d/data_structure/grid.py
78
86
  conquer3d/ops/__init__.py
79
87
  conquer3d/ops/diff_marching_cubes.py
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
9
9
 
10
10
  [project]
11
11
  name = "conquer3d"
12
- version = "0.3.1"
12
+ version = "0.3.2"
13
13
  description = "Geometric Cuda Tool Box"
14
14
  readme = "README.md"
15
15
  license = "MIT"
File without changes
File without changes
File without changes
File without changes
File without changes