Validate wall geometry.py
Jump to navigation
Jump to search
- !/usr/bin/env python3
import sys from pathlib import Path import yaml import FreeCAD
CAD_DIR = Path("cad_library")
def ft_to_in(ft):
return ft * 12.0
def nominal_to_actual_lumber(nominal):
table = {
"2x2": (1.5, 1.5),
"2x3": (1.5, 2.5),
"2x4": (1.5, 3.5),
"2x6": (1.5, 5.5),
"2x8": (1.5, 7.25),
"2x10": (1.5, 9.25),
"2x12": (1.5, 11.25),
}
return table[nominal]
def load_yaml(path):
with open(path, "r") as f:
return yaml.safe_load(f)
def count_objects(doc, prefix):
count = 0
for obj in doc.Objects:
if obj.Name.startswith(prefix):
count += 1
return count
def validate_instance(instance):
instance_id = instance["id"] params = instance["parameters"]
width_ft = params["nominal_width_ft"] height_ft = params["nominal_height_ft"] stud_nominal = params["stud_lumber_nominal"] osb_thickness = params["osb_thickness_in"]
width_in_expected = ft_to_in(width_ft) height_in_expected = ft_to_in(height_ft)
stud_thickness, stud_depth = nominal_to_actual_lumber(stud_nominal)
file_path = CAD_DIR / f"{instance_id}.FCStd"
if not file_path.exists():
return [f"{instance_id}: CAD file not found"]
doc = FreeCAD.openDocument(str(file_path))
errors = []
bbox = None
for obj in doc.Objects:
if hasattr(obj, "Shape"):
bb = obj.Shape.BoundBox
if bbox is None:
bbox = bb
else:
bbox.add(bb)
if bbox is None:
errors.append(f"{instance_id}: no geometry found")
return errors
width_actual = bbox.XLength height_actual = bbox.ZLength thickness_actual = bbox.YLength
if abs(width_actual - width_in_expected) > 0.25:
errors.append(
f"{instance_id}: width mismatch expected {width_in_expected} got {width_actual}"
)
if abs(height_actual - height_in_expected) > 0.25:
errors.append(
f"{instance_id}: height mismatch expected {height_in_expected} got {height_actual}"
)
expected_thickness = stud_depth + osb_thickness
if abs(thickness_actual - expected_thickness) > 0.25:
errors.append(
f"{instance_id}: thickness mismatch expected {expected_thickness} got {thickness_actual}"
)
stud_count = count_objects(doc, "stud_")
if stud_count < 2:
errors.append(f"{instance_id}: invalid stud count {stud_count}")
osb_count = count_objects(doc, "osb")
if osb_count != 1:
errors.append(f"{instance_id}: expected 1 OSB panel, found {osb_count}")
FreeCAD.closeDocument(doc.Name)
return errors
def main():
if len(sys.argv) != 2:
print("Usage: validate_wall_geometry.py instances.yaml")
sys.exit(1)
instances_doc = load_yaml(sys.argv[1])
instances = instances_doc["instances"]
all_errors = []
for inst in instances:
errs = validate_instance(inst)
all_errors.extend(errs)
if all_errors:
print("Geometry validation FAILED:")
for e in all_errors:
print(" ", e)
sys.exit(1)
print(f"Geometry validation passed for {len(instances)} modules")
if __name__ == "__main__":
main()