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,83 @@
1
+ {
2
+ "Description": "This experiment is intented for testing purposes only.",
3
+ "NetworkArch": "deep_sdf_decoder",
4
+ "TrainSplit": "splits/splines_v2_small.json",
5
+ "TestSplit": "splits/splines_v2_small.json",
6
+ "ReconstructionSplit": "",
7
+ "NetworkSpecs": {
8
+ "dims": [
9
+ 32,
10
+ 32,
11
+ 32,
12
+ 32,
13
+ 32,
14
+ 32
15
+ ],
16
+ "dropout": [
17
+ 0,
18
+ 1,
19
+ 2,
20
+ 3,
21
+ 4,
22
+ 5,
23
+ 6,
24
+ 7
25
+ ],
26
+ "dropout_prob": 0.2,
27
+ "norm_layers": [
28
+ 0,
29
+ 1,
30
+ 2,
31
+ 3,
32
+ 4,
33
+ 5,
34
+ 6,
35
+ 7
36
+ ],
37
+ "latent_in": [
38
+ 2
39
+ ],
40
+ "xyz_in_all": false,
41
+ "use_tanh": false,
42
+ "latent_dropout": false,
43
+ "weight_norm": true,
44
+ "geom_dimension": 3,
45
+ "homogen_predictor_dims": [
46
+ 64,
47
+ 64,
48
+ 64,
49
+ 64,
50
+ 9
51
+ ]
52
+ },
53
+ "CodeLength": 10,
54
+ "NumEpochs": 5,
55
+ "LogFrequency": 5,
56
+ "SnapshotFrequency": 100,
57
+ "AdditionalSnapshots": [
58
+ 1,
59
+ 5
60
+ ],
61
+ "LearningRateSchedule": [
62
+ {
63
+ "Type": "Step",
64
+ "Initial": 0.0005,
65
+ "Interval": 500,
66
+ "Factor": 0.5
67
+ },
68
+ {
69
+ "Type": "Step",
70
+ "Initial": 0.001,
71
+ "Interval": 500,
72
+ "Factor": 0.5
73
+ }
74
+ ],
75
+ "SamplesPerScene": 4,
76
+ "ScenesPerBatch": 2,
77
+ "DataLoaderThreads": 1,
78
+ "ClampingDistance": 0.1,
79
+ "CodeRegularization": true,
80
+ "AddHomogenization": true,
81
+ "CodeRegularizationLambda": 1e-4,
82
+ "CodeBound": 1.0
83
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "loss": 9775.975620117188,
3
+ "num_epochs": 5,
4
+ "timestamp": "2026-05-21 17:39:48",
5
+ "host_name": "neptune",
6
+ "device": "cpu",
7
+ "training_duration": "0:00:28",
8
+ "data_dir": "/storage/mkofler/dissertation/data/",
9
+ "version": "1.7.1.dev40+g989384bd4.d20260330"
10
+ }
DeepSDFStruct/utils.py ADDED
@@ -0,0 +1,96 @@
1
+ """
2
+ Utility Functions
3
+ =================
4
+
5
+ This module provides general utility functions used throughout DeepSDFStruct,
6
+ including logging configuration and color scheme definitions.
7
+
8
+ Functions
9
+ ---------
10
+ configure_logging
11
+ Set up logging for the DeepSDFStruct package with customizable
12
+ output format and destinations.
13
+
14
+ Constants
15
+ ---------
16
+ _TUWIEN_COLOR_SCHEME
17
+ TU Wien corporate color scheme for consistent visualization styling.
18
+ """
19
+
20
+ import logging
21
+ import DeepSDFStruct
22
+
23
+
24
+ def configure_logging(level=logging.INFO, logfile=None):
25
+ """Configure logging for the DeepSDFStruct package.
26
+
27
+ Sets up a logger with a standard format and optional file output.
28
+ This is called automatically when DeepSDFStruct is imported.
29
+
30
+ Parameters
31
+ ----------
32
+ level : int, default logging.INFO
33
+ Logging level (e.g., logging.DEBUG, logging.INFO, logging.WARNING).
34
+ logfile : str, optional
35
+ Path to log file. If provided, logs are written to both console
36
+ and file. If None, logs only to console.
37
+
38
+ Examples
39
+ --------
40
+ >>> from DeepSDFStruct.utils import configure_logging
41
+ >>> import logging
42
+ >>>
43
+ >>> # Set debug level and log to file
44
+ >>> configure_logging(level=logging.DEBUG, logfile='deepsdf.log')
45
+
46
+ Notes
47
+ -----
48
+ The log format is: "HH:MM:SS message"
49
+ All log messages are prefixed with a timestamp for easy debugging.
50
+ """
51
+ logger = logging.getLogger(DeepSDFStruct.__name__)
52
+ logger.setLevel(level)
53
+ formatter = logging.Formatter("%(asctime)s %(message)s", datefmt="%H:%M:%S")
54
+ if not logger.hasHandlers():
55
+ logger_handler = logging.StreamHandler()
56
+ logger_handler.setFormatter(formatter)
57
+ logger.addHandler(logger_handler)
58
+
59
+ if logfile is not None:
60
+ file_logger_handler = logging.FileHandler(logfile)
61
+ file_logger_handler.setFormatter(formatter)
62
+ logger.addHandler(file_logger_handler)
63
+
64
+
65
+ #: TU Wien corporate color scheme
66
+ #:
67
+ #: Dictionary mapping color names to RGB tuples (0-255 range).
68
+ #: Includes primary colors (blue, black, white) and secondary colors
69
+ #: (green, magenta, yellow, grey) with multiple shades of each.
70
+ #:
71
+ #: Useful for creating plots and visualizations with consistent branding.
72
+ _TUWIEN_COLOR_SCHEME = {
73
+ "blue": (0, 102, 153),
74
+ "black": (0, 0, 0),
75
+ "white": (255, 255, 255),
76
+ "blue_1": (84, 133, 171),
77
+ "blue_2": (114, 173, 213),
78
+ "blue_3": (166, 213, 236),
79
+ "blue_4": (223, 242, 253),
80
+ "grey": (100, 99, 99),
81
+ "grey_1": (157, 157, 156),
82
+ "grey_2": (208, 208, 208),
83
+ "grey_3": (237, 237, 237),
84
+ "green": (0, 126, 113),
85
+ "green_1": (106, 170, 165),
86
+ "green_2": (162, 198, 194),
87
+ "green_3": (233, 241, 240),
88
+ "magenta": (186, 70, 130),
89
+ "magenta_1": (205, 129, 168),
90
+ "magenta_2": (223, 175, 202),
91
+ "magenta_3": (245, 229, 239),
92
+ "yellow": (225, 137, 34),
93
+ "yellow_1": (238, 180, 115),
94
+ "yellow_2": (245, 208, 168),
95
+ "yellow_3": (153, 239, 225),
96
+ }
@@ -0,0 +1,211 @@
1
+ import torch
2
+ import streamlit as st
3
+ import os
4
+ import io
5
+ import meshio
6
+ import numpy as np
7
+ import matplotlib.pyplot as plt
8
+ from sklearn.manifold import TSNE
9
+ from sklearn.cluster import KMeans
10
+ from streamlit_stl import stl_from_text
11
+
12
+ import random
13
+
14
+
15
+ from DeepSDFStruct.pretrained_models import get_model, PRETRAINED_MODELS_DIR
16
+ from DeepSDFStruct.SDF import SDFfromDeepSDF
17
+ from DeepSDFStruct.mesh import create_3D_mesh
18
+
19
+ from mlflow import MlflowClient
20
+ from mlflow.entities import Experiment
21
+
22
+ client = MlflowClient(tracking_uri="sqlite:///mlruns.db")
23
+
24
+ all_exp = client.search_experiments()
25
+ experiments = {exp.name: exp for exp in all_exp}
26
+
27
+
28
+ def get_runs_from_experiment(experiment: Experiment):
29
+ runs = client.search_runs(experiment_ids=[experiment.experiment_id])
30
+ return runs
31
+
32
+
33
+ st.set_page_config(page_title="Cluster Shape Explorer", layout="wide")
34
+
35
+ # -------------------------------------------------------------------------
36
+ # Sidebar: Model Selector
37
+ # -------------------------------------------------------------------------
38
+
39
+ available_dirs = ["Pretrained Models"] + list(experiments.keys())
40
+
41
+ model_dir = st.sidebar.selectbox("Select experiment", available_dirs, 0)
42
+
43
+ available_models = {}
44
+
45
+ if model_dir == "Pretrained Models":
46
+ # Local folder models
47
+ for name in os.listdir(PRETRAINED_MODELS_DIR):
48
+ path = os.path.join(PRETRAINED_MODELS_DIR, name)
49
+ if os.path.isdir(path):
50
+ available_models[name] = path
51
+
52
+ else:
53
+ # MLflow experiment (runs)
54
+ ml_exp = experiments[model_dir]
55
+ runs = client.search_runs(experiment_ids=[ml_exp.experiment_id])
56
+
57
+ for run in runs:
58
+ run_name = run.data.tags.get("mlflow.runName", run.info.run_id)
59
+ available_models[run_name] = run.info.artifact_uri
60
+ model_name = st.sidebar.selectbox("Select a model", available_models)
61
+
62
+ local_path = available_models[model_name]
63
+ epoch_dir = os.path.join(local_path, "ModelParameters")
64
+
65
+ available_epochs = [
66
+ file[:-4]
67
+ for file in os.listdir(epoch_dir)
68
+ if file.endswith(".pth") or file.endswith(".pt")
69
+ ]
70
+
71
+ if "cluster_index" not in st.session_state.keys():
72
+ st.session_state.cluster_index = 0
73
+
74
+ epoch = st.sidebar.selectbox(
75
+ "Select epoch", available_epochs, len(available_epochs) - 1
76
+ )
77
+
78
+
79
+ # -------------------------------------------------------------------------
80
+ # Load model once
81
+ # -------------------------------------------------------------------------
82
+ @st.cache_resource
83
+ def load_model(model_name: str, epoch: str):
84
+ """
85
+ model_dir: 'deepsdf_experiments' or mlflow experiment name
86
+ model_name: run-name or folder-name
87
+ epoch: checkpoint epoch
88
+ """
89
+ model_path = available_models[model_name]
90
+ model = get_model(model_path, checkpoint=epoch)
91
+ sdf = SDFfromDeepSDF(model)
92
+ return model, sdf
93
+
94
+
95
+ model, sdf = load_model(model_name, epoch)
96
+ latent_vectors = model._trained_latent_vectors.detach().cpu().numpy()
97
+
98
+
99
+ # -------------------------------------------------------------------------
100
+ # Cluster computation (TSNE + KMeans)
101
+ # -------------------------------------------------------------------------
102
+ @st.cache_resource
103
+ def compute_clusters(latent_vectors, n_clusters=10, perplexity=3):
104
+ X_embedded = TSNE(
105
+ n_components=2, learning_rate="auto", init="random", perplexity=perplexity
106
+ ).fit_transform(latent_vectors)
107
+ kmeans = KMeans(n_clusters=n_clusters, random_state=0).fit(X_embedded)
108
+ return kmeans.labels_, X_embedded
109
+
110
+
111
+ n_clusters = st.sidebar.slider(
112
+ "Number of clusters", min_value=2, max_value=20, value=10
113
+ )
114
+ labels, X_embedded = compute_clusters(latent_vectors, n_clusters=n_clusters)
115
+
116
+ # -------------------------------------------------------------------------
117
+ # Sidebar: Cluster selector
118
+ # -------------------------------------------------------------------------
119
+
120
+ # -------------------------------------------------------------------------
121
+ # Navigation buttons (main window)
122
+ # -------------------------------------------------------------------------
123
+ col1, col2, col3 = st.columns([1, 2, 1])
124
+ with col1:
125
+ if st.button("Prev"):
126
+ st.session_state.cluster_index = max(0, st.session_state.cluster_index - 1)
127
+ with col3:
128
+ if st.button("Next"):
129
+ st.session_state.cluster_index = min(
130
+ n_clusters - 1, st.session_state.cluster_index + 1
131
+ )
132
+ with col2:
133
+ st.text(f"Cluster {st.session_state.cluster_index+1}/{n_clusters}")
134
+
135
+ indices_in_cluster = np.argwhere(labels == st.session_state.cluster_index).flatten()
136
+
137
+
138
+ # -------------------------------------------------------------------------
139
+ # STL generation function
140
+ # -------------------------------------------------------------------------
141
+ def generate_stl(lat_vec, color_rgb):
142
+ lat_vec_torch = torch.tensor(lat_vec, dtype=torch.float32, device=model.device)
143
+ sdf.set_latent_vec(lat_vec_torch)
144
+ with torch.inference_mode():
145
+ surf_mesh, _ = create_3D_mesh(
146
+ sdf, 100, differentiate=False, mesh_type="surface", device=model.device
147
+ )
148
+ surf_mesh_gus = surf_mesh.to_gus()
149
+ surf_mesh_gus.show_options["c"] = color_rgb # apply color
150
+ mesh = meshio.Mesh(
151
+ points=surf_mesh_gus.vertices, cells=[("triangle", surf_mesh_gus.faces)]
152
+ )
153
+ output = io.StringIO()
154
+ meshio.write(output, mesh, file_format="stl", binary=False)
155
+ return output
156
+
157
+
158
+ # -------------------------------------------------------------------------
159
+ # Display selected geometry
160
+ # -------------------------------------------------------------------------
161
+ def rgb_to_hex(rgb):
162
+ """Convert an RGB list [R,G,B] to hex string."""
163
+ return "#{:02x}{:02x}{:02x}".format(*rgb)
164
+
165
+
166
+ indices = [random.choice(list(indices_in_cluster)) for i in range(4)]
167
+ n_select = min(5, len(indices_in_cluster))
168
+
169
+ indices = random.sample(list(indices_in_cluster), n_select)
170
+ columns_row1 = st.columns(3)
171
+ columns_row2 = st.columns(3)
172
+
173
+ fig, ax = plt.subplots(figsize=(6, 6))
174
+ scatter = ax.scatter(
175
+ X_embedded[:, 0], X_embedded[:, 1], c=labels, cmap="tab10", alpha=0.6
176
+ )
177
+
178
+ # Map cluster id to color
179
+ cmap = plt.get_cmap("tab10")
180
+ cluster_color = [int(255 * c) for c in cmap(st.session_state.cluster_index)[:3]]
181
+
182
+ ax.scatter(
183
+ X_embedded[indices, 0],
184
+ X_embedded[indices, 1],
185
+ edgecolors="k",
186
+ facecolor=cmap(st.session_state.cluster_index),
187
+ s=120,
188
+ linewidths=2,
189
+ )
190
+ plt.colorbar(scatter, ax=ax, label="Cluster")
191
+ columns_row1[0].pyplot(fig)
192
+
193
+ for selected_idx, column in zip(indices, columns_row1[1:] + columns_row2):
194
+ lat_vec = latent_vectors[selected_idx]
195
+
196
+ # st.subheader(
197
+ # f"Latent vector index: {selected_idx} (Cluster {st.session_state.cluster_index})"
198
+ # )
199
+ stl_io = generate_stl(lat_vec, cluster_color)
200
+ with column:
201
+ stl_from_text(
202
+ text=stl_io.getvalue(),
203
+ color=rgb_to_hex(cluster_color),
204
+ material="material",
205
+ auto_rotate=True,
206
+ opacity=1,
207
+ shininess=10,
208
+ cam_v_angle=60,
209
+ cam_h_angle=-90,
210
+ height=500,
211
+ )
@@ -0,0 +1,8 @@
1
+ import subprocess
2
+ import sys
3
+ from pathlib import Path
4
+
5
+
6
+ def main():
7
+ script_path = Path(__file__).parent / "latent_viewer.py"
8
+ subprocess.run([sys.executable, "-m", "streamlit", "run", str(script_path)])