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,137 @@
|
|
|
1
|
+
from DeepSDFStruct.pretrained_models import get_model, PretrainedModels
|
|
2
|
+
from DeepSDFStruct.SDF import SDFfromDeepSDF, SDFfromLineMesh, CappedBorderSDF
|
|
3
|
+
from DeepSDFStruct.mesh import (
|
|
4
|
+
generate_2D_surf_mesh,
|
|
5
|
+
tetrahedralize_surface,
|
|
6
|
+
create_3D_mesh,
|
|
7
|
+
create_2D_mesh,
|
|
8
|
+
export_surface_mesh,
|
|
9
|
+
export_sdf_grid_vtk,
|
|
10
|
+
)
|
|
11
|
+
from DeepSDFStruct.lattice_structure import LatticeSDFStruct
|
|
12
|
+
from DeepSDFStruct.parametrization import SplineParametrization
|
|
13
|
+
from DeepSDFStruct.torch_spline import TorchSpline
|
|
14
|
+
import splinepy
|
|
15
|
+
import gustaf as _gus
|
|
16
|
+
import torch
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_deepsdf_lattice_export_3D():
|
|
20
|
+
# Load a pretrained DeepSDF model
|
|
21
|
+
model = get_model(PretrainedModels.AnalyticRoundCross)
|
|
22
|
+
sdf = SDFfromDeepSDF(model)
|
|
23
|
+
torch.manual_seed(42)
|
|
24
|
+
|
|
25
|
+
# Define a spline-based deformation field
|
|
26
|
+
deformation_spline = TorchSpline(
|
|
27
|
+
splinepy.helpme.create.box(1.5, 1, 1).bspline, device=model.device
|
|
28
|
+
)
|
|
29
|
+
control_points = [
|
|
30
|
+
[0.3],
|
|
31
|
+
[0.3],
|
|
32
|
+
[0.3],
|
|
33
|
+
[0.3],
|
|
34
|
+
[0.3],
|
|
35
|
+
[0.3],
|
|
36
|
+
[0.3],
|
|
37
|
+
[0.3],
|
|
38
|
+
[0.3],
|
|
39
|
+
[0.3],
|
|
40
|
+
[0.3],
|
|
41
|
+
[0.3],
|
|
42
|
+
]
|
|
43
|
+
param_spline = SplineParametrization(
|
|
44
|
+
splinepy.BSpline(
|
|
45
|
+
[0, 0, 0], [[0, 1 / 3, 2 / 3, 1], [0, 0.5, 1], [0, 0.5, 1]], control_points
|
|
46
|
+
),
|
|
47
|
+
device=model.device,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Create the lattice structure with deformation and microtile
|
|
51
|
+
lattice_struct = CappedBorderSDF(
|
|
52
|
+
LatticeSDFStruct(tiling=[3, 2, 2], microtile=sdf, parametrization=param_spline)
|
|
53
|
+
)
|
|
54
|
+
surf_mesh, derivative = create_3D_mesh(
|
|
55
|
+
lattice_struct,
|
|
56
|
+
10,
|
|
57
|
+
differentiate=True,
|
|
58
|
+
mesh_type="surface",
|
|
59
|
+
device=model.device,
|
|
60
|
+
deformation_function=deformation_spline,
|
|
61
|
+
)
|
|
62
|
+
export_surface_mesh(
|
|
63
|
+
"tests/tmp_outputs/mesh_with_derivative.vtk", surf_mesh.to_gus(), derivative
|
|
64
|
+
)
|
|
65
|
+
export_sdf_grid_vtk(lattice_struct, "tests/tmp_outputs/sdf.vtk")
|
|
66
|
+
mesh = surf_mesh.to_gus()
|
|
67
|
+
_gus.io.meshio.export("tests/tmp_outputs/faces.inp", mesh)
|
|
68
|
+
_gus.io.meshio.export("tests/tmp_outputs/faces.obj", mesh)
|
|
69
|
+
|
|
70
|
+
volumes, _ = tetrahedralize_surface(mesh)
|
|
71
|
+
_gus.io.mfem.export("tests/tmp_outputs/volumes.mfem", volumes)
|
|
72
|
+
|
|
73
|
+
# test other method to export 3D mesh:
|
|
74
|
+
volume_mesh, derivative_vols = create_3D_mesh(
|
|
75
|
+
lattice_struct, 10, differentiate=True, mesh_type="volume", device=model.device
|
|
76
|
+
)
|
|
77
|
+
_gus.io.meshio.export(
|
|
78
|
+
fname="tests/tmp_outputs/volumes.inp", mesh=volume_mesh.to_gus()
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_deepsdf_lattice_export_2D():
|
|
83
|
+
# Load a pretrained DeepSDF model
|
|
84
|
+
model = get_model(PretrainedModels.AnalyticRoundCross)
|
|
85
|
+
sdf = SDFfromDeepSDF(model)
|
|
86
|
+
torch.manual_seed(42)
|
|
87
|
+
|
|
88
|
+
# Define a spline-based deformation field
|
|
89
|
+
deformation_spline = TorchSpline(
|
|
90
|
+
splinepy.helpme.create.box(1.5, 1).bspline, device=model.device
|
|
91
|
+
)
|
|
92
|
+
control_points = [[0.3], [0.3], [0.3], [0.3], [0.3], [0.3]]
|
|
93
|
+
param_spline = SplineParametrization(
|
|
94
|
+
splinepy.BSpline([0, 0], [[0, 1 / 3, 2 / 3, 1], [0, 0.5, 1]], control_points),
|
|
95
|
+
device=model.device,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Create the lattice structure with deformation and microtile
|
|
99
|
+
lattice_struct = CappedBorderSDF(
|
|
100
|
+
LatticeSDFStruct(
|
|
101
|
+
tiling=[3, 2],
|
|
102
|
+
microtile=sdf.to2D(axes=[0, 1], offset=0.5),
|
|
103
|
+
parametrization=param_spline,
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
surf_mesh, derivative = create_2D_mesh(
|
|
108
|
+
lattice_struct,
|
|
109
|
+
10,
|
|
110
|
+
differentiate=True,
|
|
111
|
+
mesh_type="surface",
|
|
112
|
+
device=model.device,
|
|
113
|
+
deformation_function=deformation_spline,
|
|
114
|
+
)
|
|
115
|
+
export_surface_mesh(
|
|
116
|
+
"tests/tmp_outputs/mesh2D_with_derivative.vtk", surf_mesh.to_gus(), derivative
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_2D_mesh_export():
|
|
121
|
+
linemesh = _gus.io.meshio.load("tests/data/example_line_mesh.vtk")
|
|
122
|
+
linemesh.vertices = linemesh.vertices[:, :2]
|
|
123
|
+
|
|
124
|
+
sdf_from_linemesh = SDFfromLineMesh(linemesh, thickness=0.5)
|
|
125
|
+
mesh = generate_2D_surf_mesh(sdf_from_linemesh, 100)
|
|
126
|
+
_gus.io.meshio.export("tests/tmp_outputs/triangles.inp", mesh)
|
|
127
|
+
|
|
128
|
+
sdf_from_linemesh = SDFfromLineMesh(linemesh, thickness=0.5, smoothness=0.1)
|
|
129
|
+
mesh = generate_2D_surf_mesh(sdf_from_linemesh, 100)
|
|
130
|
+
_gus.io.meshio.export("tests/tmp_outputs/triangles_smooth.inp", mesh)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
if __name__ == "__main__":
|
|
134
|
+
test_deepsdf_lattice_export_2D()
|
|
135
|
+
test_deepsdf_lattice_export_3D()
|
|
136
|
+
|
|
137
|
+
test_2D_mesh_export()
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from DeepSDFStruct.mesh import torchVolumeMesh, mergeMeshs, torchLineMesh
|
|
2
|
+
import gustaf as gus
|
|
3
|
+
import torch
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_mesh_clean() -> None:
|
|
7
|
+
gus_mesh = gus.io.meshio.load("tests/data/example_disconnectd_mesh.inp")
|
|
8
|
+
verts_tensor = torch.tensor(gus_mesh.vertices, requires_grad=True)
|
|
9
|
+
volumes_tensor = torch.tensor(gus_mesh.volumes)
|
|
10
|
+
torch_vol_mesh = torchVolumeMesh(verts_tensor, volumes_tensor)
|
|
11
|
+
torch_vol_mesh.remove_disconnected_regions()
|
|
12
|
+
vols = torch_vol_mesh.to_gus()
|
|
13
|
+
assert (
|
|
14
|
+
vols.vertices.shape[0] == 194
|
|
15
|
+
), f"number of clean vertices should be 194, but is {vols.vertices.shape[0]}"
|
|
16
|
+
gus.io.meshio.export("tests/tmp_outputs/cleaned_mesh.inp", vols)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_mesh_merge():
|
|
20
|
+
# Simple line mesh 1: two vertices, one line
|
|
21
|
+
vertices1 = torch.tensor([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]])
|
|
22
|
+
lines1 = torch.tensor([[0, 1]])
|
|
23
|
+
mesh1 = torchLineMesh(vertices1, lines1)
|
|
24
|
+
|
|
25
|
+
# Simple line mesh 2: one duplicate vertex and one new vertex
|
|
26
|
+
vertices2 = torch.tensor([[1.0, 0.0, 0.0], [2.0, 0.0, 0.0]])
|
|
27
|
+
lines2 = torch.tensor([[0, 1]])
|
|
28
|
+
mesh2 = torchLineMesh(vertices2, lines2)
|
|
29
|
+
|
|
30
|
+
merged_mesh = mergeMeshs(mesh1, mesh2, tol=1e-8)
|
|
31
|
+
expected_vertices = torch.tensor(
|
|
32
|
+
[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [2.0, 0.0, 0.0]]
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
assert torch.allclose(merged_mesh.vertices, expected_vertices), "Vertices mismatch"
|
|
36
|
+
# Check merged connectivity
|
|
37
|
+
expected_lines = torch.tensor([[0, 1], [1, 2]])
|
|
38
|
+
assert torch.equal(
|
|
39
|
+
merged_mesh.lines, expected_lines
|
|
40
|
+
), f"Line connectivity mismatch. Expected\n {expected_lines}\n got \n{merged_mesh.lines}"
|
|
41
|
+
# Check no indices exceed number of vertices
|
|
42
|
+
assert (
|
|
43
|
+
merged_mesh.lines.max().item() < merged_mesh.vertices.shape[0]
|
|
44
|
+
), "Index out of range"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
test_mesh_merge()
|
|
49
|
+
test_mesh_clean()
|
tests/test_networks.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
|
|
3
|
+
from DeepSDFStruct.deep_sdf.networks.hierarchical_positional_sdf_decoder import (
|
|
4
|
+
HierachicalPositionalDeepSDFDecoder,
|
|
5
|
+
)
|
|
6
|
+
from DeepSDFStruct.deep_sdf.networks.hierarchical_deep_sdf_decoder import (
|
|
7
|
+
HierachicalDeepSDFDecoder,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_hierarchical_positional_decoder_forward():
|
|
12
|
+
batch = 4
|
|
13
|
+
latent_size = 8
|
|
14
|
+
geom_dim = 3
|
|
15
|
+
dims = [32, 16, 8]
|
|
16
|
+
# inject latent at layer 0 (before first linear)
|
|
17
|
+
latent_in = [0]
|
|
18
|
+
|
|
19
|
+
net = HierachicalPositionalDeepSDFDecoder(
|
|
20
|
+
latent_size=latent_size,
|
|
21
|
+
dims=dims,
|
|
22
|
+
geom_dimension=geom_dim,
|
|
23
|
+
latent_in=latent_in,
|
|
24
|
+
positional_encoding=False,
|
|
25
|
+
activation_fun="gelu",
|
|
26
|
+
)
|
|
27
|
+
net.eval()
|
|
28
|
+
|
|
29
|
+
lat = torch.randn(batch, latent_size)
|
|
30
|
+
xyz = torch.randn(batch, geom_dim)
|
|
31
|
+
inp = torch.cat([lat, xyz], dim=1)
|
|
32
|
+
|
|
33
|
+
with torch.no_grad():
|
|
34
|
+
out = net(inp)
|
|
35
|
+
|
|
36
|
+
assert out.shape == (batch, 1)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_hierarchical_deep_decoder_forward():
|
|
40
|
+
batch = 5
|
|
41
|
+
geom_dim = 3
|
|
42
|
+
# one latent chunk of size 4 injected into layer 1
|
|
43
|
+
latent_size = [4]
|
|
44
|
+
latent_in = [1]
|
|
45
|
+
dims = [32, 16]
|
|
46
|
+
|
|
47
|
+
net = HierachicalDeepSDFDecoder(
|
|
48
|
+
latent_size=latent_size,
|
|
49
|
+
dims=dims,
|
|
50
|
+
geom_dimension=geom_dim,
|
|
51
|
+
latent_in=latent_in,
|
|
52
|
+
activation_fun="silu",
|
|
53
|
+
)
|
|
54
|
+
net.eval()
|
|
55
|
+
|
|
56
|
+
lat = torch.randn(batch, sum(latent_size))
|
|
57
|
+
xyz = torch.randn(batch, geom_dim)
|
|
58
|
+
inp = torch.cat([lat, xyz], dim=1)
|
|
59
|
+
|
|
60
|
+
with torch.no_grad():
|
|
61
|
+
out = net(inp)
|
|
62
|
+
|
|
63
|
+
# hierarchical_deep_sdf_decoder returns a raw tensor (no explicit final layer),
|
|
64
|
+
# ensure it returns per-sample outputs (batch, *) and is finite
|
|
65
|
+
assert out.shape[0] == batch
|
|
66
|
+
assert torch.isfinite(out).all()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
test_hierarchical_deep_decoder_forward()
|
|
71
|
+
test_hierarchical_positional_decoder_forward()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from DeepSDFStruct.pretrained_models import get_model, PretrainedModels
|
|
2
|
+
from DeepSDFStruct.SDF import SDFfromDeepSDF
|
|
3
|
+
import torch
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_pretrained_model_evaluation():
|
|
7
|
+
for pt_model in PretrainedModels:
|
|
8
|
+
print(f"Testing pretrained model: {pt_model.name}")
|
|
9
|
+
model = get_model(pt_model)
|
|
10
|
+
sdf = SDFfromDeepSDF(model)
|
|
11
|
+
|
|
12
|
+
# Set the latent vector and visualize a slice of the SDF
|
|
13
|
+
sdf.set_latent_vec(model._trained_latent_vectors[0])
|
|
14
|
+
|
|
15
|
+
out = sdf(
|
|
16
|
+
torch.tensor(
|
|
17
|
+
[[0, 0, 0], [0, 1, 0], [0.5, 0.5, 0.5]],
|
|
18
|
+
dtype=torch.float32,
|
|
19
|
+
device=model.device,
|
|
20
|
+
)
|
|
21
|
+
)
|
|
22
|
+
print(out)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_analytic_round_cross():
|
|
26
|
+
|
|
27
|
+
model = get_model(PretrainedModels.AnalyticRoundCross)
|
|
28
|
+
sdf = SDFfromDeepSDF(model)
|
|
29
|
+
|
|
30
|
+
for radius in [0.2, 0.5, 0.7]:
|
|
31
|
+
radius_tensor = torch.tensor([radius], dtype=torch.float32, device=model.device)
|
|
32
|
+
sdf.set_latent_vec(radius_tensor)
|
|
33
|
+
|
|
34
|
+
out = sdf(torch.tensor([[0, 0, 0]], dtype=torch.float32, device=model.device))
|
|
35
|
+
torch.testing.assert_close(out[0], -radius_tensor)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if __name__ == "__main__":
|
|
39
|
+
test_pretrained_model_evaluation()
|
|
40
|
+
test_analytic_round_cross()
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import trimesh
|
|
2
|
+
from DeepSDFStruct.pretrained_models import get_model, PretrainedModels
|
|
3
|
+
from DeepSDFStruct.SDF import SDFfromDeepSDF, SDFfromMesh, normalize_mesh_to_unit_cube
|
|
4
|
+
from DeepSDFStruct.mesh import create_3D_mesh, export_sdf_grid_vtk, export_surface_mesh
|
|
5
|
+
from DeepSDFStruct.deep_sdf.reconstruction import reconstruct_from_samples
|
|
6
|
+
from DeepSDFStruct.lattice_structure import LatticeSDFStruct
|
|
7
|
+
from DeepSDFStruct.parametrization import SplineParametrization
|
|
8
|
+
from DeepSDFStruct.torch_spline import TorchScaling
|
|
9
|
+
from DeepSDFStruct.sampling import sample_mesh_surface, random_sample_sdf
|
|
10
|
+
import splinepy
|
|
11
|
+
import torch
|
|
12
|
+
import numpy as np
|
|
13
|
+
from DeepSDFStruct.deep_sdf.metrics.mesh_to_analytical import mesh_to_analytical
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_shape_reconstruction():
|
|
17
|
+
model = get_model(PretrainedModels.Primitives)
|
|
18
|
+
sdf = SDFfromDeepSDF(model)
|
|
19
|
+
torch.manual_seed(42)
|
|
20
|
+
|
|
21
|
+
mesh_orig = trimesh.load_mesh("tests/data/cone.stl")
|
|
22
|
+
mesh, scale, shift = normalize_mesh_to_unit_cube(
|
|
23
|
+
mesh_orig.copy(), shrink_factor=0.9
|
|
24
|
+
)
|
|
25
|
+
bounds = torch.tensor(
|
|
26
|
+
[[-1.0, -1.0, -1.0], [1.0, 1.0, 1.0]], device=model.device, dtype=torch.float32
|
|
27
|
+
)
|
|
28
|
+
bounds = torch.tensor(mesh.bounds, device=model.device, dtype=torch.float32)
|
|
29
|
+
print("Mesh bounds:", bounds)
|
|
30
|
+
scaling = TorchScaling(
|
|
31
|
+
scale_factors=scale, translation=shift, bounds=bounds, device=model.device
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
tiling = [2, 2, 2]
|
|
35
|
+
param_spline_sp = splinepy.BSpline(
|
|
36
|
+
[1, 1, 1],
|
|
37
|
+
[[0, 0, 1, 1], [0, 0, 1, 1], [0, 0, 1, 1]],
|
|
38
|
+
[[0.0] * model._trained_latent_vectors[0].shape[0]] * 8,
|
|
39
|
+
)
|
|
40
|
+
for i_box, n_box in enumerate(tiling):
|
|
41
|
+
knots = np.linspace(0, 1, n_box + 1)[1:-1]
|
|
42
|
+
print(f"Inserting {n_box - 1} knots at {knots} into spline dim {i_box}")
|
|
43
|
+
param_spline_sp.insert_knots(i_box, knots)
|
|
44
|
+
|
|
45
|
+
param_spline = SplineParametrization(param_spline_sp, device=model.device)
|
|
46
|
+
for p in param_spline.parameters():
|
|
47
|
+
torch.nn.init.normal_(p, mean=0.0, std=0.01)
|
|
48
|
+
|
|
49
|
+
struct = LatticeSDFStruct(
|
|
50
|
+
tiling=tiling, microtile=sdf, parametrization=param_spline, bounds=bounds
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
gt_sdf = SDFfromMesh(mesh, scale=False)
|
|
54
|
+
|
|
55
|
+
uniform_samples = random_sample_sdf(
|
|
56
|
+
gt_sdf,
|
|
57
|
+
bounds,
|
|
58
|
+
n_samples=5000,
|
|
59
|
+
sampling_strategy="uniform",
|
|
60
|
+
device=struct.get_device(),
|
|
61
|
+
)
|
|
62
|
+
surface_samples = sample_mesh_surface(
|
|
63
|
+
gt_sdf, mesh, n_samples=5000, stds=[0.25, 0.0001], device=struct.get_device()
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
SDF_samples = uniform_samples + surface_samples
|
|
67
|
+
|
|
68
|
+
recon_param = reconstruct_from_samples(
|
|
69
|
+
struct,
|
|
70
|
+
SDF_samples,
|
|
71
|
+
lr=5e-3,
|
|
72
|
+
loss_fn="ClampedL1",
|
|
73
|
+
num_iterations=50,
|
|
74
|
+
batch_size=512,
|
|
75
|
+
use_tanh_on_gt=False,
|
|
76
|
+
loss_plot_path="tests/tmp_outputs/reconstruction_loss.png",
|
|
77
|
+
optimizer_name="adam",
|
|
78
|
+
deformation_function=None,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
surf_mesh, derivative = create_3D_mesh(
|
|
82
|
+
struct,
|
|
83
|
+
32,
|
|
84
|
+
differentiate=False,
|
|
85
|
+
device=model.device,
|
|
86
|
+
mesh_type="surface",
|
|
87
|
+
bounds=bounds,
|
|
88
|
+
deformation_function=scaling,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
export_surface_mesh(
|
|
92
|
+
"tests/tmp_outputs/reconstructed_cone.stl", surf_mesh.to_gus(), derivative
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
error = mesh_to_analytical(SDFfromMesh(mesh_orig, scale=False), surf_mesh)
|
|
96
|
+
print(f"Norm of SDF error on mesh vertices: {error}")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
if __name__ == "__main__":
|
|
100
|
+
|
|
101
|
+
test_shape_reconstruction()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import trimesh
|
|
3
|
+
from DeepSDFStruct.SDF import SDFfromMesh
|
|
4
|
+
from DeepSDFStruct.sdf_primitives import SphereSDF
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_icosphere_sdf_igl():
|
|
9
|
+
mesh = trimesh.creation.icosphere(subdivisions=6, radius=1.0)
|
|
10
|
+
|
|
11
|
+
sdf_sphere_from_mesh = SDFfromMesh(mesh, backend="igl", scale=False, threshold=0)
|
|
12
|
+
sdf_sphere_from_analytical = SphereSDF(center=[0, 0, 0], radius=1.0)
|
|
13
|
+
points = torch.tensor(
|
|
14
|
+
[
|
|
15
|
+
[0.0, 0.0, 0.0],
|
|
16
|
+
[0.5, 0.0, 0.0],
|
|
17
|
+
[1.0, 0.0, 0.0],
|
|
18
|
+
[1.5, 0.0, 0.0],
|
|
19
|
+
[2.0, 2.0, 2.0],
|
|
20
|
+
],
|
|
21
|
+
dtype=torch.float32,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
distances_mesh = sdf_sphere_from_mesh(points)
|
|
25
|
+
distances_analyt = sdf_sphere_from_analytical(points)
|
|
26
|
+
|
|
27
|
+
print("\n--- SDF Test Results ---")
|
|
28
|
+
for i, p in enumerate(points):
|
|
29
|
+
print(
|
|
30
|
+
f"Point {p.tolist()}: Distance Mesh {distances_mesh[i,0]:.4f} | Distance Analytical {distances_analyt[i,0]:.4f}"
|
|
31
|
+
)
|
|
32
|
+
expected_distances = torch.tensor(
|
|
33
|
+
[[-1.0], [-0.5], [0.0], [0.5], [math.sqrt(2**2 + 2**2 + 2**2) - 1]]
|
|
34
|
+
)
|
|
35
|
+
torch.testing.assert_close(
|
|
36
|
+
distances_mesh,
|
|
37
|
+
expected_distances,
|
|
38
|
+
msg="Queried distances to mesh are wrong",
|
|
39
|
+
atol=1e-4,
|
|
40
|
+
rtol=1e-4,
|
|
41
|
+
)
|
|
42
|
+
torch.testing.assert_close(
|
|
43
|
+
distances_mesh,
|
|
44
|
+
expected_distances,
|
|
45
|
+
msg="Queried analytical distances are wrong",
|
|
46
|
+
atol=1e-4,
|
|
47
|
+
rtol=1e-4,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
test_icosphere_sdf_igl()
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import gustaf as gus
|
|
2
|
+
import torch
|
|
3
|
+
import numpy as np
|
|
4
|
+
import itertools
|
|
5
|
+
import splinepy
|
|
6
|
+
from DeepSDFStruct.SDF import SDFfromLineMesh, CappedBorderSDF, SDFfromMesh
|
|
7
|
+
from DeepSDFStruct.mesh import export_sdf_grid_vtk, export_surface_mesh, create_3D_mesh
|
|
8
|
+
from DeepSDFStruct.sdf_primitives import CrossMsSDF
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_sdf_from_multipatch_cubes_equivalence():
|
|
12
|
+
|
|
13
|
+
# --- define cubes ---
|
|
14
|
+
cube1 = splinepy.helpme.create.box(1.0, 1.0, 1.0)
|
|
15
|
+
|
|
16
|
+
cube2 = splinepy.helpme.create.box(1.0, 1.0, 1.0)
|
|
17
|
+
cube2.control_points[:, 0] = cube2.control_points[:, 0] + 1
|
|
18
|
+
big_cube = splinepy.helpme.create.box(2.0, 1.0, 1.0)
|
|
19
|
+
|
|
20
|
+
multipatch = splinepy.Multipatch([cube1, cube2])
|
|
21
|
+
|
|
22
|
+
n = 10
|
|
23
|
+
xs = torch.linspace(-0.5, 2.5, n)
|
|
24
|
+
ys = torch.linspace(-0.5, 1.5, n)
|
|
25
|
+
zs = torch.linspace(-0.5, 1.5, n)
|
|
26
|
+
|
|
27
|
+
queries = torch.stack(torch.meshgrid(xs, ys, zs, indexing="ij"), dim=-1).reshape(
|
|
28
|
+
-1, 3
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
multipatch_sdf = SDFfromMesh(multipatch.extract.faces(10))
|
|
32
|
+
big_cube_sdf = SDFfromMesh(big_cube.extract.faces(10))
|
|
33
|
+
|
|
34
|
+
vals_multipatch = multipatch_sdf(queries)
|
|
35
|
+
vals_big_cube = big_cube_sdf(queries)
|
|
36
|
+
|
|
37
|
+
torch.testing.assert_close(
|
|
38
|
+
vals_multipatch,
|
|
39
|
+
vals_big_cube,
|
|
40
|
+
atol=1e-2,
|
|
41
|
+
rtol=1e-2,
|
|
42
|
+
msg="Unified multipatch does not match single large cube",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_cap_border_dict():
|
|
47
|
+
n = 11
|
|
48
|
+
xs = torch.linspace(-1.0, 1.0, n)
|
|
49
|
+
ys = torch.linspace(-1.0, 1.0, n)
|
|
50
|
+
zs = torch.linspace(-1.0, 1.0, n)
|
|
51
|
+
|
|
52
|
+
queries = torch.stack(torch.meshgrid(xs, ys, zs, indexing="ij"), dim=-1).reshape(
|
|
53
|
+
-1, 3
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
base_sdf = CrossMsSDF(radius=0.2)
|
|
57
|
+
export_sdf_grid_vtk(base_sdf, "sdf_nocap.vtk")
|
|
58
|
+
base_sdf_vals = base_sdf(queries)
|
|
59
|
+
|
|
60
|
+
axes = ["x", "y", "z"]
|
|
61
|
+
sides = ["0", "1"]
|
|
62
|
+
caps = [1, -1]
|
|
63
|
+
|
|
64
|
+
dim_dict = {"x": 0, "y": 1, "z": 2}
|
|
65
|
+
minstr_dict = {1: "plus", -1: "minus"}
|
|
66
|
+
measure = 0.25
|
|
67
|
+
|
|
68
|
+
for axis, side, cap in itertools.product(axes, sides, caps):
|
|
69
|
+
minstr = minstr_dict[cap]
|
|
70
|
+
name = f"{axis}{side}{caps}{minstr}"
|
|
71
|
+
key = f"{axis}{side}"
|
|
72
|
+
cap_border_dict = {key: {"cap": cap, "measure": measure}}
|
|
73
|
+
|
|
74
|
+
capped_sdf = CappedBorderSDF(base_sdf, cap_border_dict)
|
|
75
|
+
print("Evaluating capdict for cap dict")
|
|
76
|
+
print(cap_border_dict)
|
|
77
|
+
capped_sdf_vals = capped_sdf(queries)
|
|
78
|
+
export_sdf_grid_vtk(capped_sdf, f"tests/tmp_outputs/sdf_{name}.vtk")
|
|
79
|
+
surf_mesh, _ = create_3D_mesh(capped_sdf, 11, mesh_type="surface")
|
|
80
|
+
export_surface_mesh(f"tests/tmp_outputs/sdf_{name}.obj", surf_mesh.to_gus())
|
|
81
|
+
export_surface_mesh(f"tests/tmp_outputs/sdf_{name}.stl", surf_mesh.to_gus())
|
|
82
|
+
interior_low = caps[0] + measure
|
|
83
|
+
interior_high = caps[1] - measure
|
|
84
|
+
|
|
85
|
+
inside_mask = (queries[:, dim_dict[axis]] > interior_low) & (
|
|
86
|
+
queries[:, dim_dict[axis]] < interior_high
|
|
87
|
+
)
|
|
88
|
+
torch.testing.assert_close(
|
|
89
|
+
capped_sdf_vals[inside_mask],
|
|
90
|
+
base_sdf_vals[inside_mask],
|
|
91
|
+
msg=f"Inside modified for {name}, cap={cap}, measure={measure}",
|
|
92
|
+
)
|
|
93
|
+
# raise NotImplementedError("stahp")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_sdf_from_linemesh():
|
|
97
|
+
linemesh = gus.io.meshio.load("tests/data/example_line_mesh.vtk")
|
|
98
|
+
linemesh.vertices = linemesh.vertices[:, :2]
|
|
99
|
+
bounds = np.array([[-9.5, 0.0], [9.5, 20.0]])
|
|
100
|
+
n_points = 100
|
|
101
|
+
x = np.linspace(bounds[0, 0], bounds[1, 0], n_points)
|
|
102
|
+
y = np.linspace(bounds[0, 1], bounds[1, 1], n_points)
|
|
103
|
+
xx, yy = np.meshgrid(x, y)
|
|
104
|
+
queries = np.hstack([xx.reshape(-1, 1), yy.reshape(-1, 1)])
|
|
105
|
+
sdf_from_linemesh = SDFfromLineMesh(linemesh, thickness=0.5)
|
|
106
|
+
sdf = sdf_from_linemesh(queries)
|
|
107
|
+
np.testing.assert_almost_equal(sdf.min(), -0.25)
|
|
108
|
+
np.testing.assert_array_compare(np.greater_equal, sdf.max(), 0)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
import warnings
|
|
113
|
+
|
|
114
|
+
warnings.filterwarnings("error")
|
|
115
|
+
test_sdf_from_multipatch_cubes_equivalence()
|
|
116
|
+
test_cap_border_dict()
|
|
117
|
+
test_sdf_from_linemesh()
|