DeepSDFStruct 1.7.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. DeepSDFStruct/SDF.py +1734 -0
  2. DeepSDFStruct/__init__.py +66 -0
  3. DeepSDFStruct/deep_sdf/__init__.py +69 -0
  4. DeepSDFStruct/deep_sdf/create_screenshots_from_plyfiles.py +65 -0
  5. DeepSDFStruct/deep_sdf/data.py +258 -0
  6. DeepSDFStruct/deep_sdf/metrics/__init__.py +14 -0
  7. DeepSDFStruct/deep_sdf/metrics/mesh_to_analytical.py +24 -0
  8. DeepSDFStruct/deep_sdf/models.py +197 -0
  9. DeepSDFStruct/deep_sdf/networks/__init__.py +8 -0
  10. DeepSDFStruct/deep_sdf/networks/analytic_round_cross.py +105 -0
  11. DeepSDFStruct/deep_sdf/networks/deep_sdf_decoder.py +155 -0
  12. DeepSDFStruct/deep_sdf/networks/hierarchical_deep_sdf_decoder.py +139 -0
  13. DeepSDFStruct/deep_sdf/networks/hierarchical_positional_sdf_decoder.py +190 -0
  14. DeepSDFStruct/deep_sdf/networks/resnet_positional_sdf_decoder.py +108 -0
  15. DeepSDFStruct/deep_sdf/nn_utils.py +70 -0
  16. DeepSDFStruct/deep_sdf/plotting.py +143 -0
  17. DeepSDFStruct/deep_sdf/reconstruction.py +165 -0
  18. DeepSDFStruct/deep_sdf/training.py +840 -0
  19. DeepSDFStruct/deep_sdf/workspace.py +363 -0
  20. DeepSDFStruct/design_of_experiments.py +235 -0
  21. DeepSDFStruct/flexicubes/__init__.py +15 -0
  22. DeepSDFStruct/flexicubes/flexicubes.py +1005 -0
  23. DeepSDFStruct/flexicubes/tables.py +2338 -0
  24. DeepSDFStruct/flexisquares/__init__.py +15 -0
  25. DeepSDFStruct/flexisquares/flexisquares.py +967 -0
  26. DeepSDFStruct/flexisquares/tables.py +63 -0
  27. DeepSDFStruct/lattice_structure.py +254 -0
  28. DeepSDFStruct/local_shapes.py +226 -0
  29. DeepSDFStruct/mesh.py +1184 -0
  30. DeepSDFStruct/optimization.py +357 -0
  31. DeepSDFStruct/parametrization.py +215 -0
  32. DeepSDFStruct/pretrained_models.py +128 -0
  33. DeepSDFStruct/sampling.py +670 -0
  34. DeepSDFStruct/sdf_operations.py +990 -0
  35. DeepSDFStruct/sdf_primitives.py +1416 -0
  36. DeepSDFStruct/splinepy_unitcells/__init__.py +35 -0
  37. DeepSDFStruct/splinepy_unitcells/chi_3D.py +1617 -0
  38. DeepSDFStruct/splinepy_unitcells/cross_lattice.py +165 -0
  39. DeepSDFStruct/splinepy_unitcells/double_lattice_extruded.py +298 -0
  40. DeepSDFStruct/splinepy_unitcells/snappy_3d.py +544 -0
  41. DeepSDFStruct/torch_spline.py +446 -0
  42. DeepSDFStruct/trained_models/analytic_round_cross/LatentCodes/latest.pth +0 -0
  43. DeepSDFStruct/trained_models/analytic_round_cross/ModelParameters/latest.pth +0 -0
  44. DeepSDFStruct/trained_models/analytic_round_cross/OptimizerParameters/latest.pth +0 -0
  45. DeepSDFStruct/trained_models/analytic_round_cross/specs.json +45 -0
  46. DeepSDFStruct/trained_models/chi_and_cross/LatentCodes/latest.pth +0 -0
  47. DeepSDFStruct/trained_models/chi_and_cross/Logs.pth +0 -0
  48. DeepSDFStruct/trained_models/chi_and_cross/ModelParameters/latest.pth +0 -0
  49. DeepSDFStruct/trained_models/chi_and_cross/OptimizerParameters/latest.pth +0 -0
  50. DeepSDFStruct/trained_models/chi_and_cross/specs.json +76 -0
  51. DeepSDFStruct/trained_models/primitives/LatentCodes/latest.pth +0 -0
  52. DeepSDFStruct/trained_models/primitives/ModelParameters/latest.pth +0 -0
  53. DeepSDFStruct/trained_models/primitives/OptimizerParameters/latest.pth +0 -0
  54. DeepSDFStruct/trained_models/primitives/specs.json +46 -0
  55. DeepSDFStruct/trained_models/primitives_2d/LatentCodes/latest.pth +0 -0
  56. DeepSDFStruct/trained_models/primitives_2d/ModelParameters/latest.pth +0 -0
  57. DeepSDFStruct/trained_models/primitives_2d/OptimizerParameters/latest.pth +0 -0
  58. DeepSDFStruct/trained_models/primitives_2d/specs.json +46 -0
  59. DeepSDFStruct/trained_models/round_cross/LatentCodes/latest.pth +0 -0
  60. DeepSDFStruct/trained_models/round_cross/Logs.pth +0 -0
  61. DeepSDFStruct/trained_models/round_cross/ModelParameters/latest.pth +0 -0
  62. DeepSDFStruct/trained_models/round_cross/OptimizerParameters/latest.pth +0 -0
  63. DeepSDFStruct/trained_models/round_cross/specs.json +45 -0
  64. DeepSDFStruct/trained_models/test_experiment/LatentCodes/latent_code_data_map.json +106 -0
  65. DeepSDFStruct/trained_models/test_experiment/specs.json +45 -0
  66. DeepSDFStruct/trained_models/test_experiment/training_summary.json +10 -0
  67. DeepSDFStruct/trained_models/test_experiment_hierarchical/LatentCodes/latent_code_data_map.json +106 -0
  68. DeepSDFStruct/trained_models/test_experiment_hierarchical/specs.json +45 -0
  69. DeepSDFStruct/trained_models/test_experiment_hierarchical/training_summary.json +10 -0
  70. DeepSDFStruct/trained_models/test_experiment_homogenization/LatentCodes/latent_code_data_map.json +5006 -0
  71. DeepSDFStruct/trained_models/test_experiment_homogenization/LatentCodes/latest.pth +0 -0
  72. DeepSDFStruct/trained_models/test_experiment_homogenization/Logs.png +0 -0
  73. DeepSDFStruct/trained_models/test_experiment_homogenization/ModelParameters/latest.pth +0 -0
  74. DeepSDFStruct/trained_models/test_experiment_homogenization/OptimizerParameters/latest.pth +0 -0
  75. DeepSDFStruct/trained_models/test_experiment_homogenization/specs.json +83 -0
  76. DeepSDFStruct/trained_models/test_experiment_homogenization/training_summary.json +10 -0
  77. DeepSDFStruct/utils.py +96 -0
  78. DeepSDFStruct/visualization/latent_viewer.py +211 -0
  79. DeepSDFStruct/visualization/latent_viewer_cli.py +8 -0
  80. benchmarks/generate_sdf_showcase.py +622 -0
  81. benchmarks/sdf_from_mesh_benchmark.py +52 -0
  82. benchmarks/sdf_from_mesh_comparison.md +33 -0
  83. benchmarks/sdf_showcase/operations/boolean/difference_sphere_box.png +0 -0
  84. benchmarks/sdf_showcase/operations/boolean/union_sphere_box.png +0 -0
  85. benchmarks/sdf_showcase/operations/transformations/circular_array_sphere.png +0 -0
  86. benchmarks/sdf_showcase/operations/transformations/dilate_sphere.png +0 -0
  87. benchmarks/sdf_showcase/operations/transformations/mirror_sphere.png +0 -0
  88. benchmarks/sdf_showcase/operations/transformations/repeat_sphere.png +0 -0
  89. benchmarks/sdf_showcase/operations/transformations/revolve_circle.png +0 -0
  90. benchmarks/sdf_showcase/operations/transformations/shell_sphere.png +0 -0
  91. benchmarks/sdf_showcase/operations/transformations/twist_torus.png +0 -0
  92. benchmarks/sdf_showcase/primitives/2D/circle.png +0 -0
  93. benchmarks/sdf_showcase/primitives/2D/equilateral_triangle.png +0 -0
  94. benchmarks/sdf_showcase/primitives/2D/hexagon.png +0 -0
  95. benchmarks/sdf_showcase/primitives/2D/polygon_pentagon.png +0 -0
  96. benchmarks/sdf_showcase/primitives/2D/rectangle.png +0 -0
  97. benchmarks/sdf_showcase/primitives/2D/rounded_rectangle.png +0 -0
  98. benchmarks/sdf_showcase/primitives/3D/box.png +0 -0
  99. benchmarks/sdf_showcase/primitives/3D/capped_cone.png +0 -0
  100. benchmarks/sdf_showcase/primitives/3D/capped_cylinder.png +0 -0
  101. benchmarks/sdf_showcase/primitives/3D/capsule.png +0 -0
  102. benchmarks/sdf_showcase/primitives/3D/cone.png +0 -0
  103. benchmarks/sdf_showcase/primitives/3D/corner_spheres.png +0 -0
  104. benchmarks/sdf_showcase/primitives/3D/cross_ms.png +0 -0
  105. benchmarks/sdf_showcase/primitives/3D/cylinder.png +0 -0
  106. benchmarks/sdf_showcase/primitives/3D/dodecahedron.png +0 -0
  107. benchmarks/sdf_showcase/primitives/3D/ellipsoid.png +0 -0
  108. benchmarks/sdf_showcase/primitives/3D/icosahedron.png +0 -0
  109. benchmarks/sdf_showcase/primitives/3D/octahedron.png +0 -0
  110. benchmarks/sdf_showcase/primitives/3D/pyramid.png +0 -0
  111. benchmarks/sdf_showcase/primitives/3D/rounded_box.png +0 -0
  112. benchmarks/sdf_showcase/primitives/3D/rounded_cone.png +0 -0
  113. benchmarks/sdf_showcase/primitives/3D/rounded_cylinder.png +0 -0
  114. benchmarks/sdf_showcase/primitives/3D/sphere.png +0 -0
  115. benchmarks/sdf_showcase/primitives/3D/tetrahedron.png +0 -0
  116. benchmarks/sdf_showcase/primitives/3D/torus.png +0 -0
  117. benchmarks/sdf_showcase/primitives/3D/wireframe_box.png +0 -0
  118. deepsdfstruct-1.7.2.dist-info/METADATA +44 -0
  119. deepsdfstruct-1.7.2.dist-info/RECORD +165 -0
  120. deepsdfstruct-1.7.2.dist-info/WHEEL +5 -0
  121. deepsdfstruct-1.7.2.dist-info/licenses/LICENSE +208 -0
  122. deepsdfstruct-1.7.2.dist-info/licenses/NOTICE +67 -0
  123. deepsdfstruct-1.7.2.dist-info/scm_file_list.json +178 -0
  124. deepsdfstruct-1.7.2.dist-info/scm_version.json +8 -0
  125. deepsdfstruct-1.7.2.dist-info/top_level.txt +4 -0
  126. docs/Makefile +20 -0
  127. docs/api_reference.rst +6 -0
  128. docs/conf.py +60 -0
  129. docs/index.rst +136 -0
  130. docs/make.bat +35 -0
  131. docs/qr_code_github.png +0 -0
  132. docs/qr_code_paper.png +0 -0
  133. docs/readme_images/example_output_01.png +0 -0
  134. docs/readme_images/example_output_02.png +0 -0
  135. docs/readme_images/example_output_03.png +0 -0
  136. docs/readme_images/example_output_04.png +0 -0
  137. tests/data/chairs/1005.obj +282 -0
  138. tests/data/chairs/1024.obj +222 -0
  139. tests/data/chairs/README.md +1 -0
  140. tests/data/circular_balls_test_case.stl +0 -0
  141. tests/data/cone.stl +1794 -0
  142. tests/data/example_disconnectd_mesh.inp +1784 -0
  143. tests/data/example_line_mesh.vtk +0 -0
  144. tests/data/stanford_bunny.stl +0 -0
  145. tests/data/sweep_test_case.stl +0 -0
  146. tests/test_DOE.py +49 -0
  147. tests/test_elongate_sdf.py +195 -0
  148. tests/test_flexisquares.py +187 -0
  149. tests/test_generate_dataset.py +76 -0
  150. tests/test_lattice_evaluation.py +137 -0
  151. tests/test_mesh_export.py +137 -0
  152. tests/test_mesh_functions.py +49 -0
  153. tests/test_networks.py +71 -0
  154. tests/test_pretrained_models.py +40 -0
  155. tests/test_reconstruction.py +101 -0
  156. tests/test_sdf_from_mesh.py +52 -0
  157. tests/test_sdf_functions.py +117 -0
  158. tests/test_sdf_primitives.py +668 -0
  159. tests/test_sdf_trimesh_comparison.py +482 -0
  160. tests/test_splinepy_unitcells.py +64 -0
  161. tests/test_structural_optimization.py +178 -0
  162. tests/test_torch_spline.py +216 -0
  163. tests/test_train_model.py +110 -0
  164. tests/test_training_data_ids.py +41 -0
  165. tests/tmp_outputs/temp_file.txt +0 -0
