Skip to content

Commit c5d47b2

Browse files
authored
feature: extract units from STEP file and scale the generated STL geometry to be in meters --> Merge pull request #27 from Antoniahuber/main
Merge pull request #27 from Antoniahuber/main
2 parents 7bda740 + fd454d0 commit c5d47b2

File tree

2 files changed

+74
-3
lines changed

2 files changed

+74
-3
lines changed

tests/test_006_geometry_utils.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class TestGeometryUtils:
2828
'003_Walls-Cavity-walls': '003_Walls-Cavity-walls_Copper--annealed-.stl',
2929
'004_Vacuum-Half_cell_sx': '004_Vacuum-Half_cell_sx_Vacuum.stl'}
3030

31+
UNITS = 0.001
32+
3133
def test_colors(self):
3234
colors = geometry.extract_colors_from_stp(self.STP_FILE)
3335
assert colors == self.COLORS
@@ -39,3 +41,8 @@ def test_materials(self):
3941
def test_solids(self):
4042
solids = geometry.extract_solids_from_stp(self.STP_FILE)
4143
assert solids == self.SOLIDS
44+
45+
def test_units(self):
46+
units = geometry.get_stp_unit_scale(self.STP_FILE)
47+
assert units == self.UNITS
48+

wakis/geometry.py

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ def extract_materials_from_stp(stp_file):
6969

7070
return stl_materials
7171

72-
def extract_solids_from_stp(stp_file):
72+
def extract_solids_from_stp(stp_file, path=''):
73+
if path and not path.endswith('/'):
74+
path += '/'
7375
solids, materials = extract_names_from_stp(stp_file)
7476
stl_solids = {}
7577
for i in range(len(list(solids.keys()))):
@@ -84,7 +86,7 @@ def extract_solids_from_stp(stp_file):
8486
solid_re = re.sub(r'[^a-zA-Z0-9_-]', '-', solid)
8587
mat_re = re.sub(r'[^a-zA-Z0-9_-]', '-', mat)
8688
name = f'{str(i).zfill(3)}_{solid_re}_{mat_re}'
87-
stl_solids[f'{str(i).zfill(3)}_{solid_re}'] = name + '.stl'
89+
stl_solids[f'{str(i).zfill(3)}_{solid_re}'] = path + name + '.stl'
8890

8991
return stl_solids
9092

@@ -142,6 +144,58 @@ def extract_names_from_stp(stp_file):
142144

143145
return solid_dict, material_dict
144146

147+
def get_stp_unit_scale(stp_file):
148+
"""
149+
Reads the unit definition from a STEP (.stp or .step) file and determines the
150+
scale factor required to convert the geometry to meters.
151+
152+
This function:
153+
- Opens and scans the header section of the STEP file.
154+
- Detects the SI base unit definition (e.g., millimeter, centimeter, meter).
155+
- Returns a scale factor to convert the geometry to meters.
156+
- Handles missing or unreadable unit information gracefully.
157+
158+
Args:
159+
stp_file (str): Path to the STEP (.stp or .step) file.
160+
161+
Returns:
162+
float: Scale factor to convert STEP geometry to meters.
163+
Defaults to 1.0 if no valid unit information is found.
164+
"""
165+
166+
unit_map = {
167+
".MILLI.": 1e-3,
168+
".CENTI.": 1e-2,
169+
".DECI.": 1e-1,
170+
".KILO.": 1e3,
171+
"$": 1.0, # '$' indicates no prefix, i.e. plain meters
172+
}
173+
174+
try:
175+
with open(stp_file, "r", encoding="utf-8", errors="ignore") as f:
176+
header = f.read(10000) # read only the beginning of the file
177+
178+
match = re.search(
179+
r"SI_UNIT\s*\(\s*(\.\w+\.)?\s*,\s*\.METRE\.\s*\)",
180+
header,
181+
re.IGNORECASE,
182+
)
183+
184+
if match:
185+
prefix = match.group(1).upper() if match.group(1) else "$"
186+
scale = unit_map.get(prefix, 1.0)
187+
print(f"Detected STEP unit: {prefix} → scale to meters: {scale}")
188+
return scale
189+
else:
190+
print("No unit found, files remain in original unit.")
191+
return 1.0
192+
193+
except Exception as exc:
194+
print(f"Error reading unit from STEP file: {exc}")
195+
print("Files remain in original unit.")
196+
197+
return 1.0
198+
145199
def generate_stl_solids_from_stp(stp_file, results_path=''):
146200
"""
147201
Extracts solid objects from a STEP (.stp) file and exports them as STL files.
@@ -150,10 +204,12 @@ def generate_stl_solids_from_stp(stp_file, results_path=''):
150204
- Imports the STEP file using `cadquery`.
151205
- Extracts solid names and their materials using `extract_names_from_stp()`.
152206
- Sanitizes solid and material names by replacing problematic characters.
153-
- Saves each solid as an STL file in the current directory.
207+
- Scales the solid to meter using `get_stp_unit_scale()`.
208+
- Saves each solid as an STL file in the current folder (default) or the given path.
154209
155210
Args:
156211
stp_file (str): Path to the STEP (.stp) file.
212+
results_path (str) (optional): default: '', path to save the STL (.stl) files
157213
158214
Raises:
159215
Exception: If `cadquery` is not installed, it prompts the user to install it.
@@ -176,6 +232,13 @@ def generate_stl_solids_from_stp(stp_file, results_path=''):
176232
''')
177233

178234
stp = cq.importers.importStep(stp_file)
235+
236+
scale_factor = get_stp_unit_scale(stp_file)
237+
if scale_factor != 1.0:
238+
print(f"Scaling geometry to meters (factor={scale_factor}).")
239+
scaled_solids = [solid.scale(scale_factor) for solid in stp.objects[0]]
240+
stp.objects = [scaled_solids]
241+
179242
solid_dict = extract_solids_from_stp(stp_file)
180243

181244
if not results_path.endswith('/'):
@@ -188,3 +251,4 @@ def generate_stl_solids_from_stp(stp_file, results_path=''):
188251
obj.exportStl(results_path+name)
189252

190253
return solid_dict
254+

0 commit comments

Comments
 (0)