from __future__ import annotations import argparse import json from pathlib import Path import cadquery as cq from cadquery import exporters def build_fixture() -> cq.Workplane: base_width = 50 base_height = 80 base_thickness = 6 corner_radius = 4 hole_dia = 5.2 csk_dia = 10 csk_angle = 82 hole_inset_x = 10 hole_inset_y = 12 shaft_width = 14 shaft_depth = 12 shaft_length = 35 hook_inner_radius = 16 hook_outer_radius = hook_inner_radius + shaft_depth gusset_thickness = 6 bottom_gusset_length = 25 bottom_gusset_height = 18 top_gusset_length = 15 top_gusset_height = 12 base = ( cq.Workplane("XY") .box(base_width, base_height, base_thickness) .edges("|Z") .fillet(corner_radius) ) hx = base_width / 2 - hole_inset_x hy = base_height / 2 - hole_inset_y pts = [(hx, hy), (-hx, hy), (hx, -hy), (-hx, -hy)] base = ( base.faces(">Z") .workplane() .pushPoints(pts) .cskHole(diameter=hole_dia, cskDiameter=csk_dia, cskAngle=csk_angle) ) z_start = base_thickness / 2 z_arc_center = z_start + shaft_length y_inner_start = shaft_depth / 2 y_outer_start = -shaft_depth / 2 hook = ( cq.Workplane("YZ") .moveTo(y_outer_start, z_start) .lineTo(y_outer_start, z_arc_center) .threePointArc( (y_outer_start + hook_outer_radius, z_arc_center + hook_outer_radius), (y_outer_start + 2 * hook_outer_radius, z_arc_center), ) .threePointArc( (y_outer_start + 2 * hook_outer_radius - shaft_depth / 2, z_arc_center - shaft_depth / 2), (y_outer_start + 2 * hook_outer_radius - shaft_depth, z_arc_center), ) .threePointArc( (y_inner_start + hook_inner_radius, z_arc_center + hook_inner_radius), (y_inner_start, z_arc_center), ) .lineTo(y_inner_start, z_start) .close() .extrude(shaft_width / 2, both=True) ) bottom_gusset = ( cq.Workplane("YZ") .moveTo(y_outer_start, z_start) .lineTo(y_outer_start - bottom_gusset_length, z_start) .lineTo(y_outer_start, z_start + bottom_gusset_height) .close() .extrude(gusset_thickness / 2, both=True) ) top_gusset = ( cq.Workplane("YZ") .moveTo(y_inner_start, z_start) .lineTo(y_inner_start + top_gusset_length, z_start) .lineTo(y_inner_start, z_start + top_gusset_height) .close() .extrude(gusset_thickness / 2, both=True) ) return base.union(hook).union(bottom_gusset).union(top_gusset) def main() -> None: parser = argparse.ArgumentParser(description="Build the CadQuery heavy-duty hook sample.") parser.add_argument("--out-dir", required=True) args = parser.parse_args() out_dir = Path(args.out_dir) out_dir.mkdir(parents=True, exist_ok=True) stl_path = out_dir / "heavy_duty_hook.stl" fixture = build_fixture() exporters.export(fixture, str(stl_path)) bbox = fixture.val().BoundingBox() print( json.dumps( { "name": "Heavy_Duty_Hook", "stl_path": str(stl_path), "bounding_box": { "xlen": bbox.xlen, "ylen": bbox.ylen, "zlen": bbox.zlen, "xmin": bbox.xmin, "xmax": bbox.xmax, "ymin": bbox.ymin, "ymax": bbox.ymax, "zmin": bbox.zmin, "zmax": bbox.zmax, }, "cadquery_features": [ "filleted base plate", "four countersunk mounting holes", "sketched curved J hook profile", "bottom triangular gusset", "top triangular gusset", "single CadQuery unioned fixture", ], } ) ) if __name__ == "__main__": main()