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.
- DeepSDFStruct/SDF.py +1734 -0
- DeepSDFStruct/__init__.py +66 -0
- DeepSDFStruct/deep_sdf/__init__.py +69 -0
- DeepSDFStruct/deep_sdf/create_screenshots_from_plyfiles.py +65 -0
- DeepSDFStruct/deep_sdf/data.py +258 -0
- DeepSDFStruct/deep_sdf/metrics/__init__.py +14 -0
- DeepSDFStruct/deep_sdf/metrics/mesh_to_analytical.py +24 -0
- DeepSDFStruct/deep_sdf/models.py +197 -0
- DeepSDFStruct/deep_sdf/networks/__init__.py +8 -0
- DeepSDFStruct/deep_sdf/networks/analytic_round_cross.py +105 -0
- DeepSDFStruct/deep_sdf/networks/deep_sdf_decoder.py +155 -0
- DeepSDFStruct/deep_sdf/networks/hierarchical_deep_sdf_decoder.py +139 -0
- DeepSDFStruct/deep_sdf/networks/hierarchical_positional_sdf_decoder.py +190 -0
- DeepSDFStruct/deep_sdf/networks/resnet_positional_sdf_decoder.py +108 -0
- DeepSDFStruct/deep_sdf/nn_utils.py +70 -0
- DeepSDFStruct/deep_sdf/plotting.py +143 -0
- DeepSDFStruct/deep_sdf/reconstruction.py +165 -0
- DeepSDFStruct/deep_sdf/training.py +840 -0
- DeepSDFStruct/deep_sdf/workspace.py +363 -0
- DeepSDFStruct/design_of_experiments.py +235 -0
- DeepSDFStruct/flexicubes/__init__.py +15 -0
- DeepSDFStruct/flexicubes/flexicubes.py +1005 -0
- DeepSDFStruct/flexicubes/tables.py +2338 -0
- DeepSDFStruct/flexisquares/__init__.py +15 -0
- DeepSDFStruct/flexisquares/flexisquares.py +967 -0
- DeepSDFStruct/flexisquares/tables.py +63 -0
- DeepSDFStruct/lattice_structure.py +254 -0
- DeepSDFStruct/local_shapes.py +226 -0
- DeepSDFStruct/mesh.py +1184 -0
- DeepSDFStruct/optimization.py +357 -0
- DeepSDFStruct/parametrization.py +215 -0
- DeepSDFStruct/pretrained_models.py +128 -0
- DeepSDFStruct/sampling.py +670 -0
- DeepSDFStruct/sdf_operations.py +990 -0
- DeepSDFStruct/sdf_primitives.py +1416 -0
- DeepSDFStruct/splinepy_unitcells/__init__.py +35 -0
- DeepSDFStruct/splinepy_unitcells/chi_3D.py +1617 -0
- DeepSDFStruct/splinepy_unitcells/cross_lattice.py +165 -0
- DeepSDFStruct/splinepy_unitcells/double_lattice_extruded.py +298 -0
- DeepSDFStruct/splinepy_unitcells/snappy_3d.py +544 -0
- DeepSDFStruct/torch_spline.py +446 -0
- DeepSDFStruct/trained_models/analytic_round_cross/LatentCodes/latest.pth +0 -0
- DeepSDFStruct/trained_models/analytic_round_cross/ModelParameters/latest.pth +0 -0
- DeepSDFStruct/trained_models/analytic_round_cross/OptimizerParameters/latest.pth +0 -0
- DeepSDFStruct/trained_models/analytic_round_cross/specs.json +45 -0
- DeepSDFStruct/trained_models/chi_and_cross/LatentCodes/latest.pth +0 -0
- DeepSDFStruct/trained_models/chi_and_cross/Logs.pth +0 -0
- DeepSDFStruct/trained_models/chi_and_cross/ModelParameters/latest.pth +0 -0
- DeepSDFStruct/trained_models/chi_and_cross/OptimizerParameters/latest.pth +0 -0
- DeepSDFStruct/trained_models/chi_and_cross/specs.json +76 -0
- DeepSDFStruct/trained_models/primitives/LatentCodes/latest.pth +0 -0
- DeepSDFStruct/trained_models/primitives/ModelParameters/latest.pth +0 -0
- DeepSDFStruct/trained_models/primitives/OptimizerParameters/latest.pth +0 -0
- DeepSDFStruct/trained_models/primitives/specs.json +46 -0
- DeepSDFStruct/trained_models/primitives_2d/LatentCodes/latest.pth +0 -0
- DeepSDFStruct/trained_models/primitives_2d/ModelParameters/latest.pth +0 -0
- DeepSDFStruct/trained_models/primitives_2d/OptimizerParameters/latest.pth +0 -0
- DeepSDFStruct/trained_models/primitives_2d/specs.json +46 -0
- DeepSDFStruct/trained_models/round_cross/LatentCodes/latest.pth +0 -0
- DeepSDFStruct/trained_models/round_cross/Logs.pth +0 -0
- DeepSDFStruct/trained_models/round_cross/ModelParameters/latest.pth +0 -0
- DeepSDFStruct/trained_models/round_cross/OptimizerParameters/latest.pth +0 -0
- DeepSDFStruct/trained_models/round_cross/specs.json +45 -0
- DeepSDFStruct/trained_models/test_experiment/LatentCodes/latent_code_data_map.json +106 -0
- DeepSDFStruct/trained_models/test_experiment/specs.json +45 -0
- DeepSDFStruct/trained_models/test_experiment/training_summary.json +10 -0
- DeepSDFStruct/trained_models/test_experiment_hierarchical/LatentCodes/latent_code_data_map.json +106 -0
- DeepSDFStruct/trained_models/test_experiment_hierarchical/specs.json +45 -0
- DeepSDFStruct/trained_models/test_experiment_hierarchical/training_summary.json +10 -0
- DeepSDFStruct/trained_models/test_experiment_homogenization/LatentCodes/latent_code_data_map.json +5006 -0
- DeepSDFStruct/trained_models/test_experiment_homogenization/LatentCodes/latest.pth +0 -0
- DeepSDFStruct/trained_models/test_experiment_homogenization/Logs.png +0 -0
- DeepSDFStruct/trained_models/test_experiment_homogenization/ModelParameters/latest.pth +0 -0
- DeepSDFStruct/trained_models/test_experiment_homogenization/OptimizerParameters/latest.pth +0 -0
- DeepSDFStruct/trained_models/test_experiment_homogenization/specs.json +83 -0
- DeepSDFStruct/trained_models/test_experiment_homogenization/training_summary.json +10 -0
- DeepSDFStruct/utils.py +96 -0
- DeepSDFStruct/visualization/latent_viewer.py +211 -0
- DeepSDFStruct/visualization/latent_viewer_cli.py +8 -0
- benchmarks/generate_sdf_showcase.py +622 -0
- benchmarks/sdf_from_mesh_benchmark.py +52 -0
- benchmarks/sdf_from_mesh_comparison.md +33 -0
- benchmarks/sdf_showcase/operations/boolean/difference_sphere_box.png +0 -0
- benchmarks/sdf_showcase/operations/boolean/union_sphere_box.png +0 -0
- benchmarks/sdf_showcase/operations/transformations/circular_array_sphere.png +0 -0
- benchmarks/sdf_showcase/operations/transformations/dilate_sphere.png +0 -0
- benchmarks/sdf_showcase/operations/transformations/mirror_sphere.png +0 -0
- benchmarks/sdf_showcase/operations/transformations/repeat_sphere.png +0 -0
- benchmarks/sdf_showcase/operations/transformations/revolve_circle.png +0 -0
- benchmarks/sdf_showcase/operations/transformations/shell_sphere.png +0 -0
- benchmarks/sdf_showcase/operations/transformations/twist_torus.png +0 -0
- benchmarks/sdf_showcase/primitives/2D/circle.png +0 -0
- benchmarks/sdf_showcase/primitives/2D/equilateral_triangle.png +0 -0
- benchmarks/sdf_showcase/primitives/2D/hexagon.png +0 -0
- benchmarks/sdf_showcase/primitives/2D/polygon_pentagon.png +0 -0
- benchmarks/sdf_showcase/primitives/2D/rectangle.png +0 -0
- benchmarks/sdf_showcase/primitives/2D/rounded_rectangle.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/box.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/capped_cone.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/capped_cylinder.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/capsule.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/cone.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/corner_spheres.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/cross_ms.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/cylinder.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/dodecahedron.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/ellipsoid.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/icosahedron.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/octahedron.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/pyramid.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/rounded_box.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/rounded_cone.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/rounded_cylinder.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/sphere.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/tetrahedron.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/torus.png +0 -0
- benchmarks/sdf_showcase/primitives/3D/wireframe_box.png +0 -0
- deepsdfstruct-1.7.2.dist-info/METADATA +44 -0
- deepsdfstruct-1.7.2.dist-info/RECORD +165 -0
- deepsdfstruct-1.7.2.dist-info/WHEEL +5 -0
- deepsdfstruct-1.7.2.dist-info/licenses/LICENSE +208 -0
- deepsdfstruct-1.7.2.dist-info/licenses/NOTICE +67 -0
- deepsdfstruct-1.7.2.dist-info/scm_file_list.json +178 -0
- deepsdfstruct-1.7.2.dist-info/scm_version.json +8 -0
- deepsdfstruct-1.7.2.dist-info/top_level.txt +4 -0
- docs/Makefile +20 -0
- docs/api_reference.rst +6 -0
- docs/conf.py +60 -0
- docs/index.rst +136 -0
- docs/make.bat +35 -0
- docs/qr_code_github.png +0 -0
- docs/qr_code_paper.png +0 -0
- docs/readme_images/example_output_01.png +0 -0
- docs/readme_images/example_output_02.png +0 -0
- docs/readme_images/example_output_03.png +0 -0
- docs/readme_images/example_output_04.png +0 -0
- tests/data/chairs/1005.obj +282 -0
- tests/data/chairs/1024.obj +222 -0
- tests/data/chairs/README.md +1 -0
- tests/data/circular_balls_test_case.stl +0 -0
- tests/data/cone.stl +1794 -0
- tests/data/example_disconnectd_mesh.inp +1784 -0
- tests/data/example_line_mesh.vtk +0 -0
- tests/data/stanford_bunny.stl +0 -0
- tests/data/sweep_test_case.stl +0 -0
- tests/test_DOE.py +49 -0
- tests/test_elongate_sdf.py +195 -0
- tests/test_flexisquares.py +187 -0
- tests/test_generate_dataset.py +76 -0
- tests/test_lattice_evaluation.py +137 -0
- tests/test_mesh_export.py +137 -0
- tests/test_mesh_functions.py +49 -0
- tests/test_networks.py +71 -0
- tests/test_pretrained_models.py +40 -0
- tests/test_reconstruction.py +101 -0
- tests/test_sdf_from_mesh.py +52 -0
- tests/test_sdf_functions.py +117 -0
- tests/test_sdf_primitives.py +668 -0
- tests/test_sdf_trimesh_comparison.py +482 -0
- tests/test_splinepy_unitcells.py +64 -0
- tests/test_structural_optimization.py +178 -0
- tests/test_torch_spline.py +216 -0
- tests/test_train_model.py +110 -0
- tests/test_training_data_ids.py +41 -0
- 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
|
|
Binary file
|
|
@@ -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
|
+
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
+
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
+
|
|
Binary file
|
|
Binary file
|
|
Binary file
|