1111import numpy as np
1212import trimesh
1313from pydantic import BaseModel , Field
14- from tesseract_core .runtime import Array , Differentiable , Float32
14+ from tesseract_core .runtime import Array , Float32
1515
1616# Example spaceclaim .exe and script file Paths
1717# spaceclaim_exe = "F:\\Ansys installations\\ANSYS Inc\\v241\\scdm\\SpaceClaim.exe"
2525class InputSchema (BaseModel ):
2626 """Input schema for bar geometry design and SDF generation."""
2727
28- differentiable_parameters : Differentiable [
28+ differentiable_parameters : list [
2929 Array [
3030 (None ,),
3131 Float32 ,
3232 ]
3333 ] = Field (
3434 description = (
3535 "Angular positions around the unit circle for the bar geometry. "
36- "The shape is (num_bars, 2), where num_bars is the number of bars "
36+ "The shape is (num_bars+1 , 2), where num_bars is the number of bars "
3737 "and the second dimension has the start then end location of each bar."
38- "The final +1 entry represents the two z height coordinates for the cutting plane which combine with "
38+ "The first (+1) entry represents the two z height coordinates for the cutting plane which combine with "
3939 "a third fixed coordinate centered on the grid with z = grid_height / 2"
4040 )
4141 )
@@ -64,10 +64,10 @@ class InputSchema(BaseModel):
6464 "grid (pre z-plane cutting). The second is the beam thickness (mm)."
6565 )
6666 )
67-
68- static_parameters : list [int ] = Field (
69- description = ("List of integers used to construct the geometry. " )
70- )
67+ """
68+ static_parameters: list[list[ int] ] = Field(
69+ description=("List of integers used to construct the geometry.")
70+ )"""
7171
7272 string_parameters : list [str ] = Field (
7373 description = (
@@ -90,8 +90,8 @@ class TriangularMesh(BaseModel):
9090class OutputSchema (BaseModel ):
9191 """Output schema for generated geometry and SDF field."""
9292
93- mesh : TriangularMesh = Field (
94- description = "Triangular mesh representation of the geometry "
93+ meshes : list [ TriangularMesh ] = Field (
94+ description = "Triangular meshes representing the geometries "
9595 )
9696
9797
@@ -100,14 +100,14 @@ class OutputSchema(BaseModel):
100100#
101101
102102
103- def build_geometry (
103+ def build_geometries (
104104 differentiable_parameters : np .ndarray ,
105105 non_differentiable_parameters : np .ndarray ,
106106 string_parameters : list [str ],
107107) -> list [trimesh .Trimesh ]:
108108 """Build a Spaceclaim geometry from the parameters by modifying template .scscript.
109109
110- Return a TriangularMesh object.
110+ Return a trimesh object.
111111 """
112112 spaceclaim_exe = Path (string_parameters [0 ])
113113 spaceclaim_script = Path (string_parameters [1 ])
@@ -116,17 +116,20 @@ def build_geometry(
116116 # and instead use the unique run directory created everytime the
117117 # tesseract is run (so there is history).
118118 with TemporaryDirectory () as temp_dir :
119- prepped_script_path , output_file = _prep_scscript (
119+ prepped_script_path = _prep_scscript (
120120 temp_dir ,
121121 spaceclaim_script ,
122122 differentiable_parameters ,
123123 non_differentiable_parameters ,
124124 )
125125 run_spaceclaim (spaceclaim_exe , prepped_script_path )
126126
127- mesh = trimesh .load (output_file )
127+ meshes = []
128+ for output_stl in sorted (Path (temp_dir ).glob ("*.stl" )):
129+ mesh = trimesh .load (output_stl )
130+ meshes .append (mesh )
128131
129- return mesh
132+ return meshes
130133
131134
132135def _prep_scscript (
@@ -142,29 +145,50 @@ def _prep_scscript(
142145 # Define output file name and location
143146 # TODO: Same as before: can we output grid_fin.stl in the tesseract
144147 # unique run directory instead of temp dir to keep history?
145- output_file = os .path .join (temp_dir , "grid_fin.stl" )
148+ output_file = os .path .join (
149+ temp_dir , "grid_fin"
150+ ) # .stl ending is included in .scscript
146151 prepped_script_path = os .path .join (temp_dir , os .path .basename (spaceclaim_script ))
147152 shutil .copy (spaceclaim_script , prepped_script_path )
148153
149154 # Define dict used to input params to .scscript
155+ # Converts np.float32 to python floats so string substitution is clean
150156 keyvalues = {}
151157 keyvalues ["__output__" ] = output_file
152- keyvalues ["__params__.z2" ] = str (differentiable_parameters [0 ])
153- keyvalues ["__params__.z3" ] = str (differentiable_parameters [1 ])
158+ keyvalues ["__params__.zeds" ] = [
159+ [float (geom_params [0 ]), float (geom_params [1 ])]
160+ for geom_params in differentiable_parameters
161+ ]
154162 keyvalues ["__params__.height" ] = non_differentiable_parameters [0 ]
155163 keyvalues ["__params__.thickness" ] = non_differentiable_parameters [1 ]
156164
157- num_of_bars = (len (differentiable_parameters ) - 2 ) // 2
165+ num_of_batches = len (differentiable_parameters ) # number of geometries requested
166+ num_of_bars = (
167+ len (differentiable_parameters [0 ]) - 2
168+ ) // 2 # Use firt geometry in batch to test number of beams
158169
159170 assert num_of_bars == 8
160171
161- for i in range (num_of_bars ):
162- keyvalues [f"__params__.s{ i + 1 } " ] = str (differentiable_parameters [i * 2 + 2 ])
163- keyvalues [f"__params__.e{ i + 1 } " ] = str (differentiable_parameters [i * 2 + 3 ])
172+ batch_starts = []
173+ batch_ends = []
174+ for i in range (num_of_batches ):
175+ geom_starts = []
176+ geom_ends = []
177+ for j in range (num_of_bars ):
178+ geom_starts .append (float (differentiable_parameters [i ][j * 2 + 2 ]))
179+ geom_ends .append (float (differentiable_parameters [i ][j * 2 + 3 ]))
180+
181+ batch_starts .append (geom_starts )
182+ batch_ends .append (geom_ends )
183+
184+ keyvalues ["__params__.starts" ] = (
185+ batch_starts # convert to string to ease injection into file text
186+ )
187+ keyvalues ["__params__.ends" ] = batch_ends
164188
165189 _find_and_replace_keys_in_archive (prepped_script_path , keyvalues )
166190
167- return [ prepped_script_path , output_file ]
191+ return prepped_script_path
168192
169193
170194def _safereplace (filedata : str , key : str , value : str ) -> str :
@@ -249,17 +273,20 @@ def run_spaceclaim(spaceclaim_exe: Path, spaceclaim_script: Path) -> None:
249273def apply (inputs : InputSchema ) -> OutputSchema :
250274 """Create a Spaceclaim geometry based on input parameters.
251275
252- Returns TraingularMesh obj and exports a .stl .
276+ Returns TraingularMesh objects .
253277 """
254- mesh = build_geometry (
278+ trimeshes = build_geometries (
255279 differentiable_parameters = inputs .differentiable_parameters ,
256280 non_differentiable_parameters = inputs .non_differentiable_parameters ,
257281 string_parameters = inputs .string_parameters ,
258282 )
259283
260284 return OutputSchema (
261- mesh = TriangularMesh (
262- points = mesh .vertices .astype (np .float32 ),
263- faces = mesh .faces .astype (np .int32 ),
264- )
285+ meshes = [
286+ TriangularMesh (
287+ points = mesh .vertices .astype (np .float32 ),
288+ faces = mesh .faces .astype (np .int32 ),
289+ )
290+ for mesh in trimeshes
291+ ]
265292 )
0 commit comments