diff --git a/designs/cabinet_bar_pull_handle/family.json b/designs/cabinet_bar_pull_handle/family.json new file mode 100644 index 0000000..86ffdcc --- /dev/null +++ b/designs/cabinet_bar_pull_handle/family.json @@ -0,0 +1,9 @@ +{ + "family": "cabinet_bar_pull_handle", + "standard": "Berenson SS bar-pull series (7068-7072) / Richelieu BP305", + "base_plane": "XY", + "description": "Cabinet/drawer bar pull as a simple assembly: a straight round bar and two standoff posts as three separate solids, each post coped to seat against the bar and tapped from the base for the mounting screw; bar ends optionally chamfered.", + "source": "Berenson 7068-7072 + Richelieu BP305 dimensioned drawings (C-C, overall length, bar/post diameter, projection, tap); bar/post/projection cross-checked constant across Amerock + Hickory", + "contributor": "BenchCAD-org", + "solids": 3 +} diff --git a/designs/cabinet_bar_pull_handle/part.py b/designs/cabinet_bar_pull_handle/part.py new file mode 100644 index 0000000..484e5be --- /dev/null +++ b/designs/cabinet_bar_pull_handle/part.py @@ -0,0 +1,52 @@ +"""cabinet_bar_pull_handle — the parametric part (simple assembly, solids=3). + +A cabinet/drawer bar pull as the three parts it is assembled from: a straight +round bar, and two cylindrical standoff posts that carry it off the cabinet +face, each post tapped from the base for the mounting machine screw. The post +tops are coped (saddle-cut by the bar cylinder) so each post mates flush against +the bar's underside with zero interpenetration — the members stay three separate +solids, as assembled. The bar overhangs each post; its ends are optionally +chamfered. The cabinet face is z=0; posts stand up +Z, the bar runs along X. + +Interface + examples: docs/DESIGN_SPEC.md +""" + +import cadquery as cq + + +def build(center_to_center, overhang, projection, bar_d, post_d, tap_d, tap_depth, chamfer_ends): + length = center_to_center + 2.0 * overhang # overall bar length + + # projection is the standoff to the bar's OUTER face, so the bar axis sits + # half a bar-diameter below it + z_bar = projection - bar_d / 2.0 + + # round bar across the posts (along X), its axis at z_bar — one solid + bar = cq.Workplane("YZ").circle(bar_d / 2.0).extrude(length).translate((-length / 2.0, 0.0, z_bar)) + if chamfer_ends: + bar = bar.faces(">X").chamfer(bar_d * 0.1) + bar = bar.faces(" list[str]: + """Engineering constraints (empty = valid). Each cites its rule.""" + bad = [] + + # finger clearance under the bar (projection is to the bar's outer face, so + # the clear gap below the bar is projection - bar_d) + if p["projection"] - p["bar_d"] < 8.0: + bad.append("projection - bar_d < 8: too little finger clearance under the bar") + + # the mounting tap must fit within the post (post rises to z = projection - bar_d/2) + if p["tap_depth"] > p["projection"] - p["bar_d"] / 2.0 - 2.0: + bad.append("tap_depth exceeds the post height (projection - bar_d/2 - 2): tap would break through") + + # the tap hole must leave a post wall + if p["tap_d"] > p["post_d"] - 2.5: + bad.append("tap_d > post_d-2.5: tap leaves <1.25 mm post wall") + + # posts must be clearly separated + if p["center_to_center"] < p["post_d"] + 8.0: + bad.append("center_to_center < post_d+8: posts too close / overlapping") + + # the bar must actually overhang the posts + if p["overhang"] < p["bar_d"]: + bad.append("overhang < bar_d: bar ends barely clear the posts") + + return bad