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,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()