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
|
Binary file
|
|
Binary file
|
|
Binary file
|
tests/test_DOE.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from DeepSDFStruct.design_of_experiments import run_experiment, ExperimentSpecifications
|
|
2
|
+
from huggingface_hub import snapshot_download
|
|
3
|
+
from huggingface_hub.utils import HfHubHTTPError
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def snapshot_download_with_retry(*args, max_retries=3, **kwargs):
|
|
8
|
+
"""Download with retry on 429 rate limit errors."""
|
|
9
|
+
for attempt in range(max_retries):
|
|
10
|
+
try:
|
|
11
|
+
return snapshot_download(*args, **kwargs)
|
|
12
|
+
except HfHubHTTPError as e:
|
|
13
|
+
if e.response and e.response.status_code == 429:
|
|
14
|
+
if attempt < max_retries - 1:
|
|
15
|
+
wait_time = 60 * (attempt + 1)
|
|
16
|
+
print(
|
|
17
|
+
f"Rate limited (429). Waiting {wait_time}s before retry {attempt + 2}/{max_retries}"
|
|
18
|
+
)
|
|
19
|
+
time.sleep(wait_time)
|
|
20
|
+
else:
|
|
21
|
+
raise
|
|
22
|
+
raise
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_DOE():
|
|
26
|
+
data_dir = snapshot_download_with_retry(
|
|
27
|
+
"mkofler/lattice_structure_unit_cells",
|
|
28
|
+
repo_type="dataset",
|
|
29
|
+
revision="b80339abc071df77ff81e8abc19ad4856d96ddbd",
|
|
30
|
+
)
|
|
31
|
+
specs = ExperimentSpecifications(
|
|
32
|
+
"DeepSDFStruct/trained_models/test_experiment/specs.json"
|
|
33
|
+
)
|
|
34
|
+
# Define hyperparameter grid / overrides
|
|
35
|
+
tuning_params = [
|
|
36
|
+
{"NetworkSpecs": {"latent_in": [2], "dropout_prob": 0.2}},
|
|
37
|
+
{"NetworkSpecs": {"latent_in": [3], "dropout_prob": 0.3}},
|
|
38
|
+
{"NetworkSpecs": {"latent_in": [4], "dropout_prob": 0.4}},
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
# Run experiments
|
|
42
|
+
for i, overrides in enumerate(tuning_params):
|
|
43
|
+
specs.update(overrides)
|
|
44
|
+
exp_name = f"run_{i}"
|
|
45
|
+
run_experiment(exp_name, data_dir, specs=specs, device="cpu")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if __name__ == "__main__":
|
|
49
|
+
test_DOE()
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test ElongateSDF and wrapper SDFs with SmoothUnion operations.
|
|
3
|
+
|
|
4
|
+
This test ensures that wrapper SDFs properly preserve tensor shapes
|
|
5
|
+
when used in combination with SmoothUnionSDF and other composite operations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
import torch
|
|
10
|
+
from DeepSDFStruct.SDF import SmoothUnionSDF, UnionSDF
|
|
11
|
+
from DeepSDFStruct.sdf_operations import (
|
|
12
|
+
ElongateSDF,
|
|
13
|
+
TwistSDF,
|
|
14
|
+
BendLinearSDF,
|
|
15
|
+
BendRadialSDF,
|
|
16
|
+
DilateSDF,
|
|
17
|
+
ErodeSDF,
|
|
18
|
+
ShellSDF,
|
|
19
|
+
MirrorSDF,
|
|
20
|
+
)
|
|
21
|
+
from DeepSDFStruct.sdf_primitives import SphereSDF, RoundedBoxSDF, CapsuleSDF
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TestElongateSDF:
|
|
25
|
+
"""Test ElongateSDF wrapper."""
|
|
26
|
+
|
|
27
|
+
def test_basic_elongate(self):
|
|
28
|
+
"""Test basic ElongateSDF functionality."""
|
|
29
|
+
sphere = SphereSDF(center=[0, 0, 0], radius=0.5)
|
|
30
|
+
elongated = ElongateSDF(sphere, size=[0.5, 0.5, 0.5])
|
|
31
|
+
|
|
32
|
+
queries = torch.randn(100, 3)
|
|
33
|
+
result = elongated(queries)
|
|
34
|
+
|
|
35
|
+
assert result.shape == (100, 1), f"Expected shape (100, 1), got {result.shape}"
|
|
36
|
+
assert torch.isfinite(result).all(), "Result contains NaN or Inf"
|
|
37
|
+
|
|
38
|
+
def test_elongate_with_smooth_union(self):
|
|
39
|
+
"""Test ElongateSDF in SmoothUnion - reproduces the original bug."""
|
|
40
|
+
sphere = SphereSDF(center=[0, 0, 0], radius=0.3)
|
|
41
|
+
elongated_sphere = ElongateSDF(sphere, size=[0.5, 0.2, 0.2])
|
|
42
|
+
capsule = CapsuleSDF(point_a=[1.0, 0, 0], point_b=[1.5, 0, 0], radius=0.2)
|
|
43
|
+
box = RoundedBoxSDF(center=[-1.0, 0, 0], extents=[0.5, 0.5, 0.5], radius=0.1)
|
|
44
|
+
|
|
45
|
+
smooth_union = SmoothUnionSDF(elongated_sphere, capsule, box, k=0.1)
|
|
46
|
+
|
|
47
|
+
queries = torch.randn(1000, 3)
|
|
48
|
+
result = smooth_union(queries)
|
|
49
|
+
|
|
50
|
+
assert result.shape == (
|
|
51
|
+
1000,
|
|
52
|
+
1,
|
|
53
|
+
), f"Expected shape (1000, 1), got {result.shape}"
|
|
54
|
+
assert torch.isfinite(result).all(), "Result contains NaN or Inf"
|
|
55
|
+
|
|
56
|
+
def test_elongate_shape_preservation(self):
|
|
57
|
+
"""Test that ElongateSDF preserves input tensor shape."""
|
|
58
|
+
sphere = SphereSDF(center=[0, 0, 0], radius=0.5)
|
|
59
|
+
elongated = ElongateSDF(sphere, size=[0.3, 0.3, 0.3])
|
|
60
|
+
|
|
61
|
+
for n_points in [1, 10, 100, 1000, 41616]:
|
|
62
|
+
queries = torch.randn(n_points, 3)
|
|
63
|
+
result = elongated(queries)
|
|
64
|
+
assert result.shape == (
|
|
65
|
+
n_points,
|
|
66
|
+
1,
|
|
67
|
+
), f"Shape mismatch for {n_points} points: expected ({n_points}, 1), got {result.shape}"
|
|
68
|
+
|
|
69
|
+
def test_elongate_gradient_flow(self):
|
|
70
|
+
"""Test that gradients flow through ElongateSDF."""
|
|
71
|
+
sphere = SphereSDF(center=[0, 0, 0], radius=0.5)
|
|
72
|
+
elongated = ElongateSDF(sphere, size=[0.3, 0.3, 0.3])
|
|
73
|
+
|
|
74
|
+
queries = torch.randn(100, 3, requires_grad=True)
|
|
75
|
+
result = elongated(queries)
|
|
76
|
+
loss = result.sum()
|
|
77
|
+
loss.backward()
|
|
78
|
+
|
|
79
|
+
assert queries.grad is not None, "Gradients not computed"
|
|
80
|
+
assert torch.isfinite(queries.grad).all(), "Gradients contain NaN or Inf"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class TestWrapperSDFsWithSmoothUnion:
|
|
84
|
+
"""Test various wrapper SDFs with SmoothUnion."""
|
|
85
|
+
|
|
86
|
+
def test_twist_sdf_smooth_union(self):
|
|
87
|
+
"""Test TwistSDF in SmoothUnion."""
|
|
88
|
+
sphere = SphereSDF(center=[0, 0, 0], radius=0.5)
|
|
89
|
+
twisted = TwistSDF(sphere, k=0.5)
|
|
90
|
+
box = RoundedBoxSDF(center=[1.0, 0, 0], extents=[0.3, 0.3, 0.3], radius=0.05)
|
|
91
|
+
|
|
92
|
+
smooth_union = SmoothUnionSDF(twisted, box, k=0.1)
|
|
93
|
+
|
|
94
|
+
queries = torch.randn(500, 3)
|
|
95
|
+
result = smooth_union(queries)
|
|
96
|
+
|
|
97
|
+
assert result.shape == (500, 1), f"Expected shape (500, 1), got {result.shape}"
|
|
98
|
+
assert torch.isfinite(result).all(), "Result contains NaN or Inf"
|
|
99
|
+
|
|
100
|
+
def test_bend_linear_sdf_smooth_union(self):
|
|
101
|
+
"""Test BendLinearSDF in SmoothUnion."""
|
|
102
|
+
sphere = SphereSDF(center=[0, 0, 0], radius=0.3)
|
|
103
|
+
bent = BendLinearSDF(sphere, p0=[0, 0, 0], p1=[0, 0, 1], v=[0.1, 0, 0])
|
|
104
|
+
capsule = CapsuleSDF(point_a=[0.5, 0, 0], point_b=[1.0, 0, 0], radius=0.2)
|
|
105
|
+
|
|
106
|
+
smooth_union = SmoothUnionSDF(bent, capsule, k=0.1)
|
|
107
|
+
|
|
108
|
+
queries = torch.randn(500, 3)
|
|
109
|
+
result = smooth_union(queries)
|
|
110
|
+
|
|
111
|
+
assert result.shape == (500, 1), f"Expected shape (500, 1), got {result.shape}"
|
|
112
|
+
assert torch.isfinite(result).all(), "Result contains NaN or Inf"
|
|
113
|
+
|
|
114
|
+
def test_dilate_sdf_smooth_union(self):
|
|
115
|
+
"""Test DilateSDF in SmoothUnion."""
|
|
116
|
+
sphere = SphereSDF(center=[0, 0, 0], radius=0.3)
|
|
117
|
+
dilated = DilateSDF(sphere, r=0.1)
|
|
118
|
+
box = RoundedBoxSDF(center=[0.8, 0, 0], extents=[0.3, 0.3, 0.3], radius=0.05)
|
|
119
|
+
|
|
120
|
+
smooth_union = SmoothUnionSDF(dilated, box, k=0.1)
|
|
121
|
+
|
|
122
|
+
queries = torch.randn(500, 3)
|
|
123
|
+
result = smooth_union(queries)
|
|
124
|
+
|
|
125
|
+
assert result.shape == (500, 1), f"Expected shape (500, 1), got {result.shape}"
|
|
126
|
+
assert torch.isfinite(result).all(), "Result contains NaN or Inf"
|
|
127
|
+
|
|
128
|
+
def test_mirror_sdf_smooth_union(self):
|
|
129
|
+
"""Test MirrorSDF in SmoothUnion."""
|
|
130
|
+
sphere = SphereSDF(center=[0.5, 0.5, 0], radius=0.3)
|
|
131
|
+
mirrored = MirrorSDF(sphere, plane_point=[0, 0, 0], plane_normal=[1, 0, 0])
|
|
132
|
+
capsule = CapsuleSDF(point_a=[0, 0, 0], point_b=[0, 1, 0], radius=0.2)
|
|
133
|
+
|
|
134
|
+
smooth_union = SmoothUnionSDF(mirrored, capsule, k=0.1)
|
|
135
|
+
|
|
136
|
+
queries = torch.randn(500, 3)
|
|
137
|
+
result = smooth_union(queries)
|
|
138
|
+
|
|
139
|
+
assert result.shape == (500, 1), f"Expected shape (500, 1), got {result.shape}"
|
|
140
|
+
assert torch.isfinite(result).all(), "Result contains NaN or Inf"
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class TestFormula1CarScenario:
|
|
144
|
+
"""Test the specific scenario from the bug report - Formula 1 car chassis."""
|
|
145
|
+
|
|
146
|
+
def test_main_chassis_smooth_union(self):
|
|
147
|
+
"""Test MainChassis configuration from bug report."""
|
|
148
|
+
rounded_box = RoundedBoxSDF(
|
|
149
|
+
center=[0, 0, 0.4], extents=[2.5, 0.8, 0.5], radius=0.15
|
|
150
|
+
)
|
|
151
|
+
elongated = ElongateSDF(rounded_box, size=[2.5, 0.4, 0.3])
|
|
152
|
+
|
|
153
|
+
capsule = CapsuleSDF(point_a=[1.5, 0, 0.3], point_b=[2.5, 0, 0.3], radius=0.25)
|
|
154
|
+
|
|
155
|
+
rounded_box2 = RoundedBoxSDF(
|
|
156
|
+
center=[-1.5, 0, 0.5], extents=[1.5, 0.7, 0.4], radius=0.1
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
smooth_union = SmoothUnionSDF(elongated, capsule, rounded_box2, k=0.1)
|
|
160
|
+
|
|
161
|
+
queries = torch.randn(41616, 3)
|
|
162
|
+
result = smooth_union(queries)
|
|
163
|
+
|
|
164
|
+
assert result.shape == (
|
|
165
|
+
41616,
|
|
166
|
+
1,
|
|
167
|
+
), f"Expected shape (41616, 1), got {result.shape}"
|
|
168
|
+
assert torch.isfinite(result).all(), "Result contains NaN or Inf"
|
|
169
|
+
|
|
170
|
+
def test_side_pod_smooth_union(self):
|
|
171
|
+
"""Test SidePod configuration from bug report."""
|
|
172
|
+
rounded_box = RoundedBoxSDF(
|
|
173
|
+
center=[-0.5, 0.55, 0.35], extents=[1.2, 0.35, 0.35], radius=0.12
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
from DeepSDFStruct.sdf_primitives import CappedConeSDF
|
|
177
|
+
|
|
178
|
+
capped_cone = CappedConeSDF(
|
|
179
|
+
point_a=[0.5, 0.55, 0.35], point_b=[-1.2, 0.55, 0.35], ra=0.25, rb=0.15
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
smooth_union = SmoothUnionSDF(rounded_box, capped_cone, k=0.15)
|
|
183
|
+
|
|
184
|
+
queries = torch.randn(1000, 3)
|
|
185
|
+
result = smooth_union(queries)
|
|
186
|
+
|
|
187
|
+
assert result.shape == (
|
|
188
|
+
1000,
|
|
189
|
+
1,
|
|
190
|
+
), f"Expected shape (1000, 1), got {result.shape}"
|
|
191
|
+
assert torch.isfinite(result).all(), "Result contains NaN or Inf"
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
if __name__ == "__main__":
|
|
195
|
+
pytest.main([__file__, "-v"])
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
from DeepSDFStruct.sdf_primitives import CircleSDF, RectangleSDF
|
|
2
|
+
from DeepSDFStruct.flexisquares.flexisquares import FlexiSquares
|
|
3
|
+
from DeepSDFStruct.torch_spline import TorchSpline
|
|
4
|
+
from DeepSDFStruct.pretrained_models import get_model, PretrainedModels
|
|
5
|
+
from DeepSDFStruct.SDF import (
|
|
6
|
+
SDFfromDeepSDF,
|
|
7
|
+
CappedBorderSDF,
|
|
8
|
+
SDFfromLineMesh,
|
|
9
|
+
DifferenceSDF,
|
|
10
|
+
)
|
|
11
|
+
from DeepSDFStruct.lattice_structure import LatticeSDFStruct
|
|
12
|
+
from DeepSDFStruct.parametrization import Constant
|
|
13
|
+
from DeepSDFStruct.mesh import create_2D_mesh
|
|
14
|
+
import numpy as np
|
|
15
|
+
import gustaf as gus
|
|
16
|
+
import splinepy
|
|
17
|
+
import torch
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_flexisquares_linemesh_diag_and_straight():
|
|
21
|
+
linemesh = gus.Edges(
|
|
22
|
+
vertices=np.array([[-1, -1], [1, 1], [1, -1]]), edges=np.array([[0, 1], [1, 2]])
|
|
23
|
+
)
|
|
24
|
+
thickness = 0.4
|
|
25
|
+
rectangle = RectangleSDF(center=[0, 0], extents=[3.9, 3.9])
|
|
26
|
+
sdf_linemesh = SDFfromLineMesh(linemesh, thickness=thickness)
|
|
27
|
+
sdf = DifferenceSDF(rectangle, sdf_linemesh)
|
|
28
|
+
bounds = torch.tensor([[-2.0, -2.0], [2.0, 2.0]])
|
|
29
|
+
mesh, _ = create_2D_mesh(sdf, 10, mesh_type="surface", bounds=bounds)
|
|
30
|
+
mesh.export("linemesh.vtk")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_flexisquares_linemesh_diag1():
|
|
34
|
+
linemesh = gus.Edges(
|
|
35
|
+
vertices=np.array([[-2, -2], [2, 2]]), edges=np.array([[0, 1]])
|
|
36
|
+
)
|
|
37
|
+
thickness = 0.4
|
|
38
|
+
rectangle = RectangleSDF(center=[0, 0], extents=[3.9, 3.9])
|
|
39
|
+
sdf_linemesh = SDFfromLineMesh(linemesh, thickness=thickness)
|
|
40
|
+
sdf = DifferenceSDF(rectangle, sdf_linemesh)
|
|
41
|
+
bounds = torch.tensor([[-2.0, -2.0], [2.0, 2.0]])
|
|
42
|
+
mesh, _ = create_2D_mesh(sdf, 2, mesh_type="surface", bounds=bounds)
|
|
43
|
+
torch.testing.assert_close(
|
|
44
|
+
mesh.vertices,
|
|
45
|
+
torch.tensor(
|
|
46
|
+
[
|
|
47
|
+
[-1.2913, 0.1414],
|
|
48
|
+
[-1.2913, 1.2913],
|
|
49
|
+
[0.1414, -1.2913],
|
|
50
|
+
[-0.5253, -0.5253],
|
|
51
|
+
[0.5253, 0.5253],
|
|
52
|
+
[-0.1414, 1.2913],
|
|
53
|
+
[1.2913, -1.2913],
|
|
54
|
+
[1.2913, -0.1414],
|
|
55
|
+
[-0.5486, 0.5486],
|
|
56
|
+
[0.5486, -0.5486],
|
|
57
|
+
]
|
|
58
|
+
),
|
|
59
|
+
rtol=1e-4,
|
|
60
|
+
atol=1e-4,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_flexisquares_linemesh_diag2():
|
|
65
|
+
linemesh = gus.Edges(
|
|
66
|
+
vertices=np.array([[-2, 2], [2, -2]]), edges=np.array([[0, 1]])
|
|
67
|
+
)
|
|
68
|
+
thickness = 0.4
|
|
69
|
+
rectangle = RectangleSDF(center=[0, 0], extents=[3.9, 3.9])
|
|
70
|
+
sdf_linemesh = SDFfromLineMesh(linemesh, thickness=thickness)
|
|
71
|
+
sdf = DifferenceSDF(rectangle, sdf_linemesh)
|
|
72
|
+
bounds = torch.tensor([[-2.0, -2.0], [2.0, 2.0]])
|
|
73
|
+
mesh, _ = create_2D_mesh(sdf, 2, mesh_type="surface", bounds=bounds)
|
|
74
|
+
torch.testing.assert_close(
|
|
75
|
+
mesh.vertices,
|
|
76
|
+
torch.tensor(
|
|
77
|
+
[
|
|
78
|
+
[-1.2913, -1.2913],
|
|
79
|
+
[-1.2913, -0.1414],
|
|
80
|
+
[-0.1414, -1.2913],
|
|
81
|
+
[-0.5253, 0.5253],
|
|
82
|
+
[0.5253, -0.5253],
|
|
83
|
+
[0.1414, 1.2913],
|
|
84
|
+
[1.2913, 0.1414],
|
|
85
|
+
[1.2913, 1.2913],
|
|
86
|
+
[-0.5486, -0.5486],
|
|
87
|
+
[0.5486, 0.5486],
|
|
88
|
+
]
|
|
89
|
+
),
|
|
90
|
+
rtol=1e-4,
|
|
91
|
+
atol=1e-4,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_flexisquares_simple():
|
|
96
|
+
circle_sdf = CircleSDF(center=(0.0, 0.0), radius=0.5)
|
|
97
|
+
bounds = torch.tensor([[-1, -1], [1, 1]])
|
|
98
|
+
fsq = FlexiSquares(device=bounds.device)
|
|
99
|
+
verts, square_idx = fsq.construct_voxel_grid([10, 10], bounds=bounds)
|
|
100
|
+
# this function is useful to plot the squares (voxels)
|
|
101
|
+
# for square in square_idx:
|
|
102
|
+
# indices = square.index_select(0, torch.tensor([0, 1, 3, 2, 0]))
|
|
103
|
+
# ax.plot(verts[indices][:, 0], verts[indices][:, 1], color="black", lw=0.3)
|
|
104
|
+
scalar_field = circle_sdf(verts)
|
|
105
|
+
vd_surf, edges, L_dev = fsq(verts, scalar_field, square_idx)
|
|
106
|
+
vd_surf_and_interior, faces, L_dev = fsq(
|
|
107
|
+
verts, scalar_field, square_idx, output_tetmesh=True
|
|
108
|
+
)
|
|
109
|
+
# check that the first vertices are the boundary vertices:
|
|
110
|
+
assert (
|
|
111
|
+
vd_surf.shape[0] == 20
|
|
112
|
+
), f"Expected 20 surface vertices, got {vd_surf.shape[0]}"
|
|
113
|
+
torch.testing.assert_close(vd_surf, vd_surf_and_interior[:20])
|
|
114
|
+
assert (
|
|
115
|
+
circle_sdf(vd_surf).abs() < 0.05
|
|
116
|
+
).all(), "Signed Distance Function not close to zero at border vertices"
|
|
117
|
+
assert (circle_sdf(vd_surf) < 0.05).all(), "Some interior nodes are positive"
|
|
118
|
+
mesh, _ = create_2D_mesh(circle_sdf, 40, mesh_type="surface", bounds=bounds)
|
|
119
|
+
try:
|
|
120
|
+
mesh, _ = create_2D_mesh(circle_sdf, 40, mesh_type="surface")
|
|
121
|
+
raise ValueError(
|
|
122
|
+
"This test should raise an error, because the bounds are not correct"
|
|
123
|
+
)
|
|
124
|
+
except RuntimeError as rError:
|
|
125
|
+
print(f"The test correctly raised the runtimeerror {rError} ✅️")
|
|
126
|
+
mesh.export("tests/tmp_outputs/circle_mesh.vtk")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_flexisquares_lattice_struct():
|
|
130
|
+
model = get_model(PretrainedModels.AnalyticRoundCross)
|
|
131
|
+
sdf = SDFfromDeepSDF(model)
|
|
132
|
+
|
|
133
|
+
# Define a spline-based deformation field
|
|
134
|
+
deformation_spline = TorchSpline(
|
|
135
|
+
splinepy.helpme.create.box(2, 1).bspline, device=model.device
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Create the lattice structure with deformation and microtile
|
|
139
|
+
lattice_struct = CappedBorderSDF(
|
|
140
|
+
LatticeSDFStruct(
|
|
141
|
+
tiling=(6, 3),
|
|
142
|
+
microtile=sdf.to2D(axes=[0, 1], offset=0.5),
|
|
143
|
+
parametrization=Constant([0.5], device=model.device),
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
bounds = torch.tensor([[-0.05, -0.05], [1.05, 1.05]], device=model.device)
|
|
147
|
+
fsq = FlexiSquares(device=model.device)
|
|
148
|
+
verts, square_idx = fsq.construct_voxel_grid([60, 30], bounds=bounds)
|
|
149
|
+
scalar_field = lattice_struct(verts) + 0.01
|
|
150
|
+
|
|
151
|
+
vd_surf, edges, L_dev = fsq(verts, scalar_field, square_idx)
|
|
152
|
+
|
|
153
|
+
vd_surf_and_interior, faces, L_dev = fsq(
|
|
154
|
+
verts, scalar_field, square_idx, output_tetmesh=True
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
assert (lattice_struct(vd_surf) < 0.1).all(), "Some interior nodes are positive"
|
|
158
|
+
|
|
159
|
+
# check if deformation spline works on extracted mesh
|
|
160
|
+
vd = deformation_spline(vd_surf_and_interior)
|
|
161
|
+
vd_surf_and_interior_manual = vd_surf_and_interior
|
|
162
|
+
vd_surf_and_interior_manual[:, 0] = vd_surf_and_interior[:, 0] * 2
|
|
163
|
+
torch.testing.assert_close(vd, vd_surf_and_interior_manual)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def test_near_zero_epsilon():
|
|
167
|
+
"""
|
|
168
|
+
Test edge case where scalar values are exactly 0 or very close to it.
|
|
169
|
+
This often causes division by zero in interpolation if not handled.
|
|
170
|
+
"""
|
|
171
|
+
fsq = FlexiSquares(device="cpu")
|
|
172
|
+
verts = torch.tensor([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]])
|
|
173
|
+
square_idx = torch.tensor([[0, 1, 2, 3]])
|
|
174
|
+
|
|
175
|
+
# Exactly zero on two corners
|
|
176
|
+
scalar_field = torch.tensor([0.0, 0.0, 1.0, 1.0])
|
|
177
|
+
|
|
178
|
+
vd, edges, _ = fsq(verts, scalar_field, square_idx)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
if __name__ == "__main__":
|
|
182
|
+
test_flexisquares_linemesh_diag_and_straight()
|
|
183
|
+
test_flexisquares_linemesh_diag1()
|
|
184
|
+
test_flexisquares_linemesh_diag2()
|
|
185
|
+
test_flexisquares_simple()
|
|
186
|
+
test_near_zero_epsilon()
|
|
187
|
+
test_flexisquares_lattice_struct()
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from DeepSDFStruct.sampling import SDFSampler, augment_by_FFD
|
|
3
|
+
from DeepSDFStruct.splinepy_unitcells.chi_3D import Chi3D
|
|
4
|
+
from DeepSDFStruct.splinepy_unitcells.cross_lattice import CrossLattice
|
|
5
|
+
import splinepy
|
|
6
|
+
import multiprocessing as mp
|
|
7
|
+
import pytest
|
|
8
|
+
import pathlib
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture(scope="session", autouse=True)
|
|
12
|
+
def set_mp_start_method():
|
|
13
|
+
mp.set_start_method("spawn", force=True)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_generate_dataset_from_obj_files():
|
|
17
|
+
outdir = "./training_data"
|
|
18
|
+
splitdir = "./training_data/splits"
|
|
19
|
+
dataset_name = "furniture"
|
|
20
|
+
|
|
21
|
+
sdf_sampler = SDFSampler(outdir, splitdir, dataset_name, overwrite_existing=True)
|
|
22
|
+
meshs = sdf_sampler.get_meshs_from_folder(
|
|
23
|
+
foldername="./tests/data/chairs", mesh_type="obj"
|
|
24
|
+
)
|
|
25
|
+
meshs_augment = augment_by_FFD(meshs)
|
|
26
|
+
sdf_sampler.add_class(meshs_augment, class_name="chairs")
|
|
27
|
+
|
|
28
|
+
sdf_sampler.process_geometries(
|
|
29
|
+
sampling_strategy="uniform",
|
|
30
|
+
add_surface_samples=True,
|
|
31
|
+
also_save_vtk=True,
|
|
32
|
+
n_samples=10,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
sdf_sampler.write_json("chairs_train.json")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.mark.parametrize("n_workers", [0, 1])
|
|
39
|
+
def test_generate_dataset(n_workers, tmp_path):
|
|
40
|
+
outdir = tmp_path / "training_data"
|
|
41
|
+
splitdir = tmp_path / "training_data/splits"
|
|
42
|
+
dataset_name = "microstructure"
|
|
43
|
+
|
|
44
|
+
sdf_sampler = SDFSampler(outdir, splitdir, dataset_name, overwrite_existing=True)
|
|
45
|
+
|
|
46
|
+
t_start = 0.1 * np.sqrt(2) / 2
|
|
47
|
+
t_end = 0.15 * np.sqrt(2) / 2
|
|
48
|
+
crosslattice_tiles = []
|
|
49
|
+
for t in np.linspace(t_start, t_end, 3):
|
|
50
|
+
tile, _ = CrossLattice().create_tile(np.array([[t]]), make3D=True)
|
|
51
|
+
crosslattice_tiles.append(splinepy.Multipatch(tile))
|
|
52
|
+
|
|
53
|
+
chi = Chi3D()
|
|
54
|
+
chi_tiles = []
|
|
55
|
+
|
|
56
|
+
for phi in np.linspace(0, -np.pi / 6, 2):
|
|
57
|
+
for x2 in np.linspace(-0.1, 0.2, 2):
|
|
58
|
+
t = 0.1
|
|
59
|
+
x1 = 0.2
|
|
60
|
+
r = 0.5 * t
|
|
61
|
+
tile, _ = chi.create_tile(np.array([[phi, t, x1, x2, r]] * 5))
|
|
62
|
+
chi_tiles.append(splinepy.Multipatch(tile))
|
|
63
|
+
|
|
64
|
+
sdf_sampler.add_class(chi_tiles, class_name="Chi3D_center", n_faces=3)
|
|
65
|
+
sdf_sampler.add_class(crosslattice_tiles, class_name="CrossLattice", n_faces=3)
|
|
66
|
+
|
|
67
|
+
sdf_sampler.process_geometries(
|
|
68
|
+
sampling_strategy="uniform", n_workers=n_workers, n_samples=10
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
sdf_sampler.write_json("chi_and_cross.json")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
if __name__ == "__main__":
|
|
75
|
+
test_generate_dataset_from_obj_files()
|
|
76
|
+
test_generate_dataset(n_workers=1, tmp_path=pathlib.Path("."))
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from DeepSDFStruct.local_shapes import LocalShapesSDF
|
|
2
|
+
from DeepSDFStruct.pretrained_models import get_model, PretrainedModels
|
|
3
|
+
from DeepSDFStruct.SDF import SDFfromDeepSDF, _cap_outside_of_unitcube
|
|
4
|
+
from DeepSDFStruct.lattice_structure import LatticeSDFStruct
|
|
5
|
+
from DeepSDFStruct.parametrization import Constant
|
|
6
|
+
from DeepSDFStruct.torch_spline import TorchSpline
|
|
7
|
+
import splinepy
|
|
8
|
+
import torch
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_deepsdf_evaluation():
|
|
13
|
+
# Load a pretrained DeepSDF model
|
|
14
|
+
model = get_model(PretrainedModels.ChiAndCross)
|
|
15
|
+
sdf = SDFfromDeepSDF(model)
|
|
16
|
+
|
|
17
|
+
# Set the latent vector and visualize a slice of the SDF
|
|
18
|
+
sdf.set_latent_vec(
|
|
19
|
+
torch.tensor([0.3, 0.3], dtype=torch.float32, device=model.device)
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
out = sdf(
|
|
23
|
+
torch.tensor(
|
|
24
|
+
[[0, 0, 0], [0, 1, 0], [0.5, 0.5, 0.5]],
|
|
25
|
+
dtype=torch.float32,
|
|
26
|
+
device=model.device,
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
print(out)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_deepsdf_lattice_evaluation():
|
|
33
|
+
# Load a pretrained DeepSDF model
|
|
34
|
+
model = get_model(PretrainedModels.AnalyticRoundCross)
|
|
35
|
+
sdf = SDFfromDeepSDF(model)
|
|
36
|
+
|
|
37
|
+
# Define a spline-based deformation field
|
|
38
|
+
deformation_spline = TorchSpline(
|
|
39
|
+
splinepy.helpme.create.box(1, 1, 1).bspline, device=model.device
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Create the lattice structure with deformation and microtile
|
|
43
|
+
lattice_struct = LatticeSDFStruct(
|
|
44
|
+
tiling=(1, 1, 1),
|
|
45
|
+
microtile=sdf,
|
|
46
|
+
parametrization=Constant([0.5], device=model.device),
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
out = lattice_struct(
|
|
50
|
+
torch.tensor(
|
|
51
|
+
[[0, 0, 0], [0, 1, 0], [0.5, 0.5, 0.5]],
|
|
52
|
+
dtype=torch.float32,
|
|
53
|
+
device=model.device,
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
fig, axs = plt.subplots(1, 3)
|
|
57
|
+
lattice_struct.plot_slice(
|
|
58
|
+
origin=(0, 0.5, 0),
|
|
59
|
+
normal=(0, 1, 0),
|
|
60
|
+
ax=axs[0],
|
|
61
|
+
show_zero_level=True,
|
|
62
|
+
res=(300, 300),
|
|
63
|
+
deformation_function=deformation_spline,
|
|
64
|
+
)
|
|
65
|
+
print(out)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_lattice_local_shapes_comparison():
|
|
69
|
+
# Load a pretrained DeepSDF model
|
|
70
|
+
model = get_model(PretrainedModels.AnalyticRoundCross)
|
|
71
|
+
sdf = SDFfromDeepSDF(model)
|
|
72
|
+
|
|
73
|
+
# Create the lattice structure with deformation and microtile
|
|
74
|
+
lattice_struct = LatticeSDFStruct(
|
|
75
|
+
tiling=(2, 1, 1),
|
|
76
|
+
microtile=sdf,
|
|
77
|
+
parametrization=Constant([0.5], device=model.device),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
test_input_param_domain = torch.tensor(
|
|
81
|
+
[[0, 0, 0], [0, 1, 0], [0.5, 0.5, 0.5]],
|
|
82
|
+
dtype=torch.float32,
|
|
83
|
+
device=model.device,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
out_latt = lattice_struct(test_input_param_domain)
|
|
87
|
+
|
|
88
|
+
local_shape_sdf = LocalShapesSDF(
|
|
89
|
+
tiling=(2, 1, 1),
|
|
90
|
+
unit_cell=sdf,
|
|
91
|
+
parametrization=Constant([0.5], device=model.device),
|
|
92
|
+
bounds=torch.tensor([[0, 0, 0], [2, 1, 1]]),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
out_ls = local_shape_sdf(test_input_param_domain)
|
|
96
|
+
|
|
97
|
+
torch.testing.assert_close(out_latt, out_ls)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_cap_outside_unitcube():
|
|
101
|
+
|
|
102
|
+
samples = torch.tensor(
|
|
103
|
+
[
|
|
104
|
+
[1.1, 0.0, 0.0],
|
|
105
|
+
[-0.1, 0.0, 0.0],
|
|
106
|
+
[0.0, 1.1, 0.0],
|
|
107
|
+
[0.0, -0.1, 0.0],
|
|
108
|
+
[0.0, 0.0, 1.1],
|
|
109
|
+
[0.0, 0.0, -0.1],
|
|
110
|
+
[0.1, 0.1, 0.1],
|
|
111
|
+
[0.9, 0.9, 0.9],
|
|
112
|
+
[0.0, 0.0, 0.0],
|
|
113
|
+
],
|
|
114
|
+
dtype=torch.float32,
|
|
115
|
+
)
|
|
116
|
+
# true = inside, false = outside
|
|
117
|
+
labels = [False, False, False, False, False, False, True, True]
|
|
118
|
+
|
|
119
|
+
# Initial SDF values: all negative (e.g., "inside")
|
|
120
|
+
sdf_values = torch.tensor([[-0.5]] * samples.shape[0], dtype=torch.float32)
|
|
121
|
+
|
|
122
|
+
capped_sdf = _cap_outside_of_unitcube(samples, sdf_values.clone())
|
|
123
|
+
|
|
124
|
+
# Check that the SDF is capped (increased) for points outside the cube
|
|
125
|
+
for sample, sdf_value, label in zip(samples, capped_sdf, labels):
|
|
126
|
+
if sdf_value < 1e-10:
|
|
127
|
+
continue
|
|
128
|
+
assertion = (sdf_value < 0) == label
|
|
129
|
+
assert assertion, f"Incorrect capping on valid sample {sample}"
|
|
130
|
+
assert capped_sdf[-1] < 1e10, "Point on the boundary is wrong"
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
if __name__ == "__main__":
|
|
134
|
+
test_deepsdf_evaluation()
|
|
135
|
+
test_lattice_local_shapes_comparison()
|
|
136
|
+
test_cap_outside_unitcube()
|
|
137
|
+
test_deepsdf_lattice_evaluation()
|