@@ -0,0 +1,446 @@
1
+ """
2
+ PyTorch-Compatible B-Spline Operations
3
+ =======================================
4
+
5
+ This module provides differentiable B-spline evaluation using PyTorch,
6
+ enabling spline-based deformations and parametrizations in gradient-based
7
+ optimization workflows.
8
+
9
+ Key Features
10
+ ------------
11
+
12
+ TorchSpline Class
13
+ Main interface for PyTorch-compatible spline evaluation. Wraps splinepy
14
+ splines and provides forward evaluation with automatic differentiation
15
+ support.
16
+
17
+ Low-level Functions
18
+ - torch_spline_1D: Evaluate 1D B-splines using de Boor's algorithm
19
+ - torch_spline_2D: Evaluate 2D tensor product B-splines
20
+ - torch_spline_3D: Evaluate 3D tensor product B-splines
21
+ - bspline_basis: Compute B-spline basis functions
22
+
23
+ The implementation uses vectorized de Boor's algorithm for efficient
24
+ batch evaluation on GPU. All operations are differentiable with respect
25
+ to control points and query coordinates.
26
+
27
+ Examples
28
+ --------
29
+ Create and evaluate a TorchSpline::
30
+
31
+ import splinepy
32
+ import torch
33
+ from DeepSDFStruct.torch_spline import TorchSpline
34
+
35
+ # Create a splinepy B-spline
36
+ spline = splinepy.BSpline(
37
+ degrees=[2, 2, 2],
38
+ control_points=torch.rand(27, 3),
39
+ knot_vectors=[
40
+ [0, 0, 0, 1, 1, 1],
41
+ [0, 0, 0, 1, 1, 1],
42
+ [0, 0, 0, 1, 1, 1]
43
+ ]
44
+ )
45
+
46
+ # Wrap in TorchSpline
47
+ torch_spline = TorchSpline(spline, device='cuda')
48
+
49
+ # Evaluate at query points
50
+ queries = torch.rand(1000, 3, device='cuda')
51
+ values = torch_spline(queries)
52
+
53
+ # Compute gradients
54
+ values.sum().backward()
55
+ print(torch_spline.control_points.grad)
56
+
57
+ Notes
58
+ -----
59
+ The module supports:
60
+ - B-splines (non-rational)
61
+ - NURBS (rational B-splines)
62
+ - Bezier splines (special case of B-splines)
63
+ - 1D, 2D, and 3D parametric dimensions
64
+ - Arbitrary output dimensions
65
+ """
66
+
67
+ import torch
68
+ import splinepy as sp
69
+ import numpy as np
70
+
71
+
72
+ def torch_spline_1D(knot_vectors, control_points, degrees, queries: torch.tensor):
73
+ knot_vector = knot_vectors[0]
74
+ degree = degrees[0]
75
+ queries_1D = queries[:, 0]
76
+ assert (len(knot_vector) - degree - 1) == len(control_points)
77
+
78
+ x = queries_1D
79
+
80
+ n_basis = len(knot_vector) - degree - 1
81
+ t_clamped = torch.clamp(x, knot_vector[degree], knot_vector[-degree - 1])
82
+ k = torch.searchsorted(knot_vector, t_clamped, right=True) - 1
83
+ k = torch.clamp(k, min=degree, max=n_basis - 1).view(-1)
84
+
85
+ # For each t, pick degree+1 control points for that span
86
+ i = k.view(-1, 1) + torch.arange(-degree, 1, device=queries_1D.device).view(
87
+ 1, -1
88
+ ) # (n_queries, degree+1)
89
+ cp_local = control_points[i] # (n_queries, degree+1)
90
+
91
+ # b[j] = N_{j,p}(t) for local control points
92
+ d = cp_local.clone()
93
+
94
+ # vectorized deboor from https://en.wikipedia.org/wiki/De_Boor%27s_algorithm
95
+ for r in range(1, degree + 1):
96
+ j_idx = torch.arange(degree, r - 1, -1, device=knot_vector.device)
97
+
98
+ idx_j_plus_k = j_idx.unsqueeze(0) + k.unsqueeze(1)
99
+ idx_j_plus_k_minus_p = idx_j_plus_k - degree
100
+ idx_j_plus_k_plus_1_minus_r = idx_j_plus_k + 1 - r
101
+
102
+ jkmp = knot_vector[idx_j_plus_k_minus_p]
103
+ j1kmr = knot_vector[idx_j_plus_k_plus_1_minus_r]
104
+
105
+ alpha = ((x.unsqueeze(-1) - jkmp) / (j1kmr - jkmp)).unsqueeze(-1)
106
+ d[:, j_idx, :] = (1.0 - alpha) * d[:, j_idx - 1, :] + alpha * d[:, j_idx, :]
107
+
108
+ y = d[:, degree]
109
+ return y
110
+
111
+
112
+ def bspline_basis(t, p, queries: torch.tensor):
113
+ x = queries
114
+
115
+ n_basis = len(t) - p - 1
116
+ t_clamped = torch.clamp(x, t[p], t[-p - 1])
117
+ k = torch.searchsorted(t, t_clamped, right=True) - 1
118
+ k = torch.clamp(k, min=p, max=n_basis - 1).view(-1)
119
+
120
+ # For each t, pick degree+1 control points for that span
121
+ i = k.view(-1, 1) + torch.arange(-p, 1, device=queries.device).view(
122
+ 1, -1
123
+ ) # (n_queries, degree+1)
124
+
125
+ # b[j] = N_{j,p}(t) for local control points
126
+ d = (
127
+ torch.eye(p + 1, device=queries.device, dtype=queries.dtype)
128
+ .unsqueeze(0)
129
+ .repeat(queries.shape[0], 1, 1)
130
+ )
131
+
132
+ # vectorized deboor from https://en.wikipedia.org/wiki/De_Boor%27s_algorithm
133
+ for r in range(1, p + 1):
134
+ j_idx = torch.arange(p, r - 1, -1, device=t.device)
135
+
136
+ idx_j_plus_k = j_idx.unsqueeze(0) + k.unsqueeze(1)
137
+ idx_j_plus_k_minus_p = idx_j_plus_k - p
138
+ idx_j_plus_k_plus_1_minus_r = idx_j_plus_k + 1 - r
139
+
140
+ jkmp = t[idx_j_plus_k_minus_p]
141
+ j1kmr = t[idx_j_plus_k_plus_1_minus_r]
142
+
143
+ alpha = ((x.unsqueeze(-1) - jkmp) / (j1kmr - jkmp)).unsqueeze(-1)
144
+ d[:, j_idx, :] = (1.0 - alpha) * d[:, j_idx - 1, :] + alpha * d[:, j_idx, :]
145
+
146
+ # Result
147
+ y = d[:, p, :]
148
+ return i, y
149
+
150
+
151
+ def torch_spline_2D(knot_vectors, control_points, degrees, queries):
152
+ """
153
+ Evaluate a 2D B-spline volume at query points.
154
+
155
+ Args:
156
+ tx, ty knot vectors for x, y
157
+ px, py degrees
158
+ cp: control points, shape (nx, ny, 2)
159
+ qx, qy,query coordinates, shape (N,)
160
+
161
+ Returns:
162
+ y: evaluated spline, shape (N, 2)
163
+ """
164
+ tx, ty = knot_vectors
165
+ px, py = degrees
166
+ qx = queries[:, 0]
167
+ qy = queries[:, 1]
168
+
169
+ # Basis functions along each axis
170
+ ix, bx = bspline_basis(tx, px, qx) # (N, px+1), (N, px+1)
171
+ iy, by = bspline_basis(ty, py, qy)
172
+
173
+ torch.testing.assert_close(bx.sum(dim=1), torch.ones_like(qx))
174
+ torch.testing.assert_close(by.sum(dim=1), torch.ones_like(qy))
175
+
176
+ nx = len(tx) - px - 1
177
+ ny = len(ty) - py - 1
178
+
179
+ assert nx * ny == control_points.shape[0]
180
+
181
+ bx_ = bx[:, :, None] # (N, px+1, 1)
182
+ by_ = by[:, None, :] # (N, 1, py+1)
183
+
184
+ # Compute outer product of weights: (N, px+1, py+1, pz+1)
185
+ weights = bx_ * by_
186
+
187
+ ix_ = ix[:, :, None] # (N, px+1, 1)
188
+ iy_ = iy[:, None, :] # (N, 1, py+1)
189
+
190
+ flat_idx = ix_ + nx * iy_
191
+ cp_selected = control_points[flat_idx]
192
+ y = (weights[:, :, :, None] * cp_selected).sum(dim=(1, 2)) # (N,)
193
+
194
+ return y
195
+
196
+
197
+ def torch_spline_3D(knot_vectors, control_points, degrees, queries):
198
+ """
199
+ Evaluate a 3D B-spline volume at query points.
200
+
201
+ Args:
202
+ tx, ty, tz: knot vectors for x, y, z
203
+ px, py, pz: degrees
204
+ cp: control points, shape (nx, ny, nz, d)
205
+ qx, qy, qz: query coordinates, shape (N,)
206
+
207
+ Returns:
208
+ y: evaluated spline, shape (N, d)
209
+ """
210
+ tx, ty, tz = knot_vectors
211
+ px, py, pz = degrees
212
+ qx = queries[:, 0]
213
+ qy = queries[:, 1]
214
+ qz = queries[:, 2]
215
+ # Basis functions along each axis
216
+ ix, bx = bspline_basis(tx, px, qx) # (N, px+1), (N, px+1)
217
+ iy, by = bspline_basis(ty, py, qy)
218
+ iz, bz = bspline_basis(tz, pz, qz)
219
+ torch.testing.assert_close(bx.sum(dim=1), torch.ones_like(qx))
220
+ torch.testing.assert_close(by.sum(dim=1), torch.ones_like(qy))
221
+ torch.testing.assert_close(bz.sum(dim=1), torch.ones_like(qz))
222
+ nx = len(tx) - px - 1
223
+ ny = len(ty) - py - 1
224
+ nz = len(tz) - pz - 1
225
+
226
+ assert nx * ny * nz == control_points.shape[0]
227
+
228
+ bx_ = bx[:, :, None, None] # (N, px+1, 1, 1)
229
+ by_ = by[:, None, :, None] # (N, 1, py+1, 1)
230
+ bz_ = bz[:, None, None, :] # (N, 1, 1, pz+1)
231
+
232
+ # Compute outer product of weights: (N, px+1, py+1, pz+1)
233
+ weights = bx_ * by_ * bz_
234
+
235
+ ix_ = ix[:, :, None, None] # (N, px+1, 1, 1)
236
+ iy_ = iy[:, None, :, None] # (N, 1, py+1, 1)
237
+ iz_ = iz[:, None, None, :] # (N, 1, 1, pz+1)
238
+
239
+ flat_idx = ix_ + nx * (iy_ + ny * iz_)
240
+ cp_selected = control_points[flat_idx]
241
+ y = (weights[:, :, :, :, None] * cp_selected).sum(dim=(1, 2, 3)) # (N,)
242
+
243
+ return y
244
+
245
+
246
+ class TorchScaling(torch.nn.Module):
247
+ """
248
+ A simple scaling function to transform the mesh from parametric to physical space.
249
+ Used as a baseline deformation function when no complex deformation is needed.
250
+ """
251
+
252
+ def __init__(
253
+ self, scale_factors, translation, bounds, device="cpu", dtype=torch.float32
254
+ ):
255
+ super().__init__()
256
+ self.scale_factors = torch.nn.Parameter(
257
+ torch.tensor(scale_factors, dtype=dtype, device=device)
258
+ )
259
+ self.translation = torch.nn.Parameter(
260
+ torch.tensor(translation, dtype=dtype, device=device)
261
+ )
262
+ self.register_buffer(
263
+ "parametric_bounds", torch.tensor(bounds, dtype=dtype, device=device)
264
+ )
265
+
266
+ def forward(self, queries: torch.Tensor):
267
+ """param -> phys"""
268
+ return queries * self.scale_factors + self.translation
269
+
270
+ def inverse_target_points(self, query_points_phys_space: torch.Tensor):
271
+ """phys -> param"""
272
+ return (query_points_phys_space - self.translation) / self.scale_factors
273
+
274
+
275
+ class TorchSpline(torch.nn.Module):
276
+ """
277
+ V2: uses custom torch spline as spline backend
278
+ """
279
+
280
+ spline: sp.BSpline
281
+
282
+ def __init__(self, spline: sp.BSpline, device="cpu", dtype=torch.float32):
283
+ # TODO register control points etc. as parameter
284
+ super().__init__()
285
+ # self.device = device
286
+ # self.dtype = dtype # dtype and device could be getter and setter
287
+ self.spline = spline
288
+ self.control_points = torch.nn.Parameter(
289
+ torch.tensor(spline.control_points, dtype=dtype, device=device)
290
+ )
291
+
292
+ for i, knot in enumerate(spline.knot_vectors):
293
+ kv = torch.tensor(knot, dtype=dtype, device=device)
294
+ self.register_buffer(f"knot_vector_{i}", kv)
295
+
296
+ self.register_buffer(
297
+ "degrees",
298
+ torch.tensor(self.spline.degrees, dtype=torch.int64, device=device),
299
+ )
300
+ self.register_buffer(
301
+ "parametric_bounds",
302
+ torch.tensor(self.spline.parametric_bounds, dtype=dtype, device=device),
303
+ )
304
+
305
+ match len(spline.degrees):
306
+ case 1:
307
+ self.spline_fun = torch_spline_1D
308
+ case 2:
309
+ self.spline_fun = torch_spline_2D
310
+ case 3:
311
+ self.spline_fun = torch_spline_3D
312
+
313
+ @property
314
+ def knot_vectors(self):
315
+ i = 0
316
+ out = []
317
+ while hasattr(self, f"knot_vector_{i}"):
318
+ out.append(getattr(self, f"knot_vector_{i}"))
319
+ i += 1
320
+ return out
321
+
322
+ def forward(self, queries: torch.Tensor):
323
+ # spline fun takes the following arguments:
324
+ # knot_vectors, control_points, degrees, queries: Any
325
+ return self.spline_fun(
326
+ self.knot_vectors, self.control_points, self.degrees, queries
327
+ )
328
+
329
+ def inverse_target_points(
330
+ self, query_points_phys_space: torch.Tensor, return_verbose=False
331
+ ):
332
+ device = query_points_phys_space.device
333
+ dtype = query_points_phys_space.dtype
334
+ queries_param_space = self.spline.proximities(
335
+ query_points_phys_space.detach().cpu().numpy(),
336
+ return_verbose=return_verbose,
337
+ )
338
+ return torch.tensor(queries_param_space, device=device, dtype=dtype)
339
+
340
+
341
+ def generate_bbox_spline(bounds):
342
+ """
343
+ Takes bounding box and generates a spline box.
344
+ Works for 2D [[xmin, ymin], [xmax, ymax]]
345
+ and 3D [[xmin, ymin, zmin], [xmax, ymax, zmax]].
346
+
347
+ Parameters
348
+ ----------
349
+ bounds : (2, d) array-like
350
+ [[min1, min2, ...],
351
+ [max1, max2, ...]]
352
+
353
+ Returns
354
+ -------
355
+ spline : splinepy.BSpline
356
+ BSpline representing the bounding box.
357
+ """
358
+ bounds = np.asarray(bounds)
359
+ mins, maxs = bounds[0], bounds[1]
360
+
361
+ dim = bounds.shape[1]
362
+ degrees = [1] * dim
363
+ knots = [[0, 0, 1, 1] for _ in range(dim)]
364
+ n = 2
365
+
366
+ # Create 1D grids per dimension
367
+ axes = [np.linspace(mins[i], maxs[i], n) for i in range(dim)]
368
+
369
+ mg = np.meshgrid(*axes, indexing="xy")
370
+ # Generate full grid
371
+ control_points = np.stack(mg, axis=-1).reshape(-1, dim)
372
+
373
+ spline = sp.BSpline(degrees, knots, control_points)
374
+ return spline
375
+
376
+
377
+ class CubicBezier(torch.nn.Module):
378
+ def __init__(self, control_points, device="cpu", dtype=torch.float32):
379
+ super().__init__()
380
+ self.device = device
381
+ self.dtype = dtype
382
+ cp = torch.as_tensor(control_points, dtype=dtype, device=device)
383
+ assert cp.shape == (
384
+ 4,
385
+ 3,
386
+ ), f"CubicBezier requires 4 control points (3D), got {cp.shape}"
387
+ self.control_points = torch.nn.Parameter(cp)
388
+
389
+ def forward(self, t):
390
+ """
391
+ Evaluate cubic bezier at parameter t in [0,1].
392
+
393
+ B(t) = (1-t)^3 P0 + 3(1-t)^2 t P1 + 3(1-t) t^2 P2 + t^3 P3
394
+ """
395
+ t = torch.as_tensor(t, dtype=self.dtype, device=self.device)
396
+ if t.dim() == 0:
397
+ t = t.unsqueeze(0)
398
+
399
+ p0, p1, p2, p3 = self.control_points
400
+
401
+ u = 1 - t
402
+ points = (
403
+ (u**3).unsqueeze(-1) * p0
404
+ + (3 * u**2 * t).unsqueeze(-1) * p1
405
+ + (3 * u * t**2).unsqueeze(-1) * p2
406
+ + (t**3).unsqueeze(-1) * p3
407
+ )
408
+ return points
409
+
410
+ def eval_tangent(self, t):
411
+ """
412
+ Evaluate tangent vector at parameter t.
413
+
414
+ B'(t) = 3(1-t)^2 (P1-P0) + 6(1-t)t (P2-P1) + 3t^2 (P3-P2)
415
+ """
416
+ t = torch.as_tensor(t, dtype=self.dtype, device=self.device)
417
+ if t.dim() == 0:
418
+ t = t.unsqueeze(0)
419
+
420
+ p0, p1, p2, p3 = self.control_points
421
+
422
+ u = 1 - t
423
+ tangent = (
424
+ (3 * u**2).unsqueeze(-1) * (p1 - p0)
425
+ + (6 * u * t).unsqueeze(-1) * (p2 - p1)
426
+ + (3 * t**2).unsqueeze(-1) * (p3 - p2)
427
+ )
428
+ return tangent
429
+
430
+ def eval_curvature(self, t):
431
+ """
432
+ Evaluate second derivative at parameter t.
433
+
434
+ B''(t) = 6(1-t)(P2-2P1+P0) + 6t(P3-2P2+P1)
435
+ """
436
+ t = torch.as_tensor(t, dtype=self.dtype, device=self.device)
437
+ if t.dim() == 0:
438
+ t = t.unsqueeze(0)
439
+
440
+ p0, p1, p2, p3 = self.control_points
441
+
442
+ u = 1 - t
443
+ curvature = (6 * u).unsqueeze(-1) * (p2 - 2 * p1 + p0) + (6 * t).unsqueeze(
444
+ -1
445
+ ) * (p3 - 2 * p2 + p1)
446
+ return curvature
@@ -0,0 +1,45 @@
1
+ {
2
+ "Description" : "This experiment is a wrapper for the round cross analytical function.",
3
+ "DataSource" : "data",
4
+ "NetworkArch" : "analytic_round_cross",
5
+ "TrainSplit": "data/splits/round_cross_only.json",
6
+ "TestSplit": "data/splits/round_cross_only.json",
7
+ "ReconstructionSplit": "",
8
+ "NetworkSpecs" : {
9
+ "dims" : [128, 128, 128, 128, 128, 128],
10
+ "dropout" : [0, 1, 2, 3, 4, 5, 6, 7],
11
+ "dropout_prob" : 0.2,
12
+ "norm_layers" : [0, 1, 2, 3, 4, 5, 6, 7],
13
+ "latent_in" : [2],
14
+ "xyz_in_all" : false,
15
+ "use_tanh" : false,
16
+ "latent_dropout" : false,
17
+ "weight_norm" : true,
18
+ "geom_dimension": 3
19
+ },
20
+ "CodeLength" : 1,
21
+ "NumEpochs" : 1000,
22
+ "SnapshotFrequency" : 100,
23
+ "AdditionalSnapshots" : [1,5],
24
+ "LearningRateSchedule" : [
25
+ {
26
+ "Type" : "Step",
27
+ "Initial" : 0.0005,
28
+ "Interval" : 500,
29
+ "Factor" : 0.5
30
+ },
31
+ {
32
+ "Type" : "Step",
33
+ "Initial" : 0.001,
34
+ "Interval" : 500,
35
+ "Factor" : 0.5
36
+ }],
37
+ "SamplesPerScene" : 16000,
38
+ "ScenesPerBatch" : 10,
39
+ "DataLoaderThreads" : 1,
40
+ "ClampingDistance" : 0.1,
41
+ "CodeRegularization" : true,
42
+ "CodeRegularizationLambda" : 1e-4,
43
+ "CodeBound" : 1.0
44
+ }
45
+
@@ -0,0 +1,76 @@
1
+ {
2
+ "Description": "This experiment learns a shape representation of Michael Giritschs chi3D in combination with a diagonal cross. It uses the microstructure v5 dataset, with larger cross strut thickness.",
3
+ "DataSource": "/storage/mkofler/GMOD/data",
4
+ "NetworkArch": "deep_sdf_decoder",
5
+ "TrainSplit": "/storage/mkofler/GMOD/data/splits/chi_and_cross_hom_v6.json",
6
+ "TestSplit": "/storage/mkofler/GMOD/data/splits/chi_and_cross_hom_v6.json",
7
+ "ReconstructionSplit": "",
8
+ "NetworkSpecs": {
9
+ "dims": [
10
+ 128,
11
+ 128,
12
+ 128,
13
+ 128,
14
+ 128,
15
+ 128
16
+ ],
17
+ "dropout": [
18
+ 0,
19
+ 1,
20
+ 2,
21
+ 3,
22
+ 4,
23
+ 5,
24
+ 6,
25
+ 7
26
+ ],
27
+ "dropout_prob": 0.2,
28
+ "norm_layers": [
29
+ 0,
30
+ 1,
31
+ 2,
32
+ 3,
33
+ 4,
34
+ 5,
35
+ 6,
36
+ 7
37
+ ],
38
+ "latent_in": [
39
+ 2
40
+ ],
41
+ "xyz_in_all": false,
42
+ "use_tanh": false,
43
+ "latent_dropout": false,
44
+ "weight_norm": true,
45
+ "geom_dimension": 3
46
+ },
47
+ "CodeLength": 2,
48
+ "NumEpochs": 1000,
49
+ "SnapshotFrequency": 100,
50
+ "AdditionalSnapshots": [
51
+ 1,
52
+ 5
53
+ ],
54
+ "LearningRateSchedule": [
55
+ {
56
+ "Type": "Step",
57
+ "Initial": 0.0005,
58
+ "Interval": 500,
59
+ "Factor": 0.5
60
+ },
61
+ {
62
+ "Type": "Step",
63
+ "Initial": 0.001,
64
+ "Interval": 500,
65
+ "Factor": 0.5
66
+ }
67
+ ],
68
+ "SamplesPerScene": 16384,
69
+ "ScenesPerBatch": 10,
70
+ "DataLoaderThreads": 1,
71
+ "ClampingDistance": 0.1,
72
+ "CodeRegularization": true,
73
+ "CodeRegularizationLambda": 1e-4,
74
+ "CodeBound": 1.0,
75
+ "seed": 1
76
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "Description" : "Debug DeepLS trained and tested on compelx geom benchmark",
3
+ "DataSource" : "/storage/lfrei/SemperitSDF/data",
4
+ "TrainSplit" : "data/splits/primitives_2mio_10x5_10boxes.json",
5
+ "TestSplit" : "data/splits/objects_10_20_extrusion_die_steffi_2mio_24boxes.json",
6
+ "NetworkArch" : "deep_sdf_decoder",
7
+ "NetworkSpecs" : {
8
+ "dims" : [ 128, 128, 128, 128 ],
9
+ "dropout" : [],
10
+ "dropout_prob" : 0.2,
11
+ "norm_layers" : [0, 1, 2, 3, 4],
12
+ "latent_in" : [],
13
+ "xyz_in_all" : false,
14
+ "use_tanh" : true,
15
+ "latent_dropout" : false,
16
+ "weight_norm" : true,
17
+ "geom_dimension": 3
18
+ },
19
+ "CodeLength" : 64,
20
+ "NumEpochs" : 1000,
21
+ "SnapshotFrequency" : 500,
22
+ "AdditionalSnapshots" : [1,5],
23
+ "LearningRateSchedule" : [
24
+ {
25
+ "Type" : "Step",
26
+ "Initial" : 0.001,
27
+ "Interval" : 100,
28
+ "Factor" : 0.5
29
+ },
30
+ {
31
+ "Type" : "Step",
32
+ "Initial" : 0.001,
33
+ "Interval" : 100,
34
+ "Factor" : 0.5
35
+ }],
36
+ "SamplesPerScene" : 256,
37
+ "ScenesPerBatch" : 32,
38
+ "DataLoaderThreads" : 0,
39
+ "ClampingDistance" : 0.5,
40
+ "CodeRegularization" : true,
41
+ "CodeRegularizationLambda" : 1e-4,
42
+ "CodeBound" : 1.0
43
+ }
44
+
45
+
46
+
@@ -0,0 +1,46 @@
1
+ {
2
+ "Description" : "DeepLS trained on 500 2D primitives split into 4x4x4 boxes",
3
+ "DataSource" : "/storage/lfrei/SemperitSDF/data",
4
+ "TrainSplit" : "data/splits/primitives_11_18_primitives_2d_4boxes.json",
5
+ "TestSplit" : "data/splits/primitives_11_18_cylinder_4boxes.json",
6
+ "NetworkArch" : "deep_sdf_decoder",
7
+ "NetworkSpecs" : {
8
+ "dims" : [ 128, 128, 128, 128, 128, 128 ],
9
+ "dropout" : [],
10
+ "dropout_prob" : 0.2,
11
+ "norm_layers" : [0, 1, 2, 3 ],
12
+ "latent_in" : [],
13
+ "xyz_in_all" : false,
14
+ "use_tanh" : true,
15
+ "latent_dropout" : false,
16
+ "weight_norm" : true,
17
+ "geom_dimension": 3
18
+ },
19
+ "CodeLength" : 16,
20
+ "NumEpochs" : 2000,
21
+ "SnapshotFrequency" : 1000,
22
+ "AdditionalSnapshots" : [ 100, 500 ],
23
+ "LearningRateSchedule" : [
24
+ {
25
+ "Type" : "Step",
26
+ "Initial" : 0.001,
27
+ "Interval" : 500,
28
+ "Factor" : 0.5
29
+ },
30
+ {
31
+ "Type" : "Step",
32
+ "Initial" : 0.001,
33
+ "Interval" : 500,
34
+ "Factor" : 0.5
35
+ }],
36
+ "SamplesPerScene" : 512,
37
+ "ScenesPerBatch" : 32,
38
+ "DataLoaderThreads" : 0,
39
+ "ClampingDistance" : 1.0,
40
+ "CodeRegularization" : true,
41
+ "CodeRegularizationLambda" : 1e-4,
42
+ "CodeBound" : 1.0
43
+ }
44
+
45
+
46
+