| 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() |
|
|