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,622 @@
1
+ """
2
+ SDF Primitive and Operation Showcase
3
+ ====================================
4
+
5
+ This script generates visual examples of all SDF primitives and operations
6
+ implemented in DeepSDFStruct, creating screenshots suitable for documentation.
7
+
8
+ The workflow:
9
+ 1. Create an SDF (primitive or operation)
10
+ 2. Generate a 3D mesh using create_3D_mesh()
11
+ 3. Convert to gustaf format with .to_gus()
12
+ 4. Create screenshots using vedo
13
+ 5. Save to organized directory structure
14
+
15
+ Usage:
16
+ uv run python benchmarks/generate_sdf_showcase.py
17
+
18
+ Output:
19
+ benchmarks/sdf_showcase/
20
+ ├── primitives/
21
+ │ ├── 3D/
22
+ │ │ ├── sphere.png
23
+ │ │ ├── box.png
24
+ │ │ ├── ...
25
+ │ └── 2D/
26
+ │ ├── circle.png
27
+ │ ├── ...
28
+ └── operations/
29
+ ├── boolean/
30
+ └── transformations/
31
+ """
32
+
33
+ import sys
34
+ import os
35
+ import pathlib
36
+ import numpy as np
37
+ import torch
38
+ import vedo
39
+ import matplotlib.pyplot as plt
40
+ from typing import Optional, Tuple
41
+
42
+ # Add parent directory to import DeepSDFStruct
43
+ sys.path.insert(0, str(pathlib.Path(__file__).parent.parent))
44
+
45
+ from DeepSDFStruct.mesh import create_3D_mesh, create_2D_mesh
46
+ from DeepSDFStruct.sdf_primitives import (
47
+ # 3D Primitives
48
+ SphereSDF,
49
+ BoxSDF,
50
+ CylinderSDF,
51
+ ConeSDF,
52
+ TorusSDF,
53
+ PlaneSDF,
54
+ RoundedBoxSDF,
55
+ WireframeBoxSDF,
56
+ CapsuleSDF,
57
+ EllipsoidSDF,
58
+ PyramidSDF,
59
+ CornerSpheresSDF,
60
+ CrossMsSDF,
61
+ RoundedCylinderSDF,
62
+ CappedConeSDF,
63
+ RoundedConeSDF,
64
+ TetrahedronSDF,
65
+ OctahedronSDF,
66
+ DodecahedronSDF,
67
+ IcosahedronSDF,
68
+ # 2D Primitives
69
+ CircleSDF,
70
+ RectangleSDF,
71
+ LineSDF,
72
+ RoundedRectangleSDF,
73
+ EquilateralTriangleSDF,
74
+ HexagonSDF,
75
+ PolygonSDF,
76
+ )
77
+ from DeepSDFStruct.sdf_operations import (
78
+ ElongateSDF,
79
+ TwistSDF,
80
+ BendLinearSDF,
81
+ BendRadialSDF,
82
+ DilateSDF,
83
+ ErodeSDF,
84
+ ShellSDF,
85
+ RepeatSDF,
86
+ MirrorSDF,
87
+ CircularArraySDF,
88
+ RevolveSDF,
89
+ )
90
+ from DeepSDFStruct.SDF import UnionSDF, DifferenceSDF
91
+
92
+ # ==================== Configuration ====================
93
+
94
+ # Output directory structure
95
+ OUTPUT_DIR = pathlib.Path("benchmarks/sdf_showcase")
96
+ OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
97
+
98
+ # Camera settings for consistent screenshots
99
+ CAMERA_SETTINGS = dict(
100
+ position=(2.5, 2.5, 2.5), focal_point=(0, 0, 0), viewup=(0, 0, 1)
101
+ )
102
+
103
+
104
+ # ==================== Helper Functions ====================
105
+
106
+
107
+ def create_screenshot(
108
+ sdf,
109
+ filename: str,
110
+ resolution: int = 64,
111
+ title: str = "",
112
+ background_color: Tuple[float, float, float] = (1.0, 1.0, 1.0),
113
+ mesh_color: str = "#6B9BD2",
114
+ ) -> bool:
115
+ """
116
+ Generate a screenshot from an SDF.
117
+
118
+ Parameters
119
+ ----------
120
+ sdf : SDFBase
121
+ The SDF to visualize
122
+ filename : str
123
+ Output filename (will be saved as .png)
124
+ resolution : int, optional
125
+ Mesh resolution (default: 64)
126
+ title : str, optional
127
+ Title for the visualization (shown as 2D text overlay)
128
+ background_color : tuple, optional
129
+ RGB background color (default: white)
130
+ mesh_color : str, optional
131
+ Hex color for the mesh (default: nice blue)
132
+
133
+ Returns
134
+ -------
135
+ bool
136
+ True if successful, False otherwise
137
+ """
138
+ try:
139
+ # Create 3D mesh from SDF
140
+ mesh, _ = create_3D_mesh(
141
+ sdf, N_base=resolution, mesh_type="surface", device="cpu"
142
+ )
143
+
144
+ # Convert to gustaf format
145
+ gus_mesh = mesh.to_gus()
146
+
147
+ # Create vedo mesh
148
+ vedo_mesh = vedo.Mesh(
149
+ [gus_mesh.vertices.astype(np.float64), gus_mesh.faces.astype(np.int64)]
150
+ )
151
+ vedo_mesh.color(mesh_color).lighting("glossy")
152
+
153
+ # Setup plotter
154
+ plt = vedo.Plotter(interactive=False)
155
+ plt.offscreen = True # Don't open window
156
+ plt.background_color = background_color
157
+
158
+ # Create the scene
159
+ plt.show(vedo_mesh, camera=vedo.camera_from_dict(CAMERA_SETTINGS))
160
+
161
+ # Add title if provided
162
+ if title:
163
+ title_text = vedo.Text2D(
164
+ title, pos="top-left", font="Arial", s=0.05, c="black"
165
+ )
166
+ plt.show(title_text)
167
+
168
+ # Save screenshot
169
+ plt.screenshot(filename, scale=2.0) # Higher resolution
170
+ plt.close()
171
+
172
+ print(f" ✓ Created: {filename}")
173
+ return True
174
+
175
+ except Exception as e:
176
+ print(f" ✗ Failed: {filename} - {e}")
177
+ return False
178
+
179
+
180
+ def create_2d_screenshot(
181
+ sdf,
182
+ filename: str,
183
+ resolution: int = 200,
184
+ title: str = "",
185
+ bounds: Optional[Tuple[float, float, float, float]] = None,
186
+ query_offset: Optional[Tuple[float, float]] = None,
187
+ interior_color: str = "#6B9BD2",
188
+ exterior_color: str = "#FFFFFF",
189
+ ) -> bool:
190
+ """
191
+ Generate a screenshot from a 2D SDF using matplotlib contour.
192
+
193
+ Parameters
194
+ ----------
195
+ sdf : SDFBase (2D)
196
+ The 2D SDF to visualize
197
+ filename : str
198
+ Output filename (will be saved as .png)
199
+ resolution : int, optional
200
+ Grid resolution for sampling (default: 200)
201
+ title : str, optional
202
+ Title for the visualization
203
+ bounds : tuple, optional
204
+ Plot bounds (xmin, xmax, ymin, ymax). If None, uses SDF domain bounds.
205
+ query_offset : tuple, optional
206
+ Offset applied to sampling coordinates before SDF evaluation.
207
+ interior_color : str, optional
208
+ Hex color for interior region (default: nice blue)
209
+ exterior_color : str, optional
210
+ Hex color for exterior region (default: white)
211
+
212
+ Returns
213
+ -------
214
+ bool
215
+ True if successful, False otherwise
216
+ """
217
+ try:
218
+ # Get bounds if not provided
219
+ if bounds is None:
220
+ sdf_bounds = sdf._get_domain_bounds()
221
+ bounds = (
222
+ sdf_bounds[0, 0].item(),
223
+ sdf_bounds[1, 0].item(),
224
+ sdf_bounds[0, 1].item(),
225
+ sdf_bounds[1, 1].item(),
226
+ )
227
+
228
+ # Create grid for sampling
229
+ x = np.linspace(bounds[0], bounds[1], resolution)
230
+ y = np.linspace(bounds[2], bounds[3], resolution)
231
+ X, Y = np.meshgrid(x, y)
232
+
233
+ # Sample SDF values
234
+ points = np.stack([X.ravel(), Y.ravel()], axis=1)
235
+ if query_offset is not None:
236
+ points_for_eval = points - np.asarray(query_offset, dtype=np.float32)
237
+ else:
238
+ points_for_eval = points
239
+ sdf_values = (
240
+ sdf(torch.tensor(points_for_eval, dtype=torch.float32))
241
+ .detach()
242
+ .numpy()
243
+ .reshape(X.shape)
244
+ )
245
+
246
+ # Create plot
247
+ figsize = (6, 6) if not title else (6, 7)
248
+ if title:
249
+ return_list = [1]
250
+ else:
251
+ return_list = []
252
+
253
+ fig, axes = plt.subplots(figsize=figsize)
254
+
255
+ # Create filled contour plot (interior vs exterior)
256
+ # SDF < 0 is interior, SDF > 0 is exterior
257
+ contour = axes.contourf(
258
+ X,
259
+ Y,
260
+ sdf_values,
261
+ levels=[-100, 0, 100],
262
+ colors=[interior_color, exterior_color],
263
+ )
264
+
265
+ # Add contour line at SDF = 0 (boundary)
266
+ axes.contour(X, Y, sdf_values, levels=[0], colors="black", linewidths=2)
267
+
268
+ # Set plot properties
269
+ axes.set_aspect("equal")
270
+ axes.set_xlim(bounds[0], bounds[1])
271
+ axes.set_ylim(bounds[2], bounds[3])
272
+ axes.set_xlabel("X")
273
+ axes.set_ylabel("Y")
274
+ if title:
275
+ axes.set_title(title)
276
+
277
+ # Remove ticks for cleaner look
278
+ axes.set_xticks([])
279
+ axes.set_yticks([])
280
+
281
+ # Save figure
282
+ plt.tight_layout()
283
+ plt.savefig(filename, dpi=150, bbox_inches="tight")
284
+ plt.close(fig)
285
+
286
+ print(f" ✓ Created: {filename}")
287
+ return True
288
+
289
+ except Exception as e:
290
+ print(f" ✗ Failed: {filename} - {e}")
291
+ import traceback
292
+
293
+ traceback.print_exc()
294
+ return False
295
+
296
+
297
+ # ==================== SDF Definitions ====================
298
+
299
+
300
+ def create_sdf_primitives():
301
+ """Create all SDF primitives and generate screenshots."""
302
+
303
+ # ======== 3D Primitives ========
304
+ output_dir = OUTPUT_DIR / "primitives" / "3D"
305
+ output_dir.mkdir(parents=True, exist_ok=True)
306
+
307
+ print("\n=== 3D Primitives ===")
308
+
309
+ # Sphere
310
+ print("Sphere")
311
+ sphere = SphereSDF(center=[0, 0, 0], radius=1.0)
312
+ create_screenshot(sphere, output_dir / "sphere.png", resolution=64)
313
+
314
+ # Box
315
+ print("Box")
316
+ box = BoxSDF(center=[0, 0, 0], extents=[1.5, 1.0, 0.8])
317
+ create_screenshot(box, output_dir / "box.png", resolution=64)
318
+
319
+ # Cone
320
+
321
+ # Cone
322
+ print("Cone")
323
+ cone = ConeSDF(apexpoint=[0, 0, -0.5], axis=[0, 0, 1], radius=0.4, height=1.0)
324
+ create_screenshot(cone, output_dir / "cone.png", resolution=64)
325
+
326
+ # Torus
327
+ print("Torus")
328
+ torus = TorusSDF(
329
+ center=[0, 0, 0], axis=[0, 0, 1], major_radius=0.5, minor_radius=0.15
330
+ )
331
+ create_screenshot(torus, output_dir / "torus.png", resolution=64)
332
+
333
+ # Rounded Box
334
+ print("Rounded Box")
335
+ rounded_box = RoundedBoxSDF(center=[0, 0, 0], extents=[1.0, 1.0, 1.0], radius=0.15)
336
+ create_screenshot(rounded_box, output_dir / "rounded_box.png", resolution=64)
337
+
338
+ # Wireframe Box
339
+ print("Wireframe Box")
340
+ wireframe_box = WireframeBoxSDF(
341
+ center=[0, 0, 0], extents=[1.0, 1.0, 1.0], thickness=0.08
342
+ )
343
+ create_screenshot(wireframe_box, output_dir / "wireframe_box.png", resolution=64)
344
+
345
+ # Capsule
346
+ print("Capsule")
347
+ capsule = CapsuleSDF(point_a=[0, 0, -0.5], point_b=[0, 0, 0.5], radius=0.25)
348
+ create_screenshot(capsule, output_dir / "capsule.png", resolution=64)
349
+
350
+ # Ellipsoid
351
+ print("Ellipsoid")
352
+ ellipsoid = EllipsoidSDF(center=[0, 0, 0], extents=[0.6, 0.8, 0.5])
353
+ create_screenshot(ellipsoid, output_dir / "ellipsoid.png", resolution=64)
354
+
355
+ # Pyramid
356
+ print("Pyramid")
357
+ pyramid = PyramidSDF(height=1.0)
358
+ create_screenshot(pyramid, output_dir / "pyramid.png", resolution=64)
359
+
360
+ # Corner Spheres
361
+ print("Corner Spheres")
362
+ corner_spheres = CornerSpheresSDF(radius=0.15, limit=0.8)
363
+ create_screenshot(corner_spheres, output_dir / "corner_spheres.png", resolution=64)
364
+
365
+ # Cross M
366
+ print("Cross M")
367
+ cross_ms = CrossMsSDF(radius=0.15)
368
+ create_screenshot(cross_ms, output_dir / "cross_ms.png", resolution=64)
369
+
370
+ # Cylinder
371
+ print("Cylinder")
372
+ cylinder = CylinderSDF(point_a=[0, 0, -0.5], point_b=[0, 0, 0.5], radius=0.3)
373
+ create_screenshot(cylinder, output_dir / "cylinder.png", resolution=64)
374
+
375
+ # Rounded Cylinder
376
+ print("Rounded Cylinder")
377
+ rounded_cyl = RoundedCylinderSDF(ra=0.3, rb=0.1, h=1.0)
378
+ create_screenshot(rounded_cyl, output_dir / "rounded_cylinder.png", resolution=64)
379
+
380
+ # Capped Cone
381
+ print("Capped Cone")
382
+ capped_cone = CappedConeSDF(
383
+ point_a=[0, 0, -0.5], point_b=[0, 0, 0.5], ra=0.1, rb=0.3
384
+ )
385
+ create_screenshot(capped_cone, output_dir / "capped_cone.png", resolution=64)
386
+
387
+ # Rounded Cone
388
+ print("Rounded Cone")
389
+ rounded_cone = RoundedConeSDF(r1=0.1, r2=0.3, h=1.0)
390
+ create_screenshot(rounded_cone, output_dir / "rounded_cone.png", resolution=64)
391
+
392
+ # Platonic Solids
393
+ print("Tetrahedron")
394
+ tetra = TetrahedronSDF(r=0.8)
395
+ create_screenshot(tetra, output_dir / "tetrahedron.png", resolution=64)
396
+
397
+ print("Octahedron")
398
+ octa = OctahedronSDF(r=0.8)
399
+ create_screenshot(octa, output_dir / "octahedron.png", resolution=64)
400
+
401
+ print("Dodecahedron")
402
+ dodeca = DodecahedronSDF(r=0.8)
403
+ create_screenshot(dodeca, output_dir / "dodecahedron.png", resolution=64)
404
+
405
+ print("Icosahedron")
406
+ icosa = IcosahedronSDF(r=0.8)
407
+ create_screenshot(icosa, output_dir / "icosahedron.png", resolution=64)
408
+
409
+ # ======== 2D Primitives ========
410
+ output_dir = OUTPUT_DIR / "primitives" / "2D"
411
+ output_dir.mkdir(parents=True, exist_ok=True)
412
+ unit_bounds = (0.0, 1.0, 0.0, 1.0)
413
+
414
+ print("\\n=== 2D Primitives ===")
415
+
416
+ # Circle
417
+ print("Circle")
418
+ circle = CircleSDF(center=[0.5, 0.5], radius=0.35)
419
+ create_2d_screenshot(
420
+ circle, output_dir / "circle.png", resolution=200, bounds=unit_bounds
421
+ )
422
+
423
+ # Rectangle
424
+ print("Rectangle")
425
+ rectangle = RectangleSDF(center=[0.5, 0.5], extents=[0.7, 0.55])
426
+ create_2d_screenshot(
427
+ rectangle, output_dir / "rectangle.png", resolution=200, bounds=unit_bounds
428
+ )
429
+
430
+ # Rounded Rectangle
431
+ print("Rounded Rectangle")
432
+ rounded_rect = RoundedRectangleSDF(
433
+ center=[0.5, 0.5], extents=[0.72, 0.56], radius=0.08
434
+ )
435
+ create_2d_screenshot(
436
+ rounded_rect,
437
+ output_dir / "rounded_rectangle.png",
438
+ resolution=200,
439
+ bounds=unit_bounds,
440
+ )
441
+
442
+ # Equilateral Triangle
443
+ print("Equilateral Triangle")
444
+ triangle = EquilateralTriangleSDF(size=0.32)
445
+ create_2d_screenshot(
446
+ triangle,
447
+ output_dir / "equilateral_triangle.png",
448
+ resolution=200,
449
+ bounds=unit_bounds,
450
+ query_offset=(0.5, 0.5),
451
+ )
452
+
453
+ # Hexagon
454
+ print("Hexagon")
455
+ hexagon = HexagonSDF(size=0.28)
456
+ create_2d_screenshot(
457
+ hexagon,
458
+ output_dir / "hexagon.png",
459
+ resolution=200,
460
+ bounds=unit_bounds,
461
+ query_offset=(0.5, 0.5),
462
+ )
463
+
464
+ # Polygon (custom shape)
465
+ print("Polygon (Pentagon)")
466
+ pentagon = PolygonSDF(
467
+ [
468
+ [0.5, 0.86], # Top
469
+ [0.82, 0.62], # Top right
470
+ [0.7, 0.24], # Bottom right
471
+ [0.3, 0.24], # Bottom left
472
+ [0.18, 0.62], # Top left
473
+ ]
474
+ )
475
+ create_2d_screenshot(
476
+ pentagon,
477
+ output_dir / "polygon_pentagon.png",
478
+ resolution=200,
479
+ bounds=unit_bounds,
480
+ )
481
+
482
+ # ======== Boolean Operations ========
483
+ output_dir = OUTPUT_DIR / "operations" / "boolean"
484
+ output_dir.mkdir(parents=True, exist_ok=True)
485
+
486
+ print("\n=== Boolean Operations ===")
487
+
488
+ # Union
489
+ print("Sphere + Box Union")
490
+ sphere = SphereSDF(center=[0.0, 0, 0], radius=0.5)
491
+ box = BoxSDF(center=[0.0, 0, 0], extents=[2.0, 0.3, 0.3])
492
+ union = sphere + box # UnionSDF is the + operator
493
+ create_screenshot(
494
+ union, output_dir / "union_sphere_box.png", resolution=64, title="Union"
495
+ )
496
+
497
+ # Difference
498
+ print("Sphere - Box Difference")
499
+ diff = DifferenceSDF(sphere, box)
500
+ create_screenshot(
501
+ diff,
502
+ output_dir / "difference_sphere_box.png",
503
+ resolution=64,
504
+ title="Difference",
505
+ )
506
+
507
+ # ======== Transformation Operations ========
508
+ output_dir = OUTPUT_DIR / "operations" / "transformations"
509
+ output_dir.mkdir(parents=True, exist_ok=True)
510
+
511
+ print("\n=== Transformation Operations ===")
512
+
513
+ # Twist
514
+ print("Twisted Torus")
515
+ base_torus = TorusSDF(
516
+ center=[0, 0, 0], axis=[0, 0, 1], major_radius=1.0, minor_radius=0.2
517
+ )
518
+ twist_params = np.pi / 2 # 90 degree twist
519
+ twisted_torus = TwistSDF(base_torus, k=twist_params)
520
+ create_screenshot(
521
+ twisted_torus, output_dir / "twist_torus.png", resolution=64, title="Twist"
522
+ )
523
+
524
+ # Dilate
525
+ print("Dilated Sphere")
526
+ base_sphere = SphereSDF(center=[0, 0, 0], radius=0.5)
527
+ dilated_sphere = DilateSDF(base_sphere, r=0.15)
528
+ create_screenshot(
529
+ dilated_sphere, output_dir / "dilate_sphere.png", resolution=64, title="Dilated"
530
+ )
531
+
532
+ # Shell
533
+ print("Sphere Shell")
534
+ shell_sphere = ShellSDF(base_sphere, thickness=0.1)
535
+ create_screenshot(
536
+ shell_sphere, output_dir / "shell_sphere.png", resolution=64, title="Shell"
537
+ )
538
+
539
+ # Repeat
540
+ print("Repeated Spheres (3x3x3)")
541
+ small_sphere = SphereSDF(center=[0, 0, 0], radius=0.2)
542
+ repeated_sphere = RepeatSDF(small_sphere, spacing=[0.6, 0.6, 0.6], count=3)
543
+ create_screenshot(
544
+ repeated_sphere,
545
+ output_dir / "repeat_sphere.png",
546
+ resolution=64,
547
+ title="Repeated (3x3x3)",
548
+ )
549
+
550
+ # Mirror
551
+ print("Mirrored Sphere")
552
+ off_center_sphere = SphereSDF(center=[0.5, 0, 0], radius=0.3)
553
+ mirrored_sphere = MirrorSDF(
554
+ off_center_sphere, plane_point=[0, 0, 0], plane_normal=[1, 0, 0]
555
+ )
556
+ create_screenshot(
557
+ mirrored_sphere,
558
+ output_dir / "mirror_sphere.png",
559
+ resolution=64,
560
+ title="Mirrored",
561
+ )
562
+
563
+ # Circular Array
564
+ print("Circular Array of Spheres")
565
+ arrayed_sphere = CircularArraySDF(off_center_sphere, count=6)
566
+ create_screenshot(
567
+ arrayed_sphere,
568
+ output_dir / "circular_array_sphere.png",
569
+ resolution=64,
570
+ title="Circular Array (6x)",
571
+ )
572
+
573
+ # Revolve (2D to 3D)
574
+ print("Revolved Circle")
575
+ circle_2d = CircleSDF(center=[0.8, 0], radius=0.15)
576
+ revolved_shape = RevolveSDF(
577
+ circle_2d, axis=torch.tensor([0, 0, 1], dtype=torch.float32)
578
+ )
579
+ create_screenshot(
580
+ revolved_shape,
581
+ output_dir / "revolve_circle.png",
582
+ resolution=64,
583
+ title="Revolved (Torus)",
584
+ )
585
+
586
+
587
+ # ==================== Main Script ====================
588
+
589
+
590
+ def main():
591
+ """Generate all SDF showcase images."""
592
+
593
+ print("=" * 60)
594
+ print("SDF Primitive and Operation Showcase Generator")
595
+ print("=" * 60)
596
+ print(f"\nOutput directory: {OUTPUT_DIR.absolute()}")
597
+
598
+ try:
599
+ create_sdf_primitives()
600
+
601
+ print("\n" + "=" * 60)
602
+ print("✓ Showcase generation complete!")
603
+ print("=" * 60)
604
+ print(f"\nGenerated screenshots saved to: {OUTPUT_DIR.absolute()}")
605
+ print("\nThese images can be used for documentation and README.")
606
+
607
+ except KeyboardInterrupt:
608
+ print("\n\nGeneration interrupted by user.")
609
+ except Exception as e:
610
+ print(f"\n\n✗ Error during generation: {e}")
611
+ import traceback
612
+
613
+ traceback.print_exc()
614
+ return 1
615
+
616
+ return 0
617
+
618
+
619
+ if __name__ == "__main__":
620
+ import sys
621
+
622
+ sys.exit(main())
@@ -0,0 +1,52 @@
1
+ import time
2
+ import trimesh
3
+ import torch
4
+ import pandas as pd
5
+ from DeepSDFStruct.SDF import SDFfromMesh
6
+
7
+
8
+ def run_benchmark():
9
+ mesh_configs = {
10
+ "Sphere (Low)": trimesh.creation.icosphere(subdivisions=2),
11
+ "Sphere (Med)": trimesh.creation.icosphere(subdivisions=4),
12
+ "Bunny (High)": trimesh.load("tests/data/stanford_bunny.stl"),
13
+ }
14
+
15
+ query_sizes = [10**4, 10**5, 10**6]
16
+ backends = ["igl", "trimesh"]
17
+ results = []
18
+
19
+ print(f"{'Mesh':<15} | {'Points':<10} | {'Backend':<10} | {'Time (s)':<10}")
20
+ print("-" * 55)
21
+
22
+ for name, mesh in mesh_configs.items():
23
+ for n_points in query_sizes:
24
+ queries = torch.randn((n_points, 3))
25
+
26
+ for backend in backends:
27
+ sdf_gen = SDFfromMesh(mesh, backend=backend, scale=True)
28
+
29
+ start_time = time.perf_counter()
30
+ _ = sdf_gen(queries)
31
+ end_time = time.perf_counter()
32
+
33
+ elapsed = end_time - start_time
34
+ results.append(
35
+ {
36
+ "Mesh": name,
37
+ "Faces": len(mesh.faces),
38
+ "Points": n_points,
39
+ "Backend": backend,
40
+ "Time": elapsed,
41
+ }
42
+ )
43
+ print(f"{name:<15} | {n_points:<10} | {backend:<10} | {elapsed:.4f}")
44
+
45
+ return pd.DataFrame(results)
46
+
47
+
48
+ df = run_benchmark()
49
+ summary = df.pivot_table(index=["Mesh", "Points"], columns="Backend", values="Time")
50
+ print("\nSpeedup (IGL vs Trimesh):")
51
+ summary["Speedup (x)"] = summary["trimesh"] / summary["igl"]
52
+ print(summary)
@@ -0,0 +1,33 @@
1
+ Mesh | Points | Backend | Time (s)
2
+ -------------------------------------------------------
3
+ Sphere (Low) | 10000 | igl | 0.0124
4
+ Sphere (Low) | 10000 | trimesh | 0.0155
5
+ Sphere (Low) | 100000 | igl | 0.0253
6
+ Sphere (Low) | 100000 | trimesh | 0.0732
7
+ Sphere (Low) | 1000000 | igl | 0.1554
8
+ Sphere (Low) | 1000000 | trimesh | 0.7333
9
+ Sphere (Med) | 10000 | igl | 0.0472
10
+ Sphere (Med) | 10000 | trimesh | 0.0399
11
+ Sphere (Med) | 100000 | igl | 0.0889
12
+ Sphere (Med) | 100000 | trimesh | 0.1181
13
+ Sphere (Med) | 1000000 | igl | 0.3624
14
+ Sphere (Med) | 1000000 | trimesh | 0.8615
15
+ Bunny (High) | 10000 | igl | 0.8877
16
+ Bunny (High) | 10000 | trimesh | 0.4823
17
+ Bunny (High) | 100000 | igl | 0.9768
18
+ Bunny (High) | 100000 | trimesh | 0.6239
19
+ Bunny (High) | 1000000 | igl | 1.6876
20
+ Bunny (High) | 1000000 | trimesh | 1.5953
21
+
22
+ Speedup (IGL vs Trimesh):
23
+ Backend igl trimesh Speedup (x)
24
+ Mesh Points
25
+ Bunny (High) 10000 0.887726 0.482313 0.543312
26
+ 100000 0.976751 0.623863 0.638712
27
+ 1000000 1.687581 1.595282 0.945307
28
+ Sphere (Low) 10000 0.012439 0.015542 1.249462
29
+ 100000 0.025271 0.073200 2.896629
30
+ 1000000 0.155364 0.733257 4.719598
31
+ Sphere (Med) 10000 0.047250 0.039910 0.844664
32
+ 100000 0.088914 0.118088 1.328118
33
+ 1000000 0.362449 0.861516 2.376932